using StarterAssets;
using UnityEngine;
using Yarn.Unity;
namespace NightclubArcadia.Dialogue
{
///
/// Owns the hand-off between dialogue and the 3D environment: locks the
/// player's controls for as long as a conversation is running, and registers
/// the <> Yarn command that hands control back.
///
/// The lock is driven by the DialogueRunner's own lifecycle events rather
/// than by each interactable, so every entry point — auto-started nodes,
/// NPCStandIn, DialogueInteractable — gets the same behaviour without
/// having to remember to do it.
///
public class DialogueTransitionController : MonoBehaviour
{
[SerializeField] private DialogueRunner dialogueRunner;
[SerializeField] private GameObject dialogueUI;
[SerializeField] private GameObject playerArmature;
private ThirdPersonController playerController;
private StarterAssetsInputs playerInputs;
private Animator playerAnimator;
static readonly int AnimIDSpeed = Animator.StringToHash("Speed");
static readonly int AnimIDMotionSpeed = Animator.StringToHash("MotionSpeed");
void Awake()
{
if (playerArmature != null)
{
playerController = playerArmature.GetComponent();
playerInputs = playerArmature.GetComponent();
playerAnimator = playerArmature.GetComponent();
}
// Subscribed in Awake rather than Start: the runner auto-starts its
// first node from its own Start(), and script execution order
// between the two is not guaranteed.
dialogueRunner.onDialogueStart?.AddListener(OnDialogueStart);
dialogueRunner.onDialogueComplete?.AddListener(OnDialogueComplete);
}
void Start()
{
// Dialogue is in progress as soon as this controller is active
// (including auto-started nodes), so the player stays locked out
// until <>. The armature itself stays
// active/visible — only its controls are disabled.
SetPlayerControlEnabled(false);
dialogueRunner.AddCommandHandler("enter_environment", EnterEnvironment);
}
void OnDestroy()
{
dialogueRunner.onDialogueStart?.RemoveListener(OnDialogueStart);
dialogueRunner.onDialogueComplete?.RemoveListener(OnDialogueComplete);
}
void OnDialogueStart()
{
DialogueUIVisibility.Show(dialogueUI);
SetPlayerControlEnabled(false);
}
// Safety net: a node that ends without <> must still
// give the player their controls back.
void OnDialogueComplete() => EnterEnvironment();
void EnterEnvironment()
{
// Hide, don't deactivate: deactivating the Canvas breaks TMP's
// cached Canvas reference and makes the next conversation throw.
// See DialogueUIVisibility.
DialogueUIVisibility.Hide(dialogueUI);
SetPlayerControlEnabled(true);
}
void SetPlayerControlEnabled(bool value)
{
if (playerController == null)
{
return;
}
if (!value)
{
// Clear pending input and the locomotion blend before freezing
// the controller. Disabling it mid-stride otherwise leaves
// Speed at its last value, and the character keeps running on
// the spot for the whole conversation.
if (playerInputs != null)
{
playerInputs.MoveInput(Vector2.zero);
playerInputs.LookInput(Vector2.zero);
playerInputs.SprintInput(false);
playerInputs.JumpInput(false);
}
if (playerAnimator != null)
{
playerAnimator.SetFloat(AnimIDSpeed, 0f);
playerAnimator.SetFloat(AnimIDMotionSpeed, 0f);
}
}
playerController.enabled = value;
}
}
}