superwow: TrackUnit/UntrackUnit, and the four chat-bubble CVars
TrackUnit is the one with a live consumer and a real feature behind it: 1.12 draws party members on the minimap and raid members NOWHERE, and pfUI puts the whole raid on it with these two — for every party and raid token on PARTY_MEMBERS_CHANGED/RAID_ROSTER_UPDATE/PLAYER_ENTERING_WORLD, each inside a pcall, so on a client without the function the feature is simply absent, which is what it has been here. The set is guids, not tokens, so a tracked raid member keeps its dot while the roster shuffles its token out from under it. The dot itself is drawn by a fourth always-show clause beside the reference's own three (`shows_tracking_dot`) — `tracked_creature` stays exactly `0x5ed210`, so the predicate that file documents is still the client's, and a tracked unit gets the ordinary cell, hover name and distance cull with nothing new drawn. The set is read off the VM handle the blip system already holds rather than behind a resource of its own: `BlipInputs` is at the 16-param ceiling and its last element is already a nested pair for that very reason. The bubble four come straight off the DLL's string pool. Three are nampower's `NP_ChatBubbles*` under SuperWoW's unprefixed names, writing the same fields — two mods, one knob, last writer wins, as the nameplate reach already is — each keeping 0 because the reference bubbles none of those kinds. `ChatBubblesCreatures` is SuperWoW's alone and the only behaviour of the four: it NARROWS `ChatBubbles` rather than standing beside it, because the mod hooks the reference's own gate and a switch added at that site can only subtract; the other reading would bubble creatures with the master switch off. It ships 1, so an untouched client bubbles monster say and yell exactly as 1.12 does. All four are welded to their struct literals in the defaults-mirror test. CREATE_CHATBUBBLE is deliberately NOT fired, and the row says why: its payload can only be the bubble, and this client's bubbles are not Lua frames — the same wall `C_ChatBubbles.GetAllChatBubbles` already answers an empty list for. Argless would be worse than silence, since an addon written against upstream reaches for arg1's regions and raises once per bubble. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -63,6 +63,16 @@ pub(crate) struct BubbleConfig {
|
||||
pub(crate) raid: bool,
|
||||
/// nampower's `NP_ChatBubblesBattleground`: the battleground and battleground-leader kinds.
|
||||
pub(crate) battleground: bool,
|
||||
/// SuperWoW's `ChatBubblesCreatures`: the monster say and yell kinds, which the reference
|
||||
/// bubbles under `ChatBubbles` with everything else. Defaults **on**, so a player who never
|
||||
/// touched it keeps the reference's behaviour; see [`bubble_cvar`] for why it narrows
|
||||
/// `ChatBubbles` rather than replacing it.
|
||||
///
|
||||
/// The three kinds above take SuperWoW's unprefixed spellings too — `ChatBubblesWhisper`,
|
||||
/// `ChatBubblesRaid`, `ChatBubblesBattleground` — on these same fields. Two mods, one knob,
|
||||
/// last writer wins, which is what both mods injected does and what a player moving either
|
||||
/// slider expects (the nameplate reach already works this way).
|
||||
pub(crate) creatures: bool,
|
||||
}
|
||||
|
||||
impl Default for BubbleConfig {
|
||||
@@ -75,6 +85,7 @@ impl Default for BubbleConfig {
|
||||
whisper: false,
|
||||
raid: false,
|
||||
battleground: false,
|
||||
creatures: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +129,13 @@ fn bubble_cvar(kind: ChatEventKind, cfg: &BubbleConfig) -> Option<bool> {
|
||||
use ChatEventKind as K;
|
||||
match kind {
|
||||
K::Party => Some(cfg.party),
|
||||
K::Say | K::Yell | K::MonsterSay | K::MonsterYell => Some(cfg.all),
|
||||
K::Say | K::Yell => Some(cfg.all),
|
||||
// SuperWoW's `ChatBubblesCreatures` **narrows** `ChatBubbles` rather than standing beside
|
||||
// it: the mod hooks this same gate, and the gate the reference has here is `ChatBubbles`
|
||||
// (`0x608b0d`), so a switch added at the site can only subtract. The alternative reading —
|
||||
// `Some(cfg.creatures)` alone — would bubble creatures with the master switch off, which
|
||||
// is a surprise no setting advertises.
|
||||
K::MonsterSay | K::MonsterYell => Some(cfg.all && cfg.creatures),
|
||||
// nampower's three extra kinds. Each stays `Some(false)` rather than `None` when its
|
||||
// switch is off, because the two answers differ where it matters: `None` is "this kind
|
||||
// never bubbles", which is what the trace prints and what a future reader would take as
|
||||
@@ -699,9 +716,13 @@ pub(crate) fn on_cvar(ev: On<crate::cvars::CvarChanged>, mut bubbles: ResMut<Bub
|
||||
// nampower's four. The distance shares `range_sq` with SuperWoW's name above and takes
|
||||
// the same clamp: a bubble cull of 0 or of a mile is not a setting either mod offers.
|
||||
"np_chatbubbledistance" => bubbles.range_sq = ev.num().clamp(10.0, 200.0).powi(2),
|
||||
"np_chatbubbleswhisper" => bubbles.whisper = ev.flag(),
|
||||
"np_chatbubblesraid" => bubbles.raid = ev.flag(),
|
||||
"np_chatbubblesbattleground" => bubbles.battleground = ev.flag(),
|
||||
"np_chatbubbleswhisper" | "chatbubbleswhisper" => bubbles.whisper = ev.flag(),
|
||||
"np_chatbubblesraid" | "chatbubblesraid" => bubbles.raid = ev.flag(),
|
||||
"np_chatbubblesbattleground" | "chatbubblesbattleground" => {
|
||||
bubbles.battleground = ev.flag();
|
||||
}
|
||||
// SuperWoW's own, with no nampower twin.
|
||||
"chatbubblescreatures" => bubbles.creatures = ev.flag(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -817,6 +838,32 @@ mod tests {
|
||||
None,
|
||||
"plain /raid still never bubbles"
|
||||
);
|
||||
// SuperWoW's `ChatBubblesCreatures` narrows `ChatBubbles`: off, the monster kinds stop
|
||||
// and the player kinds do not; and with the master switch off it cannot turn them back on.
|
||||
let no_creatures = BubbleConfig {
|
||||
creatures: false,
|
||||
..on
|
||||
};
|
||||
for k in [K::MonsterSay, K::MonsterYell] {
|
||||
assert_eq!(bubble_cvar(k, &no_creatures), Some(false), "{k:?} silenced");
|
||||
}
|
||||
for k in [K::Say, K::Yell] {
|
||||
assert_eq!(
|
||||
bubble_cvar(k, &no_creatures),
|
||||
Some(true),
|
||||
"{k:?} is a player kind and keeps bubbling"
|
||||
);
|
||||
}
|
||||
let creatures_only = BubbleConfig {
|
||||
all: false,
|
||||
creatures: true,
|
||||
..on
|
||||
};
|
||||
assert_eq!(
|
||||
bubble_cvar(K::MonsterSay, &creatures_only),
|
||||
Some(false),
|
||||
"the master switch wins: a new switch at the reference's own gate can only subtract"
|
||||
);
|
||||
|
||||
// Party off with say on is the default pair, so this also pins the boot state.
|
||||
let no_party = BubbleConfig::default();
|
||||
|
||||
@@ -502,6 +502,34 @@ pub(crate) const REGISTERED: &[Registered] = &[
|
||||
"20",
|
||||
"SuperWoW addon-compat: see crate::chat_bubble::BubbleConfig::range_sq",
|
||||
),
|
||||
// SuperWoW's other four bubble rows, read off the DLL's own string pool beside that one.
|
||||
// Three are nampower's `NP_ChatBubbles*` under their unprefixed names and write the SAME
|
||||
// `BubbleConfig` fields — two mods, one knob, last writer wins, as the nameplate reach
|
||||
// already is. Each keeps `0`: the reference bubbles none of those three kinds, and a default
|
||||
// is what a player who never touched the setting gets.
|
||||
ours(
|
||||
"ChatBubblesWhisper",
|
||||
"0",
|
||||
"SuperWoW addon-compat, LIVE: nampower's `NP_ChatBubblesWhisper` under SuperWoW's own name, on the same field — see crate::chat_bubble::BubbleConfig::whisper",
|
||||
),
|
||||
ours(
|
||||
"ChatBubblesRaid",
|
||||
"0",
|
||||
"SuperWoW addon-compat, LIVE: nampower's `NP_ChatBubblesRaid` under SuperWoW's own name, on the same field — see crate::chat_bubble::BubbleConfig::raid",
|
||||
),
|
||||
ours(
|
||||
"ChatBubblesBattleground",
|
||||
"0",
|
||||
"SuperWoW addon-compat, LIVE: nampower's `NP_ChatBubblesBattleground` under SuperWoW's own name, on the same field — see crate::chat_bubble::BubbleConfig::battleground",
|
||||
),
|
||||
// This one is SuperWoW's alone and is the only behaviour of the four: it splits the monster
|
||||
// say and yell kinds out of `ChatBubbles`, which the reference gates them under. Ships `1`,
|
||||
// so an untouched client still bubbles them exactly as 1.12 does.
|
||||
ours(
|
||||
"ChatBubblesCreatures",
|
||||
"1",
|
||||
"SuperWoW addon-compat, LIVE: the monster say and yell kinds, which 1.12 gates under `ChatBubbles` with everything else — see crate::chat_bubble::BubbleConfig::creatures",
|
||||
),
|
||||
// Registered "1" (`0x82e748`), category 4. `profanityFilter` (`0x402e68`, name `0x82e7f4`,
|
||||
// callback `0x403570`) masks `ChatProfanity.dbc` spans inside the shared masker `0x4a1a66`,
|
||||
// covering all thirteen call sites; `spamFilter` (`0x402e8e`, name `0x82e7d4`, callback
|
||||
@@ -3084,6 +3112,24 @@ mod tests {
|
||||
assert_eq!(d["ChatBubbles"] != 0.0, bubbles.all);
|
||||
assert_eq!(d["ChatBubblesParty"] != 0.0, bubbles.party);
|
||||
assert!(bubbles.all && !bubbles.party, "the binary's own pair");
|
||||
// The four SuperWoW rows and the three nampower twins that share their fields: every one
|
||||
// of these IS the live configuration for a player who never wrote it, so a registered
|
||||
// default that drifts from the struct literal is a setting that reads one way in
|
||||
// `config.toml` and behaves another (see `benilla-cvar-default-trap`).
|
||||
assert_eq!(d["ChatBubblesWhisper"] != 0.0, bubbles.whisper);
|
||||
assert_eq!(d["ChatBubblesRaid"] != 0.0, bubbles.raid);
|
||||
assert_eq!(d["ChatBubblesBattleground"] != 0.0, bubbles.battleground);
|
||||
assert_eq!(d["ChatBubblesCreatures"] != 0.0, bubbles.creatures);
|
||||
assert_eq!(d["NP_ChatBubblesWhisper"], d["ChatBubblesWhisper"]);
|
||||
assert_eq!(d["NP_ChatBubblesRaid"], d["ChatBubblesRaid"]);
|
||||
assert_eq!(
|
||||
d["NP_ChatBubblesBattleground"], d["ChatBubblesBattleground"],
|
||||
"one field cannot have two different defaults, whichever mod's name wrote it"
|
||||
);
|
||||
assert!(
|
||||
bubbles.creatures,
|
||||
"1.12 bubbles monster say and yell; an untouched client must keep doing it"
|
||||
);
|
||||
let zoom = MinimapZoom::default();
|
||||
assert_eq!(d["minimapZoom"], f32::from(zoom.outdoor));
|
||||
assert_eq!(d["minimapInsideZoom"], f32::from(zoom.inside));
|
||||
|
||||
@@ -162,6 +162,24 @@ fn tracked_creature(
|
||||
.is_some_and(|t| (1..=32).contains(&t) && tracking.creatures & (1u32 << (t - 1)) != 0)
|
||||
}
|
||||
|
||||
/// Whether a unit draws a tracking dot: the reference's own predicate, or SuperWoW's
|
||||
/// `TrackUnit` set (`benilla-ui`'s `compat/superwow.rs`), which is a fourth always-show clause
|
||||
/// the mod adds rather than a change to the three [`tracked_creature`] documents.
|
||||
///
|
||||
/// `addon_tracked` is sorted by guid, so this is a binary search per candidate and not a set
|
||||
/// build per frame.
|
||||
fn shows_tracking_dot(
|
||||
addon_tracked: &[u64],
|
||||
guid: u64,
|
||||
tracking: SelfTracking,
|
||||
creature_type: Option<u32>,
|
||||
dynamic_flags: u32,
|
||||
unit_creeping: bool,
|
||||
) -> bool {
|
||||
addon_tracked.binary_search(&guid).is_ok()
|
||||
|| tracked_creature(tracking, creature_type, dynamic_flags, unit_creeping)
|
||||
}
|
||||
|
||||
/// The creature-type resolver `0x605570`: a shapeshift form's `SpellShapeshiftForm.dbc` type
|
||||
/// first (`<= 0` reads Humanoid), else an NPC's cached template, else Humanoid for a player.
|
||||
fn creature_type_of(
|
||||
@@ -196,6 +214,9 @@ pub(in crate::minimap) fn emit_tracking_dots(
|
||||
ctx: &BlipCtx,
|
||||
tracking: SelfTracking,
|
||||
candidates: &TrackedCandidates,
|
||||
// SuperWoW's `TrackUnit` set, sorted by guid (`benilla-ui`'s `compat/superwow.rs`): each of
|
||||
// these draws the ordinary tracked-unit dot whatever the player's own tracking spell is.
|
||||
addon_tracked: &[u64],
|
||||
statuses: &HashMap<u64, u32>,
|
||||
self_guid: Option<u64>,
|
||||
names: &NameCache,
|
||||
@@ -283,7 +304,14 @@ pub(in crate::minimap) fn emit_tracking_dots(
|
||||
names,
|
||||
forms,
|
||||
);
|
||||
if tracked_creature(tracking, creature_type, dyn_flags, creeping) {
|
||||
if shows_tracking_dot(
|
||||
addon_tracked,
|
||||
guid.0,
|
||||
tracking,
|
||||
creature_type,
|
||||
dyn_flags,
|
||||
creeping,
|
||||
) {
|
||||
dot(guid.0, tf, TRACKED_UNIT_CELL, DotName::Guid);
|
||||
}
|
||||
}
|
||||
@@ -461,6 +489,45 @@ mod tests {
|
||||
"a key-ITEM slot's index is an item entry, not a LockType"
|
||||
);
|
||||
assert!(!tracked_resource(1 << 2, 99, &locks), "unknown lock id");
|
||||
}
|
||||
|
||||
/// SuperWoW's `TrackUnit` set is a fourth always-show clause: a tracked guid draws its dot
|
||||
/// with **no** tracking spell up and no dyn-flag help, which is the whole point — 1.12 draws
|
||||
/// raid members on the minimap nowhere at all. An untracked guid is unaffected.
|
||||
#[test]
|
||||
fn the_addon_tracked_set_lights_a_dot_the_reference_would_not() {
|
||||
let none = SelfTracking {
|
||||
creatures: 0,
|
||||
resources: 0,
|
||||
stealthed: false,
|
||||
};
|
||||
let tracked = [0x10u64, 0x20, 0x30];
|
||||
|
||||
assert!(
|
||||
shows_tracking_dot(&tracked, 0x20, none, Some(7), 0, false),
|
||||
"in the set, nothing else on: the dot the mod exists to draw"
|
||||
);
|
||||
assert!(
|
||||
!shows_tracking_dot(&tracked, 0x21, none, Some(7), 0, false),
|
||||
"not in the set and nothing else on: no dot"
|
||||
);
|
||||
assert!(
|
||||
!shows_tracking_dot(&[], 0x20, none, Some(7), 0, false),
|
||||
"an empty set changes nothing — the usual case"
|
||||
);
|
||||
// The reference's own three clauses still stand on their own.
|
||||
assert!(
|
||||
shows_tracking_dot(&[], 0x20, none, Some(7), UNIT_DYNFLAG_TRACK_UNIT, false),
|
||||
"the server's own track bit"
|
||||
);
|
||||
let beasts = SelfTracking {
|
||||
creatures: 1 << 0,
|
||||
..none
|
||||
};
|
||||
assert!(
|
||||
shows_tracking_dot(&[], 0x20, beasts, Some(1), 0, false),
|
||||
"and the tracking spell's type mask"
|
||||
);
|
||||
|
||||
// Track Beasts sets bit 0 (Beast is creature type 1).
|
||||
let beasts = SelfTracking {
|
||||
|
||||
@@ -406,7 +406,15 @@ fn emit_minimap(
|
||||
*blip_hover = blips::MinimapBlipHover::None;
|
||||
// Drain the `Minimap:PingLocation` click before any early return: it is spent in the frame it
|
||||
// was made, and a click on a frame the map does not draw is not a ping.
|
||||
let click = script.and_then(|mut s| s.take_minimap_ping_request());
|
||||
// SuperWoW's `TrackUnit` set, read off the VM handle this system already holds rather than
|
||||
// behind a resource of its own: `BlipInputs` is at the 16-param ceiling (its last element is
|
||||
// already a nested pair for exactly that reason), and the set is at most a raid of guids.
|
||||
// Empty — the usual case — allocates nothing.
|
||||
let mut tracked_units: Vec<u64> = Vec::new();
|
||||
let click = script.and_then(|mut s| {
|
||||
tracked_units = s.tracked_units();
|
||||
s.take_minimap_ping_request()
|
||||
});
|
||||
let (Some(slot), Some(assets), Some(map), Some(catalog)) =
|
||||
(widget.0.as_ref(), assets, map, catalog)
|
||||
else {
|
||||
@@ -744,6 +752,7 @@ fn emit_minimap(
|
||||
ctx,
|
||||
tracking,
|
||||
&tracked,
|
||||
&tracked_units,
|
||||
quest.statuses(),
|
||||
self_guid,
|
||||
&names,
|
||||
|
||||
@@ -218,6 +218,45 @@ pub(in crate::script) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
// is dropped by the same rule every routed line follows, which is upstream's behaviour too —
|
||||
// the function appends to an open file.
|
||||
//
|
||||
// TrackUnit("unit") / UntrackUnit("unit"): put a minimap tracking dot on a named unit, and
|
||||
// take it off. The set is by **guid**, not by token, so a tracked raid member keeps its dot
|
||||
// while the roster shuffles its token out from under it.
|
||||
//
|
||||
// Why this is a real feature rather than a cosmetic one: 1.12 draws party members on the
|
||||
// minimap and **raid members nowhere** (`minimap::blips::emit_party_dots` walks
|
||||
// `party_slots()`, which is what the reference's own placement does). pfUI calls `TrackUnit`
|
||||
// for all four party and all forty raid tokens on every `PARTY_MEMBERS_CHANGED`,
|
||||
// `RAID_ROSTER_UPDATE` and `PLAYER_ENTERING_WORLD` under `C.unitframes.track_group`
|
||||
// (`modules/superwow.lua:96-127`), each inside a `pcall` — so on a client without the
|
||||
// function the whole feature is simply absent, which is what it has been here.
|
||||
//
|
||||
// The dot is drawn by the same always-show clause the reference's own creature tracking uses
|
||||
// (`UNIT_DYNFLAG_TRACK_UNIT`, `0x5ed210`), so a tracked unit gets the ordinary tracked-unit
|
||||
// cell, hover name and distance cull, and nothing new had to be drawn. A token naming
|
||||
// nothing live is ignored rather than raising: pfUI calls these on units that may have just
|
||||
// left the group, which is exactly the case its `pcall` is defending against.
|
||||
for (name, track) in [("TrackUnit", true), ("UntrackUnit", false)] {
|
||||
g.set(
|
||||
name,
|
||||
lua.create_function(move |lua, token: Option<String>| {
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
let guid = token
|
||||
.as_deref()
|
||||
.and_then(|t| model.unit(t))
|
||||
.map(|u| u.guid)
|
||||
.filter(|g| *g != 0);
|
||||
if let Some(guid) = guid {
|
||||
if track {
|
||||
model.tracked_units.insert(guid);
|
||||
} else {
|
||||
model.tracked_units.remove(&guid);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
}
|
||||
|
||||
// CanLootUnit("unit"): whether that unit's corpse holds loot for us. Needs no new feed —
|
||||
// [`UnitState`] already carries `dynamic_flags` raw and `dead`, so this is the same two-part
|
||||
// test the loot sparkle makes on the engine side (`creature_anim/spell_visual.rs`'s
|
||||
@@ -516,6 +555,16 @@ impl crate::script::UiScript {
|
||||
pub fn take_combat_log_adds(&mut self) -> Vec<String> {
|
||||
std::mem::take(&mut self.model_mut().combat_log_adds)
|
||||
}
|
||||
|
||||
/// The `TrackUnit` set as a sorted slice of guids, for the minimap's blip pass; empty when
|
||||
/// nothing is tracked, which is the usual case and allocates nothing.
|
||||
///
|
||||
/// A copy rather than a borrow because the caller holds the VM through a `NonSendMut` it then
|
||||
/// spends elsewhere, and because the set is at most the player's own raid — forty-odd `u64`s.
|
||||
/// Sorted, so the blip pass binary-searches it instead of keeping an index of its own.
|
||||
pub fn tracked_units(&self) -> Vec<u64> {
|
||||
self.model_mut().tracked_units.iter().copied().collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -764,6 +813,48 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// `TrackUnit`/`UntrackUnit` keep a set of **guids**, so a tracked raid member keeps its dot
|
||||
/// while the roster shuffles its token; and a token naming nothing is ignored, not raised,
|
||||
/// because pfUI calls these on units that may have just left the group.
|
||||
#[test]
|
||||
fn track_unit_keeps_a_sorted_guid_set_and_untrack_removes_from_it() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
let unit = |guid: u64| {
|
||||
Some(UnitState {
|
||||
exists: true,
|
||||
has_object: true,
|
||||
guid,
|
||||
..Default::default()
|
||||
})
|
||||
};
|
||||
s.set_unit("raid1", unit(0x30));
|
||||
s.set_unit("raid2", unit(0x10));
|
||||
s.set_unit("raid3", unit(0x20));
|
||||
assert!(s.tracked_units().is_empty());
|
||||
|
||||
s.run(
|
||||
r#"TrackUnit("raid1") TrackUnit("raid2") TrackUnit("raid3")
|
||||
TrackUnit("raid2")
|
||||
TrackUnit("raid9") TrackUnit()"#,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
s.tracked_units(),
|
||||
vec![0x10, 0x20, 0x30],
|
||||
"sorted, deduplicated, and a token naming nothing added nothing"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "handler errors: {:?}", s.errors());
|
||||
|
||||
s.run(r#"UntrackUnit("raid3") UntrackUnit("raid9") UntrackUnit()"#)
|
||||
.unwrap();
|
||||
assert_eq!(s.tracked_units(), vec![0x10, 0x30]);
|
||||
assert!(
|
||||
s.tracked_units().windows(2).all(|w| w[0] < w[1]),
|
||||
"the blip pass binary-searches this, so the order is load-bearing"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "handler errors: {:?}", s.errors());
|
||||
}
|
||||
|
||||
/// `CanLootUnit("unit")` is the loot sparkle's own two-part test read through the token map:
|
||||
/// dead, and `UNIT_DYNAMIC_FLAGS` bit `0x1`. A unit with no descriptor answers nil, not 0.
|
||||
#[test]
|
||||
|
||||
@@ -796,6 +796,11 @@ pub(crate) struct Model {
|
||||
pub(crate) uninvite_requests: Vec<String>,
|
||||
/// `ConsoleExec` lines that were not CVar writes.
|
||||
pub(crate) console_lines: Vec<String>,
|
||||
/// SuperWoW's `TrackUnit`/`UntrackUnit` set, by guid: the units the minimap draws a tracking
|
||||
/// dot for on top of whatever the player's own tracking spell finds
|
||||
/// (`benilla-app`'s `minimap::blips`). Sorted, so the app can binary-search it per candidate
|
||||
/// without building an index of its own.
|
||||
pub(crate) tracked_units: std::collections::BTreeSet<u64>,
|
||||
/// SuperWoW's `CombatLogAdd` lines, queued for the app to append to `WoWCombatLog.txt`
|
||||
/// (`compat/superwow.rs`). Not chat: nothing is printed to a window.
|
||||
pub(crate) combat_log_adds: Vec<String>,
|
||||
@@ -1950,6 +1955,7 @@ impl Model {
|
||||
roll_requests: Vec::new(),
|
||||
uninvite_requests: Vec::new(),
|
||||
console_lines: Vec::new(),
|
||||
tracked_units: std::collections::BTreeSet::new(),
|
||||
combat_log_adds: Vec::new(),
|
||||
logging_chat: false,
|
||||
logging_combat: false,
|
||||
|
||||
@@ -284,6 +284,12 @@ fn allowed_beyond_1_12() -> HashSet<&'static str> {
|
||||
"CanLootUnit",
|
||||
"GetSpeed",
|
||||
"UnitNameplate",
|
||||
// SuperWoW's `TrackUnit`/`UntrackUnit` (script/compat/superwow.rs): a minimap tracking dot
|
||||
// on a named unit. 1.12 draws party members and raid members nowhere, and has no verb for
|
||||
// tracking anything by hand — the dots it draws come from a tracking spell alone. pfUI
|
||||
// puts the whole raid on the minimap with these.
|
||||
"TrackUnit",
|
||||
"UntrackUnit",
|
||||
// SuperWoW's `CombatLogAdd(text)` (script/compat/superwow.rs): one line appended to
|
||||
// `WoWCombatLog.txt`, which 1.12 writes C-side off its own chat router and gives Lua no
|
||||
// way into. It is also a detection name in its own right — BigWigs' Spell Requests gates
|
||||
|
||||
Reference in New Issue
Block a user