fix: drop the spell body from passive talent tooltips (#538)
- A passive talent's tooltip is the talent builder's: name, rank, requirements, description and the next rank, with no weapon-class "Requires" row or other spell line (#476) - Talent tooltip tests pin the talent builder's gold spacer and every line of an exceptional talent's spell body - SetTalent's doc states why an exceptional talent's next-rank pass never shows: no 1.12 exceptional talent has a second rank Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-Authored-By: Sam <samuv.eth@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
Sam
parent
459e0e90c1
commit
c574e20031
@@ -8,11 +8,14 @@
|
||||
//! `TALENT_BRANCH_ARRAY[tier][column]`. The respec pair answers a class trainer's
|
||||
//! `CONFIRM_TALENT_WIPE` and reads no snapshot.
|
||||
//!
|
||||
//! `GameTooltip:SetTalent` is the spell builder (`0x52e610`) with talent lines: the rank line
|
||||
//! (`TOOLTIP_TALENT_RANK`, `0x854a2c`), the red requirement lines after it, and "Click to learn"
|
||||
//! (`TOOLTIP_TALENT_LEARN`, `0x8549f8`) in green on a learnable rank. The "Next rank:" block,
|
||||
//! `TOOLTIP_TALENT_NEXT_RANK` in white and the next rank's description in gold, is untraced in
|
||||
//! the reference.
|
||||
//! `GameTooltip:SetTalent` (`0x535170`) builds an exceptional talent with the spell builder
|
||||
//! (`0x52e610`) and any other with the talent builder (`0x52b0a0`), which keeps the spell's name
|
||||
//! and description and drops the rest of its body. The talent lines are the talent builder's: the
|
||||
//! rank line (`TOOLTIP_TALENT_RANK`, `0x854a2c`), the red requirement lines after it, the "Next
|
||||
//! rank:" block (`TOOLTIP_TALENT_NEXT_RANK`, `0x854a10`, white, over the next rank's gold
|
||||
//! description) and "Click to learn" (`TOOLTIP_TALENT_LEARN`, `0x8549f8`) in green on a learnable
|
||||
//! rank. The spell builder writes the same lines around the full body (`0x52f735`); its next-rank
|
||||
//! pass, a second full body, never runs, as no 1.12 exceptional talent has a second rank.
|
||||
|
||||
use mlua::{Lua, MultiValue, Table, Value};
|
||||
|
||||
@@ -272,6 +275,7 @@ pub(super) fn install_tooltip_method(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
(
|
||||
t.display_spell,
|
||||
TalentLines {
|
||||
exceptional: t.exceptional,
|
||||
rank_line,
|
||||
reqs: t.req_lines.clone(),
|
||||
next_spell: t.next_spell,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! The talent bindings over a pushed snapshot, the learn queue, and `SetTalent`: the spell tooltip
|
||||
//! with the talent lines interleaved.
|
||||
//! The talent bindings over a pushed snapshot, the learn queue, and `SetTalent`: the talent builder
|
||||
//! for a passive talent, the spell tooltip with the talent lines interleaved for an exceptional one.
|
||||
|
||||
use super::common::script;
|
||||
use crate::script::*;
|
||||
@@ -115,29 +115,40 @@ fn seed_talent_strings(s: &mut UiScript) {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// `SetTalent`'s lines: name, `TOOLTIP_TALENT_RANK` (white), cost, gold description,
|
||||
/// `TOOLTIP_TALENT_NEXT_RANK` and the next rank's gold description, green `TOOLTIP_TALENT_LEARN`.
|
||||
/// A passive talent's full spell view, every body line filled, so a test sees which ones the talent
|
||||
/// builder drops.
|
||||
fn passive_body(description: &str) -> SpellTooltipView {
|
||||
SpellTooltipView {
|
||||
name: "Improved Fireball".into(),
|
||||
cost: Some("35 Mana".into()),
|
||||
range: Some("30 yd range".into()),
|
||||
cast_time: Some("Instant".into()),
|
||||
cooldown: Some("6 sec cooldown".into()),
|
||||
requires_item: Some("Requires One-Handed Axes".into()),
|
||||
requires_form: Some("Requires Battle Stance".into()),
|
||||
reagents: Some("Reagents: Light Feather".into()),
|
||||
chance: Some("2.62% chance to dodge".into()),
|
||||
description: description.into(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// `SetTalent` on a passive talent is the talent builder `0x52b0a0`: name, `TOOLTIP_TALENT_RANK`
|
||||
/// (white), the gold description, a `" "` spacer, `TOOLTIP_TALENT_NEXT_RANK` and the next rank's
|
||||
/// gold description, green `TOOLTIP_TALENT_LEARN`; no cost, range, cast, requirement, reagent or
|
||||
/// chance line.
|
||||
#[test]
|
||||
fn set_talent_renders_the_interleaved_tooltip() {
|
||||
fn set_talent_on_a_passive_talent_is_the_talent_builder() {
|
||||
let mut s = script();
|
||||
seed_talent_strings(&mut s);
|
||||
s.set_talents(one_tab_state());
|
||||
s.set_spell_tooltip(
|
||||
11070,
|
||||
SpellTooltipView {
|
||||
name: "Improved Fireball".into(),
|
||||
cast_time: Some("Instant".into()),
|
||||
description: "Reduces the casting time of your Fireball spell by 0.3 sec.".into(),
|
||||
..Default::default()
|
||||
},
|
||||
passive_body("Reduces the casting time of your Fireball spell by 0.3 sec."),
|
||||
);
|
||||
s.set_spell_tooltip(
|
||||
11071,
|
||||
SpellTooltipView {
|
||||
name: "Improved Fireball".into(),
|
||||
description: "Reduces the casting time of your Fireball spell by 0.4 sec.".into(),
|
||||
..Default::default()
|
||||
},
|
||||
passive_body("Reduces the casting time of your Fireball spell by 0.4 sec."),
|
||||
);
|
||||
s.run(
|
||||
r#"
|
||||
@@ -146,13 +157,18 @@ fn set_talent_renders_the_interleaved_tooltip() {
|
||||
tt:SetOwner(a, "ANCHOR_RIGHT")
|
||||
tt:SetTalent(1, 1)
|
||||
assert(tt:IsShown(), "SetTalent shows")
|
||||
-- name, rank, Instant, desc, next-rank header, next desc, learn hint = 7 lines.
|
||||
-- name, rank, desc, spacer, next-rank header, next desc, learn hint = 7 lines.
|
||||
assert(tt:NumLines() == 7, "got " .. tt:NumLines())
|
||||
assert(TTTextLeft1:GetText() == "Improved Fireball")
|
||||
assert(TTTextLeft2:GetText() == "[RANK 3/5]", "got " .. TTTextLeft2:GetText())
|
||||
assert(TTTextLeft3:GetText() == "Instant")
|
||||
assert(TTTextLeft3:GetText() == "Reduces the casting time of your Fireball spell by 0.3 sec.")
|
||||
assert(TTTextLeft4:GetText() == " ", "the spacer, got " .. tostring(TTTextLeft4:GetText()))
|
||||
assert(TTTextLeft5:GetText() == "[NEXT_RANK]")
|
||||
assert(TTTextLeft6:GetText() == "Reduces the casting time of your Fireball spell by 0.4 sec.")
|
||||
assert(TTTextLeft7:GetText() == "[LEARN]")
|
||||
for i = 1, 7 do
|
||||
assert(getglobal("TTTextRight" .. i):GetText() == nil, "no right column on line " .. i)
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
@@ -163,6 +179,56 @@ fn set_talent_renders_the_interleaved_tooltip() {
|
||||
if t == "[LEARN]" && c[0] < 1e-6 && (c[1] - 1.0).abs() < 1e-6)
|
||||
});
|
||||
assert!(green, "the learn hint is green");
|
||||
// The spacer is `0x530380`'s gold, not `0x5303b0`'s caller colour.
|
||||
let gold_spacer = quads.iter().any(|q| {
|
||||
matches!(&q.content, QuadContent::Text { text: Some(t), color: Some(c), .. }
|
||||
if t == " " && (c[0] - 1.0).abs() < 1e-6 && (c[1] - 210.0 / 255.0).abs() < 1e-3 && c[2] < 1e-6)
|
||||
});
|
||||
assert!(gold_spacer, "the spacer is gold");
|
||||
assert!(s.take_errors().is_empty());
|
||||
}
|
||||
|
||||
/// An exceptional talent (`Talent.dbc` Flags bit 0) is the spell builder `0x52e610` instead, whose
|
||||
/// cost, cast and required-item lines stay: Holy Shield's "Requires Shields".
|
||||
#[test]
|
||||
fn set_talent_on_an_exceptional_talent_keeps_the_spell_body() {
|
||||
let mut s = script();
|
||||
seed_talent_strings(&mut s);
|
||||
let mut state = one_tab_state();
|
||||
let holy_shield = &mut state.talents[0][1];
|
||||
holy_shield.name = "Holy Shield".into();
|
||||
holy_shield.exceptional = true;
|
||||
holy_shield.req_lines = vec!["Requires 30 points in Protection Talents".into()];
|
||||
s.set_talents(state);
|
||||
s.set_spell_tooltip(
|
||||
11119,
|
||||
SpellTooltipView {
|
||||
name: "Holy Shield".into(),
|
||||
cost: Some("150 Mana".into()),
|
||||
cast_time: Some("Instant cast".into()),
|
||||
requires_item: Some("Requires Shields".into()),
|
||||
description: "Increases chance to block by 30% for 10 sec.".into(),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
s.run(
|
||||
r#"
|
||||
local a = CreateFrame("Button", "TB3"); a:SetPoint("CENTER", 0, 0); a:SetWidth(10); a:SetHeight(10)
|
||||
local tt = CreateFrame("GameTooltip", "TT3")
|
||||
tt:SetOwner(a, "ANCHOR_RIGHT")
|
||||
tt:SetTalent(1, 2)
|
||||
local want = {
|
||||
"Holy Shield", "[RANK 0/5]", "Requires 30 points in Protection Talents", "150 Mana",
|
||||
"Instant cast", "Requires Shields", "Increases chance to block by 30% for 10 sec.",
|
||||
}
|
||||
assert(tt:NumLines() == table.getn(want), "got " .. tt:NumLines())
|
||||
for i, text in ipairs(want) do
|
||||
local got = getglobal("TT3TextLeft" .. i):GetText()
|
||||
assert(got == text, "line " .. i .. ": " .. tostring(got))
|
||||
end
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert!(s.take_errors().is_empty());
|
||||
}
|
||||
|
||||
|
||||
@@ -227,6 +227,10 @@ pub(super) fn spell_view_of(lua: &Lua, spell_id: u32) -> Option<SpellTooltipView
|
||||
/// while locked, the next-rank block and the green learn hint.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(super) struct TalentLines {
|
||||
/// `Talent.dbc` Flags bit 0: `SetTalent` hands an exceptional talent to the spell builder
|
||||
/// `0x52e610` (`0x5352ab`) and any other to the talent builder `0x52b0a0`, which writes no
|
||||
/// cost, range, cast, required-item, form, reagent or chance line.
|
||||
pub exceptional: bool,
|
||||
/// `TOOLTIP_TALENT_RANK` ("Rank %d/%d") filled from the player's strings; `None` when they
|
||||
/// lack the key, and no rank row shows.
|
||||
pub rank_line: Option<String>,
|
||||
@@ -275,6 +279,7 @@ fn render_spell(
|
||||
v.rank.clone().filter(|_| show_rank).map(|t| (t, GRAY))
|
||||
};
|
||||
append_line(lua, this, (v.name.clone(), name_color), right, false)?;
|
||||
let talent_builder = talent.is_some_and(|t| !t.exceptional);
|
||||
// The talent head: the white rank line, then the red requirements while locked.
|
||||
if let Some(t) = talent {
|
||||
if let Some(rank) = &t.rank_line {
|
||||
@@ -284,7 +289,7 @@ fn render_spell(
|
||||
append_line(lua, this, (req.clone(), RED), None, true)?;
|
||||
}
|
||||
}
|
||||
if !aura {
|
||||
if !aura && !talent_builder {
|
||||
match (&v.cost, &v.range) {
|
||||
(Some(c), Some(r)) => append_line(
|
||||
lua,
|
||||
@@ -340,7 +345,16 @@ fn render_spell(
|
||||
// `0x52b2cd`) over the next rank's gold description, then the green `TOOLTIP_TALENT_LEARN`
|
||||
// hint (`0x52b362`). Both are the player's own strings; without them the line is skipped.
|
||||
if let Some(t) = talent {
|
||||
if let Some(next) = &t.next_desc {
|
||||
// The talent builder skips a rank with an empty description, header and all (`0x52b294`).
|
||||
let next = t
|
||||
.next_desc
|
||||
.as_ref()
|
||||
.filter(|n| !talent_builder || !n.is_empty());
|
||||
if let Some(next) = next {
|
||||
// The talent builder's gold `" "` spacer (`0x82ee00`, `0x52b2a8`) after a description.
|
||||
if talent_builder && !desc.is_empty() {
|
||||
append_line(lua, this, (" ".into(), GOLD), None, false)?;
|
||||
}
|
||||
if let Some(header) = crate::strings::global(lua, "TOOLTIP_TALENT_NEXT_RANK") {
|
||||
append_line(lua, this, (header, WHITE), None, false)?;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user