114 lines
4.2 KiB
C#
114 lines
4.2 KiB
C#
using StarterAssets;
|
|
using UnityEngine;
|
|
using Yarn.Unity;
|
|
|
|
namespace NightclubArcadia.Dialogue
|
|
{
|
|
/// <summary>
|
|
/// 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 <<enter_environment>> 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.
|
|
/// </summary>
|
|
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<ThirdPersonController>();
|
|
playerInputs = playerArmature.GetComponent<StarterAssetsInputs>();
|
|
playerAnimator = playerArmature.GetComponent<Animator>();
|
|
}
|
|
|
|
// 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 <<enter_environment>>. 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 <<enter_environment>> 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;
|
|
}
|
|
}
|
|
}
|