Unit tooltips title the unit with its PvP name, as 1.12.1 does (#802)
A unit tooltip's title read the bare name where 1.12.1 decorates it. The tooltip calls the name builder `0x609370` itself (`0x52a1ab`, or `0x52a1db` when there is no second part) with the decoration flag a literal `1`, the same call `UnitPVPName` makes, so no CVar gates it. With the flag on, a ranked player reads `UNIT_PVP_NAME` of the gendered `PVP_RANK_<rank>_<team>` and the name, a city protector's `PVP_MEDAL<n>` follows on a second line (`0x6093ef`), and a civilian kill (`0x612550`) reads the `PVP_RANK_CIVILIAN` prefix (`0x609461`). The title now runs the builder `UnitPVPName` already uses. The test covers rank, gender, medal, civilian, an unranked player and a creature; an on-demand `ui-tooltip-rank` capture shows a ranked player's tooltip. Fixes #794 Co-Authored-By: Claude Opus 5.5 <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
ab909df6d9
commit
bb84f1f1fb
@@ -317,9 +317,9 @@ pub fn decorated_name(
|
||||
Some(format_two_strings(&template, &title, name))
|
||||
}
|
||||
|
||||
/// `0x609370`, the name builder behind `UnitPVPName` (its legs are at the binding); `name` is the
|
||||
/// plain `UnitName`.
|
||||
fn pvp_name(lua: &Lua, u: &super::UnitState, name: &str, player_level: u32) -> String {
|
||||
/// `0x609370`, the name builder behind `UnitPVPName` (its legs are at the binding) and the unit
|
||||
/// tooltip's title (`0x52a1ab`), both with the decoration flag `1`; `name` is the plain `UnitName`.
|
||||
pub(super) fn pvp_name(lua: &Lua, u: &super::UnitState, name: &str, player_level: u32) -> String {
|
||||
// Leg A: a ranked player; the title is gendered by this unit and range-unchecked.
|
||||
if u.is_player && u.pvp_rank != 0 {
|
||||
let decorated = decorated_name(
|
||||
|
||||
@@ -182,6 +182,105 @@ fn a_pending_name_titles_unknownobject_and_the_answer_replaces_it() {
|
||||
assert!(s.take_errors().is_empty());
|
||||
}
|
||||
|
||||
/// The title is decorated by `0x609370` with the flag on (`0x52a1ab`), the builder `UnitPVPName`
|
||||
/// runs: a ranked player's rank first through `UNIT_PVP_NAME`, gendered by the unit's sex, and a
|
||||
/// city protector's medal on a second line; a civilian kill's `PVP_RANK_CIVILIAN` prefix; anyone
|
||||
/// else the plain name.
|
||||
#[test]
|
||||
fn the_tooltip_title_is_the_pvp_name() {
|
||||
let mut s = script();
|
||||
seed_level_strings(&mut s);
|
||||
s.set_screen_size(800.0, 600.0);
|
||||
s.set_player_req_state(PlayerReqState {
|
||||
level: 30,
|
||||
..Default::default()
|
||||
});
|
||||
let player = |sex: u8, pvp_rank: u8| UnitState {
|
||||
exists: true,
|
||||
is_player: true,
|
||||
guid: 0x0000_0000_0000_002A,
|
||||
name: Some("Pfedh".into()),
|
||||
level: 1,
|
||||
race: Some("Human".into()),
|
||||
class: Some("Warrior".into()),
|
||||
sex,
|
||||
pvp_rank,
|
||||
pvp_team: 1,
|
||||
..Default::default()
|
||||
};
|
||||
s.run(
|
||||
r#"
|
||||
UNIT_PVP_NAME = "%s %s"
|
||||
PVP_RANK_7_1 = "Sergeant"
|
||||
PVP_RANK_7_1_FEMALE = "Sergeant (f)"
|
||||
PVP_MEDAL1 = "Guardian of Stormwind"
|
||||
PVP_RANK_CIVILIAN = "Civilian"
|
||||
local a = CreateFrame("Button", "UF1"); a:SetPoint("CENTER", 0, 0); a:SetWidth(10); a:SetHeight(10)
|
||||
local tt = CreateFrame("GameTooltip", "TT")
|
||||
tt:SetOwner(a, "ANCHOR_RIGHT")
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
let title = |s: &UiScript| -> String { s.eval("return TTTextLeft1:GetText()").unwrap() };
|
||||
|
||||
s.set_unit("target", Some(player(2, 7)));
|
||||
s.run(r#"assert(TT:SetUnit("target") == 1)"#).unwrap();
|
||||
assert_eq!(title(&s), "Sergeant Pfedh", "the rank rides first");
|
||||
|
||||
s.set_unit("target", Some(player(3, 7)));
|
||||
s.run(r#"assert(TT:SetUnit("target") == 1)"#).unwrap();
|
||||
assert_eq!(
|
||||
title(&s),
|
||||
"Sergeant (f) Pfedh",
|
||||
"the unit's sex picks the twin"
|
||||
);
|
||||
|
||||
s.set_unit("target", Some(player(2, 0)));
|
||||
s.run(r#"assert(TT:SetUnit("target") == 1)"#).unwrap();
|
||||
assert_eq!(
|
||||
title(&s),
|
||||
"Pfedh",
|
||||
"an unranked player keeps the plain name"
|
||||
);
|
||||
|
||||
let mut protector = player(2, 7);
|
||||
protector.pvp_medal = 1;
|
||||
s.set_unit("target", Some(protector));
|
||||
s.run(r#"assert(TT:SetUnit("target") == 1)"#).unwrap();
|
||||
assert_eq!(
|
||||
title(&s),
|
||||
"Sergeant Pfedh\nGuardian of Stormwind",
|
||||
"the medal rides a second line"
|
||||
);
|
||||
|
||||
let mut beast = wolf();
|
||||
beast.pvp_rank = 7; // a creature carries no rank byte, and must not read one anyway
|
||||
s.set_unit("target", Some(beast));
|
||||
s.run(r#"assert(TT:SetUnit("target") == 1)"#).unwrap();
|
||||
assert_eq!(title(&s), "Timber Wolf", "a creature takes no rank");
|
||||
|
||||
// Hostile, PvP-flagged, a civilian and grey (`0x612550`): the dishonorable-kill prefix.
|
||||
s.set_unit(
|
||||
"target",
|
||||
Some(UnitState {
|
||||
exists: true,
|
||||
name: Some("Defias Civilian".into()),
|
||||
level: 20,
|
||||
reaction: 2,
|
||||
pvp: true,
|
||||
civilian: true,
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
s.run(r#"assert(TT:SetUnit("target") == 1)"#).unwrap();
|
||||
assert_eq!(
|
||||
title(&s),
|
||||
"Civilian Defias Civilian",
|
||||
"a civilian kill warns"
|
||||
);
|
||||
assert!(s.take_errors().is_empty());
|
||||
}
|
||||
|
||||
/// The faction line sits between the level line and "PvP" (`0x529fe0`). Civilian needs the PvP
|
||||
/// bit, the flag, hostility and a grey level (`0x612550`); Leader only the PvP bit and the flag
|
||||
/// (`0x6125c0`).
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! The engine unit tooltip, in `0x529fe0`'s line order: the name (gold; FrameXML recolours it by
|
||||
//! reaction), the creature subtitle, the level line, the faction name, then "PvP", "Skinnable",
|
||||
//! "Civilian" and "Leader", with health on the `<name>StatusBar` child. The world mouseover drives
|
||||
//! it through [`super::UiScript::world_tooltip_unit`]; unit-frame hovers call `SetUnit` from Lua.
|
||||
//! The engine unit tooltip, in `0x529fe0`'s line order: the name (gold, decorated by `0x609370`
|
||||
//! as `UnitPVPName`'s is; FrameXML recolours it by reaction), the creature subtitle, the level
|
||||
//! line, the faction name, then "PvP", "Skinnable", "Civilian" and "Leader", with health on the
|
||||
//! `<name>StatusBar` child. The world mouseover drives it through
|
||||
//! [`super::UiScript::world_tooltip_unit`]; unit-frame hovers call `SetUnit` from Lua.
|
||||
|
||||
use mlua::{Lua, Table};
|
||||
|
||||
@@ -118,10 +119,12 @@ fn render_unit(lua: &Lua, this: &Table, token: &str) -> mlua::Result<bool> {
|
||||
return Ok(false);
|
||||
};
|
||||
// The name: `CGUnit_C::GetUnitName` `0x609210` (`0x52a187`), whose every miss falls to
|
||||
// `UNKNOWNOBJECT`, as `UnitName`'s does. A token with no object never reaches the builder
|
||||
// `UNKNOWNOBJECT`, as `UnitName`'s does, then decorated by `0x609370` with the flag a literal
|
||||
// `1` (`0x52a19b`), as `UnitPVPName` does, so no CVar gates it: a ranked player's rank and
|
||||
// medal line, a civilian kill's prefix. A token with no object never reaches the builder
|
||||
// (`0x468460`): no plate, and `SetUnit` answers nil.
|
||||
let title = match &u.name {
|
||||
Some(n) => n.clone(),
|
||||
Some(name) => super::pvp::pvp_name(lua, &u, name, player_level),
|
||||
None => unknownobject(lua)?.to_str()?.to_string(),
|
||||
};
|
||||
append_line(lua, this, (title, GOLD), None, false)?;
|
||||
|
||||
Reference in New Issue
Block a user