Runtime UI under Scripts/UI: a UIStateController owning menu/dialogue state, a PlayerControlLock so opening a panel suppresses world input, panel visibility helpers, and the menu input binding. Views: the dialogue stream presenter and its options presenter and message row, the portrait HUD, and the player menu with character, journal, and tab-bar views. Skills gains SkillsPanelView and SkillRowView, which is why the Skills asmdef now references UnityEngine.UI. Prefabs/UI holds the authored canvases (dialogue stream, HUD, player menu) and their rows. UILayerBootstrap remains a hand-added runtime fallback for scenes where those prefabs are not instantiated; it deliberately does not auto-spawn. Editor/UILayerSetup.cs is the one-shot menu that builds and wires those prefabs into a scene, with UILayerSetupAutoRun reminding after a script reload. Art/UI/Placeholder carries the placeholder portrait the HUD falls back to. Co-Authored-By: Claude Opus 5 <[email protected]>
83 lines
3.0 KiB
C#
83 lines
3.0 KiB
C#
using NightclubArcadia.Dialogue;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using Yarn.Unity;
|
|
|
|
namespace NightclubArcadia.UI.Menu
|
|
{
|
|
/// <summary>
|
|
/// Character tab stub: shows player name / alias from Yarn variable storage
|
|
/// and an empty inventory placeholder.
|
|
/// </summary>
|
|
public sealed class CharacterPanelView : MonoBehaviour
|
|
{
|
|
[SerializeField] TMP_Text nameLabel;
|
|
[SerializeField] TMP_Text aliasLabel;
|
|
[SerializeField] TMP_Text inventoryPlaceholder;
|
|
[SerializeField] DialogueRunner dialogueRunner;
|
|
|
|
// No OnEnable refresh: panels are toggled via CanvasGroup, so OnEnable
|
|
// fires exactly once during scene load — before the DialogueRunner has
|
|
// initialised its variables. MenuTabBar calls Refresh on tab show,
|
|
// which is both correctly timed and repeatable.
|
|
public void Refresh()
|
|
{
|
|
if (dialogueRunner == null)
|
|
{
|
|
dialogueRunner = FindFirstObjectByType<DialogueRunner>();
|
|
}
|
|
|
|
var storage = dialogueRunner != null ? dialogueRunner.VariableStorage : null;
|
|
|
|
if (nameLabel != null)
|
|
{
|
|
nameLabel.text = ReadString(storage, "$player_name_full", "Unknown");
|
|
}
|
|
|
|
if (aliasLabel != null)
|
|
{
|
|
// $player_alias is a *smart* variable
|
|
// <<declare $player_alias = alias_name($player_name) as string>>
|
|
// and Yarn's Unity runtime never assigns
|
|
// VariableStorageBehaviour.SmartVariableEvaluator, so reading it
|
|
// through TryGetValue always throws InvalidOperationException.
|
|
// Derive it from $player_name with the same generator the Yarn
|
|
// function wraps — deterministic, so the result is identical.
|
|
var realName = ReadString(storage, "$player_name", string.Empty);
|
|
aliasLabel.text = string.IsNullOrEmpty(realName)
|
|
? "—"
|
|
: AliasNameGenerator.AliasName(realName);
|
|
}
|
|
|
|
if (inventoryPlaceholder != null)
|
|
{
|
|
inventoryPlaceholder.text = "Inventory — empty";
|
|
}
|
|
}
|
|
|
|
static string ReadString(VariableStorageBehaviour storage, string key, string fallback)
|
|
{
|
|
if (storage == null)
|
|
{
|
|
return fallback;
|
|
}
|
|
|
|
try
|
|
{
|
|
if (storage.TryGetValue<string>(key, out var value) && !string.IsNullOrEmpty(value))
|
|
{
|
|
return value;
|
|
}
|
|
}
|
|
catch (System.InvalidOperationException)
|
|
{
|
|
// Smart variables, and variables read before the runner has a
|
|
// compiled Program, throw rather than returning false. A HUD
|
|
// panel showing a fallback string is the right failure mode.
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
}
|
|
}
|