Files
nightclub-arcadia/NightclubArcadia/Assets/Scripts/Dialogue/DialogueTransitionController.cs
T
lennartandClaude Opus 5 5b0827072b suppress world interaction while a menu panel is open
DialogueInteractable, DialogueTransitionController, and DialogueUIVisibility are
reworked against the new UI layer: visibility is driven by UIStateController
rather than each component toggling canvases itself, which removes the
duplicated show/hide logic the two had accumulated.

NPCStandIn now ignores the interact button while UIStateController reports a
menu open, so pressing interact through an open panel no longer starts a
conversation behind it.

The yarnproject change is graph-editor node positions only.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 19:04:16 +02:00

86 lines
3.4 KiB
C#

using NightclubArcadia.UI;
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 &lt;&lt;enter_environment&gt;&gt; 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. Control locking is delegated to
/// <see cref="PlayerControlLock"/> so the player menu can hold a second
/// independent lock without re-enabling movement mid-conversation.
/// </summary>
public class DialogueTransitionController : MonoBehaviour
{
[SerializeField] private DialogueRunner dialogueRunner;
[SerializeField] private GameObject dialogueUI;
[SerializeField] private GameObject playerArmature;
[SerializeField] private PlayerControlLock controlLock;
void Awake()
{
if (controlLock == null && playerArmature != null)
{
controlLock = playerArmature.GetComponent<PlayerControlLock>();
}
// 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()
{
if (controlLock == null && playerArmature != null)
{
controlLock = playerArmature.GetComponent<PlayerControlLock>();
}
// 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.
controlLock?.Acquire(this);
dialogueRunner.AddCommandHandler("enter_environment", EnterEnvironment);
}
void OnDestroy()
{
dialogueRunner.onDialogueStart?.RemoveListener(OnDialogueStart);
dialogueRunner.onDialogueComplete?.RemoveListener(OnDialogueComplete);
}
void OnDialogueStart()
{
DialogueUIVisibility.Show(dialogueUI);
UIStateController.Instance?.SetDialogueActive(true);
controlLock?.Acquire(this);
}
// 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 / PanelVisibility.
DialogueUIVisibility.Hide(dialogueUI);
UIStateController.Instance?.SetDialogueActive(false);
// Release is idempotent on a HashSet — safe when both
// <<enter_environment>> and onDialogueComplete fire.
controlLock?.Release(this);
}
}
}