feat(ui): Campaign AS AS2 — examination header identity block (G1/G2/G3/G5/G10)
Retail's CharExamineUI::SetAppraiseInfo (@0x004B45F0, player-target
examine subview) binds four fixed header lines that acdream was feeding
from the wrong appraisal properties:
- 0x10000150 (Heritage) got raw string prop 4 verbatim. Retail composes
"<Gender> <Heritage>" via InqGenderHeritageDisplay (@0x005B5AE0) from
Int 113 Gender + Int 188 HeritageGroup, falling back to the assessed
object's creature-type name only when HeritageGroup == 0. Reuses the
Campaign CT gender/heritage tables in CharacterIdentityText (new
GenderHeritageDisplay helper) and the controller's existing
CreatureDisplayNameResolver for the creature-type fallback arm.
- 0x10000151 (Profession/title) got AllegianceName (string 47) — the
title line was never shown at all. Now resolves the CURRENT display
title from Int 261 CharacterTitleId through CharacterTitleResolver
(CharacterTitleTable::GetCharacterTitleFromID @0x005C6ED0), falling
back to String 5 Template verbatim when the id is absent or
unresolvable. RetailUiRuntime.MountAppraisal now wires the SAME
CharacterTitleResolver instance the D.2b Character panel already
owns (_bindings.Character.TitleResolver), resolved per call under
DatLock — never captured once at mount time, per the secure-trade
deferred-Func lesson.
- 0x10000152 (PlayerKiller) got MonarchsName (string 11) — never shown.
Ruling R7: retail reads the LOCAL weenie's PWD bits
(ACCWeenieObject::IsPK/IsPKLite @0x0058C8B0/@0x0058C8A0), never the
appraisal payload. Now reads the assessed ClientObject's
PublicWeenieBitfield directly (bit 0x20 -> "Player Killer", bit
0x02000000 -> "Player Killer Lite", else "Non-Player Killer").
Apply()'s existing bail-out when the object has left the local table
already matches retail's "weenie is gone, leave the line cleared".
- 0x1000053A (AllegianceName) invented a literal "Assessment
incomplete" on failed assess — zero retail provenance, deleted
outright. Retail clears the element first (ClearCreatureText already
does this every ApplyCreature call) then sets String 47
AllegianceName only inside the Int 30 AllegianceRank >= 1 gate.
Scoped to the character branch since retail's CreatureExamineUI
(monsters) never binds this element at all.
None of the four lines are success-gated — ACE sends the int/string
tables even on a failed assess, matching retail's own composition.
First-ever AppraisalView.Character controller test coverage (gap G10):
header composition, title/allegiance fallback and gating, all three PK
variants plus the missing-object clear case, failed-assess rendering
with a repo grep confirming the invented literal is gone, and a
regression pin proving the monster (character: false) path is
untouched.
Register: files AD-114 for the examination preview's animated clone
(mirrors the assessed target's live current pose via
CreatureAppraisalFramePresenter) versus retail's independently
animated private CreatureMode clone (BasicCreatureExamineUI::Init
@0x004AB9C0) — owner-ruled intentional deviation, 2026-08-25
("we animate it, and I like it").
Full hermetic suite green: 15,469 tests passed, 0 failed, 0 skipped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0332466fdc
commit
f8a2258979
5 changed files with 533 additions and 15 deletions
|
|
@ -116,6 +116,32 @@ internal static class CharacterIdentityText
|
|||
_ => null,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Campaign AS slice AS2 (2026-08-25): examination window element
|
||||
/// <c>0x10000150</c> (Heritage), <c>CharExamineUI::SetAppraiseInfo
|
||||
/// @0x004B45F0</c> via <c>AppraisalSystem::InqGenderHeritageDisplay
|
||||
/// @0x005B5AE0</c>. Unlike <see cref="StatHeaderLine"/> (the
|
||||
/// stat-management panel's header, which never appends a title and has
|
||||
/// no creature-type fallback), this composes ONLY
|
||||
/// "<Gender> <Heritage>" — falling back to
|
||||
/// <paramref name="creatureTypeFallback"/> (the assessed object's
|
||||
/// creature-type display name, Int 2, resolved through the SAME
|
||||
/// EnumMapper family <see cref="CreatureDisplayNameResolver"/> already
|
||||
/// serves the monster view's own <c>0x1000014E</c> line) ONLY when
|
||||
/// <paramref name="heritageGroup"/> is exactly 0, matching retail's own
|
||||
/// integer comparison rather than "the lookup failed".
|
||||
/// </summary>
|
||||
public static string GenderHeritageDisplay(
|
||||
int gender,
|
||||
int heritageGroup,
|
||||
string? creatureTypeFallback)
|
||||
{
|
||||
string? heritage = heritageGroup == 0
|
||||
? creatureTypeFallback
|
||||
: HeritageGroupDisplayName(heritageGroup);
|
||||
return Join(GenderDisplayName(gender), heritage);
|
||||
}
|
||||
|
||||
private static string Join(params string?[] parts)
|
||||
{
|
||||
return string.Join(" ", parts
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue