add UI layer: dialogue stream, HUD, player menu, and skills panel

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]>
This commit is contained in:
2026-08-25 19:03:42 +02:00
co-authored by Claude Opus 5
parent 932cd0582f
commit cba87dcb31
57 changed files with 7707 additions and 1 deletions
@@ -0,0 +1,65 @@
using NightclubArcadia.UI;
using UnityEngine;
namespace NightclubArcadia.UI.HUD
{
/// <summary>
/// Bottom-right player portrait. Visible only when dialogue is inactive
/// and the player menu is closed.
/// </summary>
public sealed class PortraitHudView : MonoBehaviour
{
[SerializeField] GameObject panelRoot;
void OnEnable()
{
if (UIStateController.Instance != null)
{
UIStateController.Instance.StateChanged += OnStateChanged;
}
}
void Start()
{
// Re-subscribe in case Instance was not ready during OnEnable,
// and apply the initial visibility.
if (UIStateController.Instance != null)
{
UIStateController.Instance.StateChanged -= OnStateChanged;
UIStateController.Instance.StateChanged += OnStateChanged;
}
ApplyVisibility();
}
void OnDisable()
{
if (UIStateController.Instance != null)
{
UIStateController.Instance.StateChanged -= OnStateChanged;
}
}
void OnStateChanged() => ApplyVisibility();
void ApplyVisibility()
{
if (panelRoot == null)
{
return;
}
var state = UIStateController.Instance;
var visible = state == null || (!state.IsDialogueActive && !state.IsMenuOpen);
if (visible)
{
PanelVisibility.Show(panelRoot);
}
else
{
PanelVisibility.Hide(panelRoot);
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fd1354ade14b4b1d80da9acac4aa9e12
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: