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:
@@ -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>| {
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user