the Lightwell stands up, and stealth openers start your swing

- the Lightwell has its shaft: a billboard card kept only the x of its bone's scale, so a bone animated non-uniformly (the well's 8.8 × 8.8 × 37.6 light column) collapsed into a squat blob drowning the bowl — 32 models and 49 bones in the corpus were losing their stretch the same way, mage Teleport's six bones at ×11 among them; `bbscan` grew the column that finds them
- Backstab, Garrote, Ambush, Cheap Shot, Shred, Ravage, Pounce and one Judgement start your auto-attack on the spell's hit, as the reference does — the "no spell carries that bit" the deferral rested on was a proof by absence over ten rows; the census now covers the shipped file's 36
- Quiver's range rung reads right: the interact-distance map held players only, so a mob "target" answered a constant at every distance; it holds every unit now, with the inspect refusal riding the entry
- the craft and tradeskill reagent slot is the reference's QuestItemTemplate (147×41, the name plate, the count on the icon) — its base template lives in the Blizzard addons, not FrameXML, which is why the first cut was hand-rolled

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Sam
2026-08-25 18:47:48 +07:00
co-authored by Claude Fable 5
parent 343e46fdde
commit eaabe372f7
20 changed files with 1175 additions and 248 deletions
+47 -15
View File
@@ -24,7 +24,10 @@
//! family is already unit-keyed; `super::char_stats::player_inv_slot` routes `"player"` to the
//! self feed and the inspected token here.
//! - **Range:** the two verified d² predicates, `CanInspect` and `CheckInteractDistance`, over one
//! app-fed per-token distance map ([`UiScript::set_inspect_reach`]) — the VM holds no positions.
//! app-fed per-token distance map ([`UiScript::set_unit_reach`]) — the VM holds no positions. The
//! map itself is unit-general (every held unit, creature included) and is fed by
//! `benilla::ui_unit`; only `CanInspect`'s players-only leg is inspect's, and it rides in the
//! entry.
//!
//! The view is keyed by **unit token**, not guid — the ref stores `InspectFrame.unit` as a token
//! and re-reads it on `PLAYER_TARGET_CHANGED`, so an inspect window follows a re-target exactly as
@@ -52,8 +55,8 @@ pub const INTERACT_DIST_SQ: [f64; 4] = [100.0, 123.45678, 100.0, 900.0];
/// as `INSPECT_DISTANCE` (`ObjectDefines.h:26`), so client and server agree exactly.
pub const CAN_INSPECT_DIST_SQ: f64 = 100.0;
/// What the app resolved about one popup token's unit this frame — the input both range predicates
/// read ([`UiScript::set_inspect_reach`]).
/// What the app resolved about one unit token's unit this frame — the input both range predicates
/// read ([`UiScript::set_unit_reach`]).
///
/// An entry exists **iff the token resolved to a live unit object**, which is the distinction the
/// reference's own bindings turn on: `0x515940` maps the token to a GUID and then asks the object
@@ -68,6 +71,11 @@ pub struct UnitReach {
/// (`sObjectMgr.GetPlayer` and `!IsValidAttackTarget`, `MiscHandler.cpp:945-956`) — consulted
/// by `CanInspect` alone. `CheckInteractDistance` is a pure distance test in the binary and
/// must not read this, or following an enemy player would gray a row the reference leaves live.
///
/// `sObjectMgr.GetPlayer` is the whole is-it-a-player leg, and it lives **here** rather than in
/// the map's membership: the map is fed for every held unit, creature included (B304), so a
/// boar 3 yards away is `inspectable: false` while still answering `CheckInteractDistance`
/// truthfully.
pub inspectable: bool,
}
@@ -86,15 +94,25 @@ pub struct InspectView {
}
impl super::UiScript {
/// Push the per-token **inspect reach** map: unit token → squared distance from the player to
/// that unit, for every popup token the app could resolve to a live player this frame.
/// Push the per-token **unit reach** map: unit token → squared distance from the player to
/// that unit, for **every** token the app resolved to a live unit object this frame. Nothing
/// to do with `UNIT_FIELD_COMBATREACH` — this is where each token's unit *is*, not how far it
/// swings.
///
/// One app-fed number serves both range predicates, because the binary uses the same d² for
/// both: `CanInspect` compares it against `100.0`, and `CheckInteractDistance(unit, type)`
/// against [`INTERACT_DIST_SQ`]`[type-1]`. It lives here rather than on
/// [`super::UnitState`] because the popup rows need it for **party** tokens too, and those have
/// no `UnitState` (only `"player"`/`"target"`/`"mouseover"` are fed) — keying it off the unit
/// snapshot would have left the party menu's rows permanently dead.
/// against [`INTERACT_DIST_SQ`]`[type-1]`.
///
/// **The map is unit-general, not a players-only or popup-only one.** It was both, and being
/// both was report B304: `CheckInteractDistance` is a pure distance test over any unit token in
/// the binary, so a creature `"target"` that never entered the map made Quiver's 30-yard rung
/// answer a constant for every mob at every distance. The one typemask that *is* real —
/// inspect's — rides in the entry as [`UnitReach::inspectable`], never at the token.
///
/// It lives here rather than on [`super::UnitState`] because a distance changes **every
/// frame**: the unit snapshots are pushed on diff (1439) and fire `UNIT_*` transitions when
/// they move, so folding a moving number into them would re-push and re-fire every token every
/// frame. This map is re-pushed wholesale instead, and nothing keys an event off it.
///
/// A token absent from the map is a token the object manager holds **no unit for**, and both
/// predicates answer `nil` there — the reference's own null-object arm (wow-re
@@ -102,8 +120,11 @@ impl super::UiScript {
/// `lua_pushnil`). It used to read as *in range*, on the reasoning that missing data must not
/// gray a row; the binary says the opposite, and that default was report B316 — every distance
/// row lit up for exactly the party member who was too far away to have an object at all.
pub fn set_inspect_reach(&mut self, reach: HashMap<String, UnitReach>) {
self.model_mut().inspect_reach = reach;
///
/// **Keys are lowercase** — the resolver folds case (`_strnicmp`, 1247), so the lookup does
/// too, exactly as `Model::unit` does for the snapshots.
pub fn set_unit_reach(&mut self, reach: HashMap<String, UnitReach>) {
self.model_mut().unit_reach = reach;
}
/// Push (or clear, with `None`) the inspected unit's resolved equipment view. The app calls
@@ -135,10 +156,19 @@ impl super::UiScript {
/// What the app resolved for `token`, or `None` when it resolved to no live unit at all — the
/// reference's NULL-object arm, which both predicates answer `nil` to.
///
/// Case-folded on the way in, the same shape `Model::unit` uses for the snapshot map: both
/// predicates reach the unit through the one resolver `0x515970`, whose nine compares are
/// `_strnicmp` (1247), so `CheckInteractDistance("Target", 4)` is the same question as
/// `CheckInteractDistance("target", 4)`.
fn reach(lua: &Lua, token: &Option<String>) -> Option<UnitReach> {
let token = token.as_deref()?;
let model = lua.app_data_ref::<Model>().expect("model app_data");
model.inspect_reach.get(token).copied()
if token.bytes().any(|b| b.is_ascii_uppercase()) {
model.unit_reach.get(&token.to_ascii_lowercase()).copied()
} else {
model.unit_reach.get(token).copied()
}
}
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
@@ -150,9 +180,11 @@ pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
// `threshold = DAT_00b4d918 = 100.0` (10 yards, the same number vmangos enforces as
// `INSPECT_DISTANCE`). The operator is theirs too: `test ah,0x41; jne` skips the out-of-range
// action on `C0|C3` — Less, Equal, *or unordered* — so the three in-range arms below are that
// mask spelled out, and a NaN distance reads as in RANGE, not out. Only the app-fed distance is
// consulted here; the is-a-player and not-attackable legs are folded in app-side, since a token
// that fails them is never entered in the reach map at all. Decision 0631.
// mask spelled out, and a NaN distance reads as in RANGE, not out. The is-a-player and
// not-attackable legs are resolved app-side and ride IN the entry
// ([`UnitReach::inspectable`]) — not by keeping the token out of the map, which is the map's
// own law (a creature `"target"` is in it, at its real distance, and is not inspectable).
// Decision 0631.
g.set(
"CanInspect",
lua.create_function(|lua, unit: Option<String>| {
+7 -6
View File
@@ -1157,11 +1157,12 @@ pub(crate) struct Model {
pub(crate) dressup_intents: Vec<super::dressup::DressUpIntent>,
/// The dressing-room pane's bake yaw, the fourth of those scalars.
pub(crate) dressup_yaw: f32,
/// Unit token → what the app resolved about it this frame, for every popup token that named a
/// **live unit object** — the input to both verified range predicates (`CanInspect`,
/// `CheckInteractDistance`). An absent token is one the object manager holds nothing for, and
/// both answer `nil` there (see [`UiScript::set_inspect_reach`] and [`super::UnitReach`]).
pub(crate) inspect_reach: HashMap<String, super::UnitReach>,
/// Unit token (lowercase) → what the app resolved about it this frame, for **every** token
/// that named a **live unit object**, creature as readily as player — the input to both
/// verified range predicates (`CanInspect`, `CheckInteractDistance`). An absent token is one
/// the object manager holds nothing for, and both answer `nil` there (see
/// [`UiScript::set_unit_reach`] and [`super::UnitReach`]).
pub(crate) unit_reach: HashMap<String, super::UnitReach>,
/// The skills-pane snapshot the app pushes ([`skills::SkillsState`], decision 0437 phase 4) and
/// the synthesized display tree built from it ([`skills::UiScript::set_skills`]) — the skills
@@ -1615,7 +1616,7 @@ impl Model {
pet_paperdoll_yaw: 0.0,
dressup_intents: Vec::new(),
dressup_yaw: 0.0,
inspect_reach: HashMap::new(),
unit_reach: HashMap::new(),
chat_input: Vec::new(),
skills: skills::SkillsState::default(),
skills_groups: Vec::new(),