feat(ui): spellcasting cast-button + character-panel attribute/skill tooltips — gmSpellcastingUI::UpdateCastButtonTooltip @0x004C6A30, AttributeInfoRegion/Attribute2ndInfoRegion/SkillInfoRegion @0x004F1530/0x004F1680/0x004F2140
TS-85 remainder batch (hover/UI overnight round, batch B). Audit found
three of the four listed spellcasting SetTooltip sites (endowment icon,
favorite, submenu) were already correct via UiCatalogSlot's pre-existing
Label-driven GetTooltipText; only the cast button (UiButton, no tooltip
wiring at all) was a real gap. Ports the verified literal states
("Select a spell to cast" / "You have no spells ready to cast" / the
full endowment-item USE-the-%s branch) plus a documented, narrower
fallback (spell name only) for the one sub-branch whose exact wording
sits behind a genuine gmNoticeHandler vtable-slot collision in the
pseudo-C dump rather than the unlabeled-string-pool class the rest of
this batch recovered.
Character panel: new UiClickablePanel.TooltipText seam (same pattern as
UiButton.TooltipText) carries the six hardcoded attribute descriptions
and three pair-shared vitals descriptions (byte-decoded from the retail
string pool) plus skill tooltips composed from the already-DAT-parsed
SkillBase.Description/.Formula — no hand-transcription needed for the
~30+ skill strings. The formula-to-text algorithm itself
(SkillSystem::InqSkillFormula) was recovered by byte-decoding six short
fragments Binary Ninja left completely unlabeled between two
gmSpellcastingUI vtable declarations.
Live-verified against the local ACE server: 34 real skills' composed
tooltips and both reachable cast-button states captured via a temporary
probe (stripped before this commit). Full solution suite green
(14,647 tests, 0 failures) both before and after the probe strip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
c623b57ad3
commit
39c49e140e
8 changed files with 269 additions and 9 deletions
|
|
@ -192,6 +192,43 @@ public static class CharacterStatController
|
|||
("Mana", 0x06004C3Du, 5u), // max enum 5; current enum 6
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// TS-85 (character-panel tooltips): retail <c>SkillSystem::InqAttributeDescription
|
||||
/// @ 0x005c8e30</c> — six hardcoded strings, byte-decoded from the retail binary's
|
||||
/// string pool (the pseudo-C dump truncates them with "…"). Ported from
|
||||
/// <c>AttributeInfoRegion::AttributeInfoRegion @ 0x004f1530</c>'s
|
||||
/// <c>UIElement::SetTooltip</c> call at 0x004f1617, keyed by retail attribute id
|
||||
/// (matches <see cref="AttrRows"/>' statId column, NOT the array index — the
|
||||
/// authored row order swaps Coordination/Quickness relative to the id numbering).
|
||||
/// </summary>
|
||||
private static readonly IReadOnlyDictionary<uint, string> AttributeDescriptions =
|
||||
new Dictionary<uint, string>
|
||||
{
|
||||
[1u] = "Measures your character's muscular power.", // Strength
|
||||
[2u] = "Measures how healthy your character is.", // Endurance
|
||||
[3u] = "Measures how fast your character is.", // Quickness
|
||||
[4u] = "Measures your character's reflexes", // Coordination (no trailing period — verified byte-exact)
|
||||
[5u] = "Measures your character's mind and senses.", // Focus
|
||||
[6u] = "Measures your character's willpower.", // Self
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// TS-85 (character-panel tooltips): retail <c>SkillSystem::InqAttribute2ndDescription
|
||||
/// @ 0x005c8f70</c> — three hardcoded strings shared by each Max/Current pair (1&2,
|
||||
/// 3&4, 5&6), byte-decoded from the retail string pool. Ported from
|
||||
/// <c>Attribute2ndInfoRegion::Attribute2ndInfoRegion @ 0x004f1680</c>'s
|
||||
/// <c>UIElement::SetTooltip</c> call at 0x004f1777, keyed by <see cref="VitalRows"/>'
|
||||
/// maxStatId column (1/3/5 — either member of the pair resolves the same text in
|
||||
/// retail).
|
||||
/// </summary>
|
||||
private static readonly IReadOnlyDictionary<uint, string> Attribute2ndDescriptions =
|
||||
new Dictionary<uint, string>
|
||||
{
|
||||
[1u] = "(Endurance/2)\nIf you run out of health, you will die!", // Health
|
||||
[3u] = "(Endurance)\nAffects your actions and movement.", // Stamina
|
||||
[5u] = "(Self)\nAffects how much magic you can cast.", // Mana
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Bind the Attributes-tab header + 9-row list + footer elements, tab button states,
|
||||
/// and raise buttons in <paramref name="layout"/> to <paramref name="data"/>.
|
||||
|
|
@ -671,7 +708,7 @@ public static class CharacterStatController
|
|||
|
||||
for (int i = 0; i < AttrRows.Length; i++)
|
||||
{
|
||||
var (rowName, iconDid, _) = AttrRows[i];
|
||||
var (rowName, iconDid, statId) = AttrRows[i];
|
||||
int rowIndex = i;
|
||||
|
||||
var row = AddRow(list, datFont, spriteResolve,
|
||||
|
|
@ -694,6 +731,7 @@ public static class CharacterStatController
|
|||
return v.ToString();
|
||||
},
|
||||
valueColorProvider: () => AttributeValueColor(data(), rowIndex));
|
||||
row.TooltipText = AttributeDescriptions.GetValueOrDefault(statId);
|
||||
|
||||
row.OnClick = () =>
|
||||
{
|
||||
|
|
@ -706,7 +744,7 @@ public static class CharacterStatController
|
|||
|
||||
for (int i = 0; i < VitalRows.Length; i++)
|
||||
{
|
||||
var (rowName, iconDid, _) = VitalRows[i];
|
||||
var (rowName, iconDid, maxStatId) = VitalRows[i];
|
||||
int rowIndex = i;
|
||||
int absIndex = AttrRows.Length + i;
|
||||
|
||||
|
|
@ -726,6 +764,7 @@ public static class CharacterStatController
|
|||
};
|
||||
},
|
||||
valueColorProvider: () => VitalValueColor(data(), rowIndex));
|
||||
row.TooltipText = Attribute2ndDescriptions.GetValueOrDefault(maxStatId);
|
||||
|
||||
row.OnClick = () =>
|
||||
{
|
||||
|
|
@ -785,6 +824,9 @@ public static class CharacterStatController
|
|||
valueProvider: () => LiveSkill().CurrentLevel.ToString(),
|
||||
valueColorProvider: () => SkillValueColor(LiveSkill()),
|
||||
nameColor: Vector4.One);
|
||||
// TS-85: SkillInfoRegion::GetTooltip (0x004f1fe0), stamped once at
|
||||
// row construction — matches retail (never recomputed per frame).
|
||||
row.TooltipText = skill.TooltipText;
|
||||
row.OnClick = () =>
|
||||
{
|
||||
HandleSkillRowClick(rowIndex, sel, bindings, spriteResolve, data, allRaise1, allRaise10);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue