fix(saga): restore cast relationship labels

- Give each Cast entry enough space to render muted relationship subtext
- Preserve fixed Saga geometry with the slightly taller Cast rows
- Add populated-Cast regression coverage for visible relationship labels
This commit is contained in:
DazedAnon 2026-07-22 19:57:12 -05:00
parent 0c60f1a2ba
commit a0d1b9a688
4 changed files with 31 additions and 9 deletions

View file

@ -9590,6 +9590,14 @@ public static class AgentHarness
$"panel={panel} ownsAvatar={ownsAvatar} avatarVisible={avatarVisible} live={DossierAvatar.ShowingLiveAvatar} dbg='{dbg}' pass={pass}";
break;
}
case "saga_panel_cast_relations":
{
int want = ParseInt(cmd.value, 1);
int have = LifeSagaPanel.LastVisibleRelationCount;
pass = LifeSagaPanel.Visible && have >= want;
detail = $"relationLabels={have} wantMin={want} visible={LifeSagaPanel.Visible}";
break;
}
case "caption_hover_keeps_beat":
{
bool hover = LifeSagaViewController.IsHoverPreview;

View file

@ -3628,8 +3628,12 @@ internal static class HarnessScenarios
Step("sad6", "spectator", value: "on"),
Step("sad7", "interest_saga_clear"),
Step("sad8", "pick_unit", asset: "human"),
Step("sad8a", "happiness_remember_partner"),
Step("sad8b", "pick_unit", asset: "human", value: "other"),
Step("sad9", "focus", asset: "human"),
Step("sad10", "saga_force_admit_focus"),
Step("sad10a", "saga_memory_record_focus", asset: "War", value: "opposing commander",
expect: "harness_cast_relation"),
Step("sad11", "assert", expect: "saga_tab_selected", value: "dossier"),
// saga_select_tab saga → hover preview of watched MC (compat).
Step("sad11b", "caption_capture_beat"),
@ -3639,6 +3643,7 @@ internal static class HarnessScenarios
Step("sad14b", "assert", expect: "saga_panel_fixed_size"),
Step("sad14c", "assert", expect: "caption_hover_keeps_beat"),
Step("sad14d", "assert", expect: "saga_panel_cast_first"),
Step("sad14e", "assert", expect: "saga_panel_cast_relations", value: "1"),
Step("sad15", "screenshot", value: "saga-hover-depth"),
Step("sad15b", "saga_stamp_chapter_focus", asset: "Love", value: "Bound with a lasting partner"),
Step("sad15c", "saga_memory_record_focus", asset: "Founding", value: "kingdom"),

View file

@ -20,7 +20,7 @@ public static class LifeSagaPanel
/// <summary>Full inner hover width; Cast starts beside the portrait, Legacy spans below it.</summary>
public const float BodyWidthFixed = 232f;
/// <summary>Fixed hover depth; never derived from Cast or Legacy content.</summary>
public const float BodyHeightFixed = 74f;
public const float BodyHeightFixed = 82f;
public const float BodyHeightMax = BodyHeightFixed;
public const float BodyWidthMin = BodyWidthFixed;
@ -37,12 +37,12 @@ public static class LifeSagaPanel
private const float Pad = 3f;
private const float CastFace = 14f;
private const float CastSlotW = 59f;
private const float CastSlotH = 15f;
private const float CastSlotH = 18f;
private const float SecLabelH = 9f;
private const float LineH = 9f;
private const float PortraitReserveW = 44f;
private const int BodyFont = 7;
private const int BuildVersion = 14;
private const int BuildVersion = 15;
private static GameObject _root;
private static RectTransform _rootRt;
@ -74,6 +74,8 @@ public static class LifeSagaPanel
/// <summary>Saga hover reuses the dossier avatar for the hovered character.</summary>
public static bool OwnsAvatar => true;
public static string LastLayoutDebug { get; private set; } = "";
/// <summary>Harness/UI diagnostic: rendered Cast relationship sublabels.</summary>
public static int LastVisibleRelationCount { get; private set; }
public static void EnsureBuilt(Transform parent)
{
@ -128,6 +130,7 @@ public static class LifeSagaPanel
_speciesId = "";
_fingerprint = "";
LegacyParts.Clear();
LastVisibleRelationCount = 0;
_laidOutHeight = BodyHeightFixed;
if (_root != null)
{
@ -275,11 +278,17 @@ public static class LifeSagaPanel
}
int castN = 0;
LastVisibleRelationCount = 0;
for (int i = 0; i < CastSlots.Length; i++)
{
if (CastSlots[i].Root.activeSelf)
{
castN++;
if (CastSlots[i].Relation != null
&& !string.IsNullOrEmpty(CastSlots[i].Relation.text))
{
LastVisibleRelationCount++;
}
}
}
@ -313,7 +322,7 @@ public static class LifeSagaPanel
}
// Reserve both Cast rows even when sparse so Legacy never moves between characters.
y = 44f;
y = 52f;
float legacyBudget = Mathf.Max(LineH, yMax - y - SecLabelH);
int omittedLegacy = 0;
string fittedLegacy = FitLegacyToHeight(fullW, legacyBudget, out omittedLegacy);
@ -406,10 +415,10 @@ public static class LifeSagaPanel
Text relation = MakeLabel(root.transform, "Rel", BodyFont, HudTheme.Muted, FontStyle.Normal);
RectTransform nameRt = name.rectTransform;
nameRt.anchoredPosition = new Vector2(CastFace + 2f, 0f);
nameRt.sizeDelta = new Vector2(CastSlotW - CastFace - 2f, 8f);
nameRt.sizeDelta = new Vector2(CastSlotW - CastFace - 2f, 9f);
RectTransform relRt = relation.rectTransform;
relRt.anchoredPosition = new Vector2(CastFace + 2f, -8f);
relRt.sizeDelta = new Vector2(CastSlotW - CastFace - 2f, 7f);
relRt.anchoredPosition = new Vector2(CastFace + 2f, -9f);
relRt.sizeDelta = new Vector2(CastSlotW - CastFace - 2f, 9f);
root.SetActive(false);
return new CastSlot

View file

@ -39,12 +39,12 @@ Use one fixed logical envelope for every Saga state:
| Region | Size | Contract |
|---|---:|---|
| Whole surface | `240 × 117` | Never content-sized |
| Whole surface | `240 × 125` | Never content-sized |
| Glyph rail | `232 × 18` | Fixed position and hit targets |
| Gap | `232 × 2` | Reserved |
| Nametag | `232 × 15` | One line; fixed controls at right |
| Gap | `232 × 1` | Reserved |
| Body | `232 × 74` | Portrait, Cast, and Legacy stay inside |
| Body | `232 × 82` | Portrait, Cast, and Legacy stay inside |
| Outer padding | `4` horizontal, `3` vertical | Constant |
The body must never report a content-derived preferred height. Empty and full characters use