fix(ui): #267 character panel reflects vitae/buffed skills and attributes
Retail CACQualities::EnchantAttribute (0x00594570), EnchantAttribute2nd (0x00594670, already ported for #6), and EnchantSkill (0x005947b0) are the three enchantment-composition functions the Character window's Attributes and Skills tabs depend on. Primary attributes never reference the vitae singleton in retail (only Attribute2nd/Skill do) — confirmed directly from the decompiled function bodies, not assumed. EnchantmentMath.GetMod gains requiredType/includeVitae parameters (default to the prior behavior) so a numeric StatMod key collision across domains (e.g. key=1 is both Strength and MaxHealth) can't leak a buff into the wrong computation. Spellbook.GetAttributeMod/GetSkillMod and LocalPlayerState.GetEffectiveAttribute/GetEffectiveSkill/ GetSkillVitaeModifier wire the retail chain through to the panel. CharacterSheetProvider now reports the effective value as the main number and CharacterSkill.CurrentLevel is no longer an alias of BaseLevel (this also activates the previously-dead SkillValueColor buffed/debuffed row coloring). CharacterStatController's footer-title parenthetical is cited from gmAttributeUI::DisplaySelectionFooter_Attribute (0x0049d280) and gmSkillUI::DisplaySelectionFooter_Trained (0x0049b860) + SkillInfoRegion::GetVitaeModifier (0x004f0fa0): skills show up to two segments (vitae's own contribution, then the buff-only residual), while vitae-immune attributes show at most one; no parenthetical when the delta is zero. The panel now refreshes on Spellbook.EnchantmentsChanged, not only raw property/attribute updates. Core goldens cover the user-reported 33% vitae example (303->203, "(-100)" exactly), buff+vitae composition, and the attribute vitae-immunity finding. Provider/controller tests cover the full row-click -> footer-title path and live refresh. Full solution suite passes with zero failures. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f6275f4501
commit
cf2605fa4a
11 changed files with 1089 additions and 53 deletions
|
|
@ -89,6 +89,10 @@ public sealed class CharacterSheet
|
|||
// ── Innate attributes (UpdateInnateAttributeInfo 0x004b87e0) ────────────
|
||||
// InqAttribute order: 1,2,4,3,5,6 = Strength, Endurance, Quickness, Coordination, Focus, Self.
|
||||
|
||||
// Issue #267: these are the EFFECTIVE (post-buff) values — retail
|
||||
// CACQualities::EnchantAttribute (0x00594570). Primary attributes are
|
||||
// vitae-immune in retail; only active buffs move this number away from
|
||||
// AttributeBaseValues below.
|
||||
public int Strength { get; init; }
|
||||
public int Endurance { get; init; }
|
||||
public int Quickness { get; init; }
|
||||
|
|
@ -96,6 +100,17 @@ public sealed class CharacterSheet
|
|||
public int Focus { get; init; }
|
||||
public int Self { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Unenchanted base value for each of the 6 primary attributes, in
|
||||
/// POSITIONAL order matching <c>CharacterStatController.AttrRows</c>
|
||||
/// (Strength, Endurance, Coordination, Quickness, Focus, Self — note
|
||||
/// this differs from the individual-property declaration order above).
|
||||
/// Issue #267: paired with the effective values above to compute the
|
||||
/// retail footer-title delta parenthetical
|
||||
/// (<c>gmAttributeUI::DisplaySelectionFooter_Attribute</c> 0x0049d280).
|
||||
/// </summary>
|
||||
public int[] AttributeBaseValues { get; init; } = Array.Empty<int>();
|
||||
|
||||
// ── Skills (UpdateFakeSkills 0x004b8930) ────────────────────────────────
|
||||
// Character Information uses 0xB5/0xC0 for Chess/Fishing; skill credits use 0x18.
|
||||
// InqInt(0x18) = available skill credits — footer 0x10000245 in the Attributes tab.
|
||||
|
|
@ -187,9 +202,18 @@ public sealed record CharacterSkill(
|
|||
uint IconDid,
|
||||
CharacterSkillAdvancementClass AdvancementClass,
|
||||
int BaseLevel,
|
||||
// Issue #267: CurrentLevel is now the EFFECTIVE (vitae + buff) level —
|
||||
// retail CACQualities::EnchantSkill (0x005947b0). Previously an alias of
|
||||
// BaseLevel; this activates the existing CharacterStatController.
|
||||
// SkillValueColor buffed/debuffed row coloring.
|
||||
int CurrentLevel,
|
||||
bool UsableUntrained,
|
||||
int TrainedCost,
|
||||
int SpecializedCost,
|
||||
long RaiseCost,
|
||||
long Raise10Cost = 0L);
|
||||
long Raise10Cost = 0L,
|
||||
// Issue #267: vitae's isolated contribution to CurrentLevel (always ≤ 0),
|
||||
// retail SkillInfoRegion::GetVitaeModifier (0x004f0fa0). Used for the
|
||||
// footer-title vitae-specific parenthetical, separate from the buff delta
|
||||
// (CurrentLevel − VitaeModifier − BaseLevel).
|
||||
int VitaeModifier = 0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue