fix(ui): Campaign AS AS2 fix round — review findings F13-F18

Opus's dual-lens review of the AS2 header-identity-block commit
(f8a22589) approved the retail port as exact and ordered six
mechanical fixes:

- F13: correct the plan doc's "static tinted preview" claim about
  retail's examine-window paperdoll — the decomp shows retail's clone
  IS animated (BasicCreatureExamineUI::Init @0x004AB9C0 ->
  CPhysicsObj::makeObject -> MorphToExistingObject +
  play_script_internal, CreatureMode::Render driving update_position
  every frame), just decoupled from the live target. AD-114 already
  records the correct deviation; only the plan doc's rationale text
  was wrong.
- F14: add a regression test pinning the monster path (character:
  false, success:false, no String 5 / Int 0x105 marker) so element
  0x1000053A stays cleared — the exact case that used to emit the
  invented "Assessment incomplete" literal AS2 deleted. This is the
  test coverage the AS2 commit message over-claimed already existed.
- F15: dedup the PK/PKLite PWD bit constants that were copy-pasted
  across three files into named PublicWeenieFlags.PlayerKiller
  (0x20) / PlayerKillerLite (0x02000000) values, routing all three
  consumers (AppraisalUiController, CharacterSheetProvider,
  LocalPlayerTeleportController) through them. Pure mechanical
  substitution; the write side (PlayerKillerStatusBitfield.Apply) is
  untouched.
- F16: file register row AD-115 for a real, previously-unrecorded
  deviation the AS2 commit introduced: BuildCharacterTitleDisplay
  clears element 0x10000151 when neither source resolves, where
  retail (CharExamineUI::Show, BasicCreatureExamineUI::Init) never
  clears it and would show the previous target's stale title
  instead. Marked intentional — ours is deliberately better.
- F17: narrow BuildPlayerKillerDisplay's doc comment — retail's
  `cur_weenobj != 0` guard @0x004b482f skips only the PK line, not
  the whole response; the whole-response drop on a missing local
  object is separate, pre-existing, and out of scope here.
- F18: ledger row for AS2 now reads "review fix round" with the land
  commit and a note that the fix SHA is recorded at review-close.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-25 08:59:29 +02:00
parent f8a2258979
commit cc5290af7d
7 changed files with 85 additions and 30 deletions

View file

@ -759,6 +759,49 @@ public sealed class AppraisalUiControllerTests
Assert.Equal(string.Empty, HeaderText(layout, 0x1000053Au));
}
[Fact]
public void FailedMonsterAssess_DoesNotEmitInventedAssessmentIncompleteLiteral()
{
// Regression pin (review F14): pre-AS2, a FAILED monster assess
// (character:false — no String 5 Template, no Int 0x105 marker) was
// the exact case that set 0x1000053A to the invented, zero-retail-
// provenance "Assessment incomplete" literal. The monster branch of
// ApplyCreature never composes 0x1000053A at all (see
// CreatureResponse_HeaderIdentityElementsUnaffectedByPlayerFix
// above), so the element must stay cleared/empty here too, success
// or not.
ImportedLayout layout = FixtureLoader.LoadExamination();
var objects = new ClientObjectTable();
objects.AddOrUpdate(new ClientObject
{
ObjectId = ObjectId,
Name = "Drudge",
Type = ItemType.Creature,
});
using var interaction = NewInteraction(objects, []);
using AppraisalUiController controller = Bind(
layout,
objects,
interaction,
new CombatState(),
[],
[],
() => { },
() => { },
creatureNames: new CreatureDisplayNameResolver(
new Dictionary<uint, string> { [11u] = "Drudge" }))!;
interaction.ExamineSelectedOrEnterMode(ObjectId);
var properties = new PropertyBundle();
properties.Ints[2u] = 11; // CreatureType — no String 5 / Int 261 marker present.
Assert.True(controller.Apply(
Parsed(properties, MinimalCreatureProfile(), success: false)));
Assert.Equal(AppraisalView.Creature, controller.ActiveView);
Assert.Equal(string.Empty, HeaderText(layout, 0x1000053Au));
}
[Fact]
public void ResponseForNeitherPendingNorCurrent_IsIgnored()
{