Unit aura bindings resolve their token as 1.12.1 does, so mouseover, party1target and any casing read the unit they name (#497)
`UnitBuff`, `UnitDebuff`, `UnitAura`, `CancelUnitBuff` and the tooltip's `SetUnitBuff`/`SetUnitDebuff` now resolve their unit token as 1.12.1's `0x515970` does before reading any aura, so `mouseover`, `party1target`, `raid3target`, `pettarget`, `targettargettarget` and any casing (`"PLAYER"`) answer for the unit they name, where before they answered nil. - The resolver matches the nine base names as prefixes in the reference's order, folding ASCII case (`SStrCmpI 0x64a4c0`), with `npc` whole; the number after `party`/`raid` wraps at 32 bits and counts from 1; `mouseover` counts only while the unit is loaded (`0x515bd2`); each `target` suffix hops to that unit's `UNIT_FIELD_TARGET` (`0x5159d3`-`0x515a2c`); a name none of the nine match raises `Unknown unit name: %s` (`0x515c14`), before any index check, as `UnitBuff` calls it at `0x519542`. - The VM holds one aura list per guid, plus the player's own in cache order (`0xbc6040`); any token naming the player reads that list, so its order and durations are unchanged. - The feed builds lists only for guids some token can name, through the existing per-guid cache. `UNIT_AURA` fires for the same tokens as before. Fixes #488 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8d31e9e91e
commit
65a5e44d48
@@ -69,7 +69,7 @@ fn aura(
|
||||
|
||||
/// Push the list and fire `PLAYER_AURAS_CHANGED`, as the app's feed does (`BuffFrame.lua:113`).
|
||||
fn push(s: &mut UiScript, auras: Vec<AuraState>) {
|
||||
s.set_auras("player", Some(auras));
|
||||
s.set_player_auras(auras);
|
||||
s.fire_event("PLAYER_AURAS_CHANGED", vec![]);
|
||||
}
|
||||
|
||||
@@ -351,7 +351,7 @@ fn a_refreshed_duration_reaches_the_bar_with_no_aura_event() {
|
||||
assert_eq!(text(&s, "BuffButton0Duration"), "19 s");
|
||||
|
||||
// The refresh: a new expiry and no event.
|
||||
s.set_auras("player", Some(vec![mark(300.0)]));
|
||||
s.set_player_auras(vec![mark(300.0)]);
|
||||
frame(&mut s, 0.1);
|
||||
assert_eq!(
|
||||
text(&s, "BuffButton0Duration"),
|
||||
@@ -367,14 +367,14 @@ fn a_refreshed_duration_reaches_the_bar_with_no_aura_event() {
|
||||
// timed aura with no stamp yet shows "0 s" and pulses until the stamp joins.
|
||||
let mut pending = mark(0.0);
|
||||
pending.until_cancelled = false; // a timed spell, stamp not yet arrived
|
||||
s.set_auras("player", Some(vec![pending]));
|
||||
s.set_player_auras(vec![pending]);
|
||||
frame(&mut s, 0.1);
|
||||
assert_eq!(
|
||||
text(&s, "BuffButton0Duration"),
|
||||
"0 s",
|
||||
"the reference draws the floor of a timed aura it has no stamp for, not a blank"
|
||||
);
|
||||
s.set_auras("player", Some(vec![mark(60.0)])); // the stamp joins, still no event
|
||||
s.set_player_auras(vec![mark(60.0)]); // the stamp joins, still no event
|
||||
frame(&mut s, 0.1);
|
||||
assert_eq!(
|
||||
text(&s, "BuffButton0Duration"),
|
||||
@@ -885,17 +885,14 @@ fn the_duration_line_reads_the_real_global_strings() {
|
||||
|
||||
// `secs_left` seconds remaining at GetTime = 10, read back as the tooltip's last line.
|
||||
let mut line = |secs_left: f64| -> String {
|
||||
s.set_auras(
|
||||
"player",
|
||||
Some(vec![AuraState {
|
||||
spell_id: 1459,
|
||||
name: Some("Arcane Intellect".into()),
|
||||
duration: 86_400.0,
|
||||
expiration_time: 10.0 + secs_left,
|
||||
helpful: true,
|
||||
..Default::default()
|
||||
}]),
|
||||
);
|
||||
s.set_player_auras(vec![AuraState {
|
||||
spell_id: 1459,
|
||||
name: Some("Arcane Intellect".into()),
|
||||
duration: 86_400.0,
|
||||
expiration_time: 10.0 + secs_left,
|
||||
helpful: true,
|
||||
..Default::default()
|
||||
}]);
|
||||
s.run(
|
||||
r#"
|
||||
BENILLA_TIP:SetOwner(BENILLA_ANCHOR, "ANCHOR_RIGHT")
|
||||
|
||||
@@ -260,8 +260,15 @@ fn the_debuff_row_fills_from_the_pets_own_auras() {
|
||||
s.set_unit("pet", Some(pet("Grimjaw", 72, 45, 80, 0)));
|
||||
s.fire_event("UNIT_PET", vec![ScriptValue::Str("player".into())]);
|
||||
|
||||
s.set_auras(
|
||||
"pet",
|
||||
// `"pet"` resolves to the pet's guid, whose list the row reads.
|
||||
const PET: u64 = 0xF140_0000_0000_0077;
|
||||
s.set_unit_guids(&benilla_ui::script::UnitGuids {
|
||||
player: 1,
|
||||
pet: PET,
|
||||
..Default::default()
|
||||
});
|
||||
s.set_unit_auras(
|
||||
PET,
|
||||
Some(vec![pet_buff(1000, "Rend", 1), pet_buff(1001, "Sunder", 3)]),
|
||||
);
|
||||
s.fire_event("UNIT_AURA", vec![ScriptValue::Str("pet".into())]);
|
||||
@@ -289,7 +296,7 @@ fn the_debuff_row_fills_from_the_pets_own_auras() {
|
||||
);
|
||||
assert!(draws(&mut s, "Interface\\Icons\\Spell_1001"));
|
||||
|
||||
s.set_auras("pet", Some(vec![]));
|
||||
s.set_unit_auras(PET, Some(vec![]));
|
||||
s.fire_event("UNIT_AURA", vec![ScriptValue::Str("pet".into())]);
|
||||
assert!(!s
|
||||
.eval::<bool>("return PetFrameDebuff1:IsVisible()")
|
||||
|
||||
@@ -323,22 +323,23 @@ fn auto_shot_bar_drains_the_moment_an_ammo_lock_event_arrives() {
|
||||
/// Seat one active player buff by name and announce it the way the app's aura feed does.
|
||||
fn seat_a_buff(s: &mut UiScript, spell_id: u32, name: &str, icon: &str) {
|
||||
use benilla_ui::script::AuraState;
|
||||
s.set_auras(
|
||||
"player",
|
||||
Some(vec![AuraState {
|
||||
spell_id,
|
||||
name: Some(name.into()),
|
||||
icon: Some(icon.into()),
|
||||
count: 0,
|
||||
debuff_type: None,
|
||||
duration: 0.0,
|
||||
expiration_time: 0.0,
|
||||
helpful: true,
|
||||
cancelable: true,
|
||||
until_cancelled: true,
|
||||
channeled: false,
|
||||
}]),
|
||||
);
|
||||
s.set_unit_guids(&benilla_ui::script::UnitGuids {
|
||||
player: 1,
|
||||
..Default::default()
|
||||
});
|
||||
s.set_player_auras(vec![AuraState {
|
||||
spell_id,
|
||||
name: Some(name.into()),
|
||||
icon: Some(icon.into()),
|
||||
count: 0,
|
||||
debuff_type: None,
|
||||
duration: 0.0,
|
||||
expiration_time: 0.0,
|
||||
helpful: true,
|
||||
cancelable: true,
|
||||
until_cancelled: true,
|
||||
channeled: false,
|
||||
}]);
|
||||
s.fire_event("PLAYER_AURAS_CHANGED", vec![]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! The stock target frame's aura rows (`TargetFrame.lua:263`) under a stubbed feed.
|
||||
//! BuffFrame.xml loads first for the `DebuffTypeColor` and `RefreshBuffs` the frames use.
|
||||
|
||||
use benilla_ui::script::{AuraState, QuadContent, ScriptValue, UiScript, UnitState};
|
||||
use benilla_ui::script::{AuraState, QuadContent, ScriptValue, UiScript, UnitGuids, UnitState};
|
||||
|
||||
use super::test_ui::load_ui as load_xml;
|
||||
|
||||
@@ -56,10 +56,18 @@ fn target(s: &mut UiScript, reaction: u8, auras: Vec<AuraState>) {
|
||||
..UnitState::default()
|
||||
}),
|
||||
);
|
||||
s.set_auras("target", Some(auras));
|
||||
s.set_unit_guids(&UnitGuids {
|
||||
player: 1,
|
||||
target: TARGET,
|
||||
..Default::default()
|
||||
});
|
||||
s.set_unit_auras(TARGET, Some(auras));
|
||||
s.fire_event("PLAYER_TARGET_CHANGED", vec![]);
|
||||
}
|
||||
|
||||
/// The target's guid, which `"target"` resolves to.
|
||||
const TARGET: u64 = 0xF130_0000_0000_0001;
|
||||
|
||||
fn debuff(spell_id: u32, name: &str, count: u8, debuff_type: Option<&str>) -> AuraState {
|
||||
AuraState {
|
||||
spell_id,
|
||||
@@ -235,7 +243,7 @@ fn reaching_the_wrap_shrinks_the_first_row_to_17px() {
|
||||
assert_eq!(size(&s, "TargetFrameBuff1"), (17.0, 17.0));
|
||||
|
||||
// Below the wrap they grow back; the feed re-fires UNIT_AURA on the change.
|
||||
s.set_auras("target", Some(vec![debuff(1000, "D0", 1, None)]));
|
||||
s.set_unit_auras(TARGET, Some(vec![debuff(1000, "D0", 1, None)]));
|
||||
s.fire_event("UNIT_AURA", vec![ScriptValue::Str("target".into())]);
|
||||
assert_eq!(size(&s, "TargetFrameDebuff1"), (21.0, 21.0));
|
||||
assert_eq!(size(&s, "TargetFrameDebuff1Border"), (23.0, 23.0));
|
||||
@@ -249,7 +257,7 @@ fn clearing_the_list_or_the_target_hides_the_buttons() {
|
||||
target(&mut s, 2, vec![debuff(589, "Pain", 1, Some("Magic"))]);
|
||||
assert!(shown(&s, "TargetFrameDebuff1"));
|
||||
|
||||
s.set_auras("target", Some(vec![]));
|
||||
s.set_unit_auras(TARGET, Some(vec![]));
|
||||
s.fire_event("UNIT_AURA", vec![ScriptValue::Str("target".into())]);
|
||||
assert!(
|
||||
!shown(&s, "TargetFrameDebuff1"),
|
||||
@@ -260,7 +268,7 @@ fn clearing_the_list_or_the_target_hides_the_buttons() {
|
||||
target(&mut s, 2, vec![debuff(589, "Pain", 1, Some("Magic"))]);
|
||||
assert!(shown(&s, "TargetFrameDebuff1"));
|
||||
s.set_unit("target", None);
|
||||
s.set_auras("target", None);
|
||||
s.set_unit_auras(TARGET, None);
|
||||
s.fire_event("PLAYER_TARGET_CHANGED", vec![]);
|
||||
assert!(!shown(&s, "TargetFrameDebuff1"), "no target, no buttons");
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
|
||||
@@ -432,22 +432,19 @@ fn buff_hover_hangs_below_left_of_the_button() {
|
||||
"Interface\\FrameXML\\TextStatusBar.xml",
|
||||
"Interface\\FrameXML\\BuffFrame.xml",
|
||||
]);
|
||||
s.set_auras(
|
||||
"player",
|
||||
Some(vec![AuraState {
|
||||
spell_id: 1459,
|
||||
name: Some("Arcane Intellect".into()),
|
||||
icon: Some("Interface\\Icons\\Spell_Holy_MagicalSentry".into()),
|
||||
count: 1,
|
||||
debuff_type: None,
|
||||
duration: 1800.0,
|
||||
expiration_time: 1800.0,
|
||||
helpful: true,
|
||||
cancelable: true,
|
||||
until_cancelled: false,
|
||||
channeled: false,
|
||||
}]),
|
||||
);
|
||||
s.set_player_auras(vec![AuraState {
|
||||
spell_id: 1459,
|
||||
name: Some("Arcane Intellect".into()),
|
||||
icon: Some("Interface\\Icons\\Spell_Holy_MagicalSentry".into()),
|
||||
count: 1,
|
||||
debuff_type: None,
|
||||
duration: 1800.0,
|
||||
expiration_time: 1800.0,
|
||||
helpful: true,
|
||||
cancelable: true,
|
||||
until_cancelled: false,
|
||||
channeled: false,
|
||||
}]);
|
||||
// The event the stock buff buttons register (`BuffFrame.lua:113`).
|
||||
s.fire_event("PLAYER_AURAS_CHANGED", vec![]);
|
||||
s.resolve();
|
||||
|
||||
@@ -402,8 +402,9 @@ fn the_debuff_row_draws_what_the_unit_carries() {
|
||||
benilla_formats::wow_data_or_skip!();
|
||||
let mut s = load_tot();
|
||||
switch_on(&mut s);
|
||||
s.set_auras(
|
||||
"targettarget",
|
||||
s.set_unit_guids(&unit_guids());
|
||||
s.set_unit_auras(
|
||||
TOT,
|
||||
Some(vec![
|
||||
debuff(1000, "Rend", None),
|
||||
debuff(1001, "Curse of Agony", Some("Curse")),
|
||||
@@ -437,7 +438,8 @@ fn the_target_rows_wrap_short_while_the_frame_stands_beside_them() {
|
||||
benilla_formats::wow_data_or_skip!();
|
||||
let mut s = load_tot();
|
||||
// A hostile target, so the debuffs lead and the buffs hang off them.
|
||||
s.set_auras("target", Some(vec![debuff(2000, "Sunder", None)]));
|
||||
s.set_unit_guids(&unit_guids());
|
||||
s.set_unit_auras(TARGET, Some(vec![debuff(2000, "Sunder", None)]));
|
||||
s.fire_event("UNIT_AURA", vec![ScriptValue::Str("target".into())]);
|
||||
|
||||
let anchor = |s: &mut UiScript, frame: &str| -> String {
|
||||
@@ -498,3 +500,17 @@ fn the_reconcile_takes_the_frame_down_when_the_token_goes_silent() {
|
||||
assert!(shown(&mut s), "and brings it back");
|
||||
assert!(s.errors().is_empty(), "script errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
const TARGET: u64 = 0xF130_0000_0000_0001;
|
||||
const TOT: u64 = 0xF130_0000_0000_0002;
|
||||
|
||||
/// The resolver's inputs: `"target"` is `TARGET`, held and targeting `TOT`, so `"targettarget"`
|
||||
/// is `TOT`.
|
||||
fn unit_guids() -> benilla_ui::script::UnitGuids {
|
||||
benilla_ui::script::UnitGuids {
|
||||
player: 1,
|
||||
target: TARGET,
|
||||
held: [(TARGET, TOT)].into_iter().collect(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user