using DG.Tweening; using System; using TMPro; using UnityEngine; using UnityEngine.XR.Interaction.Toolkit; public class CafeWaiterNPC : NPCController { public FMODWhisperBridge fmodWhisperBridge; public TextMeshProUGUI notepadText; public GameObject notepad; public Transform backRoom; public Transform plate; public float backRoomMovingTime = 5f; public float spawnedFoodDiameter = 0.5f; public int ignorePlayerCollisionLayer = 2; private Vector3 notepadOriginalRotation; private Vector3 notepadFlippedRotation; private Vector3 startingPosition; // states: // 0 - idle // 1 - waiting for player order // 2 - waiting for player to answer "is this correct?" // 3 - bringing food // 4 - waiting for player to go away private int state; private float lastPlayerVoiceUpdateTime; private void Awake() { state = 0; lastPlayerVoiceUpdateTime = Time.time; startingPosition = transform.position; } private void Start() { notepadOriginalRotation = notepad.transform.localRotation.eulerAngles; notepadFlippedRotation = notepadOriginalRotation + new Vector3(0, 180, 0); notepad.SetActive(false); } protected override void OnPlayerApproach() { if (state == 0) { SpeakVoiceLine(0); fmodWhisperBridge.ActivateRecording(); fmodWhisperBridge.OnWhisperSegmentUpdated += OnPlayerSpeechUpdated; fmodWhisperBridge.OnWhisperSegmentFinished += OnPlayerSpeechFinished; notepad.SetActive(true); state = 1; } } protected override void OnPlayerLeave() { fmodWhisperBridge.OnWhisperSegmentUpdated -= OnPlayerSpeechUpdated; fmodWhisperBridge.OnWhisperSegmentFinished -= OnPlayerSpeechFinished; fmodWhisperBridge.DeactivateRecording(); // If currently not bringing food and the player leaves, then go back to idle state if (state != 3) { notepad.SetActive(false); notepad.transform.localRotation = Quaternion.Euler(notepadOriginalRotation); state = 0; } } private void OnPlayerSpeechFinished(string playerText) { if (Time.time < lastPlayerVoiceUpdateTime + 1.0f) { return; } lastPlayerVoiceUpdateTime = Time.time; if (state == 1) { // Show transcription and ask whether it is correct fmodWhisperBridge.DeactivateRecording(); notepadText.text = playerText; notepad.transform.DOLocalRotate(notepadFlippedRotation, 0.5f).OnComplete(() => { fmodWhisperBridge.ActivateRecording(); fmodWhisperBridge.OnWhisperSegmentUpdated += OnPlayerSpeechUpdated; fmodWhisperBridge.OnWhisperSegmentFinished += OnPlayerSpeechFinished; }); SpeakVoiceLine(1); state = 2; } else if (state == 2) { fmodWhisperBridge.DeactivateRecording(); // Flip notepad back notepad.transform.DOLocalRotate(notepadOriginalRotation, 0.5f); // if player answered positively, bring food, otherwise ask again if (playerText.ToLower().Contains("ye")) { SpeakVoiceLine(2); Invoke("BringFood", 1f); state = 3; } else { SpeakVoiceLine(3); fmodWhisperBridge.ActivateRecording(); fmodWhisperBridge.OnWhisperSegmentUpdated += OnPlayerSpeechUpdated; fmodWhisperBridge.OnWhisperSegmentFinished += OnPlayerSpeechFinished; state = 1; } } } private void OnPlayerSpeechUpdated(string playerText) { // Only update notepad text when currently listening to player order if (state == 1) { notepadText.text = playerText; // For now, when something is transcribed, treat it as player finished speaking OnPlayerSpeechFinished(playerText); } // faster reaction to player answering yes/no else if (state == 2) { OnPlayerSpeechFinished(playerText); } } private async void BringFood() { notepad.SetActive(false); transform.DOMove(backRoom.position, backRoomMovingTime); byte[] imageBytes = await InvokeAiClient.Instance.GenerateImage(notepadText.text); Texture2D GeneratedTexture = ModelGenerationUtils.CreateTexture(imageBytes); Sprite sprite = ModelGenerationUtils.CreateSprite(GeneratedTexture); string encodedTexture = Convert.ToBase64String(GeneratedTexture.EncodeToJPG()); byte[] encodedModel = await TrellisClient.Instance.GenerateModel(encodedTexture); GameObject spawnedObject = await ModelGenerationUtils.Instance.SpawnModel(encodedModel, spawnedFoodDiameter); // Come back transform.DOMove(startingPosition, backRoomMovingTime).OnComplete(() => { spawnedObject.transform.position = plate.position + new Vector3(0, 1f, 0); InitializeSpawnedObject(spawnedObject); SpeakVoiceLine(4); state = 4; }); } private void InitializeSpawnedObject(GameObject spawnedObject) { Rigidbody rigidbody = spawnedObject.AddComponent(); //spawnedObject.AddComponent(); //spawnedObject.AddComponent(); MeshCollider spawnedObjectCollider = spawnedObject.GetComponent(); spawnedObjectCollider.convex = true; spawnedObject.layer = ignorePlayerCollisionLayer; XRGrabInteractable grabComponent = spawnedObject.AddComponent(); grabComponent.useDynamicAttach = true; } }