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]>
89 lines
2.6 KiB
C#
89 lines
2.6 KiB
C#
using System.Collections.Generic;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
|
|
namespace NightclubArcadia.Skills
|
|
{
|
|
/// <summary>
|
|
/// Skills tab content. Rebuilds the list from SkillDatabase.All +
|
|
/// PlayerSkills.GetRank on each show. No change event on PlayerSkills —
|
|
/// refresh on show is deliberate (menu is modal; ranks cannot change while open).
|
|
/// </summary>
|
|
public sealed class SkillsPanelView : MonoBehaviour
|
|
{
|
|
[SerializeField] RectTransform contentRoot;
|
|
[SerializeField] SkillRowView rowPrefab;
|
|
[SerializeField] TMP_Text axisHeaderPrefab;
|
|
[SerializeField] int displayMaxRank = 6;
|
|
|
|
readonly List<GameObject> spawned = new List<GameObject>();
|
|
|
|
/// <summary>
|
|
/// Wired from MenuTabBar TabEntry.onShown in the Inspector.
|
|
/// </summary>
|
|
public void Refresh()
|
|
{
|
|
Clear();
|
|
|
|
var runtime = SkillRuntime.Instance;
|
|
if (runtime == null || runtime.Database == null || runtime.Skills == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (contentRoot == null || rowPrefab == null)
|
|
{
|
|
Debug.LogWarning($"[{nameof(SkillsPanelView)}] Missing contentRoot or rowPrefab.", this);
|
|
return;
|
|
}
|
|
|
|
SkillAxis? currentAxis = null;
|
|
foreach (var def in runtime.Database.All)
|
|
{
|
|
if (def == null || string.IsNullOrEmpty(def.Id))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (currentAxis == null || currentAxis.Value != def.Axis)
|
|
{
|
|
currentAxis = def.Axis;
|
|
SpawnAxisHeader(def.Axis);
|
|
}
|
|
|
|
var rank = runtime.Skills.GetRank(def.Id);
|
|
var row = Instantiate(rowPrefab, contentRoot);
|
|
row.gameObject.SetActive(true);
|
|
row.Bind(def, rank, displayMaxRank);
|
|
spawned.Add(row.gameObject);
|
|
}
|
|
}
|
|
|
|
void SpawnAxisHeader(SkillAxis axis)
|
|
{
|
|
if (axisHeaderPrefab == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var header = Instantiate(axisHeaderPrefab, contentRoot);
|
|
header.gameObject.SetActive(true);
|
|
header.text = axis.ToString().ToUpperInvariant();
|
|
spawned.Add(header.gameObject);
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
for (var i = 0; i < spawned.Count; i++)
|
|
{
|
|
if (spawned[i] != null)
|
|
{
|
|
Destroy(spawned[i]);
|
|
}
|
|
}
|
|
|
|
spawned.Clear();
|
|
}
|
|
}
|
|
}
|