superwow: Clickthrough is live — the last function on the DLL's manifest
It had been stored and read by nothing, which is "a knob with two positions and one picture". Now the world pick looks through a dead unit that holds no loot for us, which is what SuperAPI's own description asks for: "click through corpses to loot corpses underneath them" — the pile after an AoE pull, where the one body you want is behind four empty ones. The test is `CanLootUnit`'s own, negated (`clickthrough_skips`), so "lootless" means one thing to that verb, to this pick and to the loot sparkle; and because `UNIT_DYNFLAG_LOOTABLE` is per-viewer on the server, a corpse somebody else may loot is lootless TO US, which is exactly the pile the feature is for. A living unit is never skipped whatever its flags — that would make mobs unclickable — and a unit whose descriptor has not streamed is not known to be empty. The flag is mirrored out of the VM once a frame rather than read in the pick, which is a `SystemParam` with no reach into the script; a one-frame lag on a preference set once is immaterial. The resource is `Option` in the pick, like `factions` beside it: a harness that drives this pick builds the world it needs and nothing more, and absent means off, which is also the default. The stale aggregate row this closes is rewritten rather than deleted, because what it got wrong is the lesson: it called six functions "blocked — each needs new app-side state", and three of those blockers were answered by data the tree already had. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -34,7 +34,8 @@ nothing behind it.
|
||||
| `GetWeaponEnchantID` | **done** | the one thing `GetWeaponEnchantInfo` cannot say: the reference reads that very dword as its gate (`0x4c97f8`, nonzero, no DBC lookup) and then reports only that there *was* an enchant, so on the stock surface nothing can tell Windfury from Rockbiter or a shaman's imbue from a rogue's poison. The id was already in hand app-side and discarded — `ui_char.rs`'s `weapon_enchant` used it as a `?` gate — so this was one field on the snapshot. Two returns, main hand then off hand, which is the order its six-value sibling already answers in; the shape is a reading, since upstream publishes no usage string for it and nothing in the corpus calls it. A test asserts the two functions cannot disagree about *which* hands are enchanted |
|
||||
| `GetWeaponEnchantInfo([unit])`'s unit argument | todo, blocked | the extension that takes a **unit** still needs per-unit weapon-enchant tracking; today only `"player"`'s is fed at all |
|
||||
| `GetContainerItemInfo` charges (negative stack count) | todo, blocked | the raw signed field exists (`ObjectFields::item_spell_charges`, benilla-protocol), but nothing in `benilla-formats`' item template says *which* of an item's five spell-trigger slots (if any) is a consumable charge rather than an equip/proc effect — exposing it without that would be a guess, not a port |
|
||||
| New functions (`SpellInfo`, `TrackUnit`/`UntrackUnit`, `SetMouseoverUnit`, `Clickthrough`, `SetAutoloot`, `UnitNameplate`, `CanLootUnit`, Import/Export/AppendText, map/world coord conversions, `IsSwimming`/`IsMounted`/`isIndoors`/`GetSpeed`, mail/quest-link getters) | todo, blocked | each needs new app-side state, not a binding tweak: `IsSwimming`/`IsMounted`/`isIndoors`/`GetSpeed` need a self-movement snapshot (`move_flags() & SWIMMING`, `unit_mount_display_id()`, `area.rs`'s `indoor` bool, and a `MoveSpeeds` component — none currently reach a `feed_*` system that can push them, and `ui_unit.rs`'s `feed_units` is already at Bevy's 16-param ceiling); **`SpellInfo` is now done** — `compat/superwow.rs`, over `script/spellbook.rs`'s `spell_display`: the player's book first, then the host's `Spell.dbc` probe ([`SpellProbe`], installed by `ui_script::lifecycle::install_spell_probe` on every VM build), so it reaches every shipped spell as the real mod does. The range pair stays nil — `SpellRange.dbc` is not on the probe; the rest (`TrackUnit`, `SetMouseoverUnit`, `Clickthrough`, `SetAutoloot`, `CanLootUnit`, `UnitNameplate`, file I/O, map/world coords, quest links) are each plausible but unstarted. **`CombatLogAdd` is done** and has its own row below. **The two mail link getters are now done**, built for ClassicAPI's own `Mail` namespace (`compat/classic_api/merchant_frame.rs`, see its row below) — SuperWoW specifies the same `GetSendMailItemLink`/`GetInboxItemLink`, so that half of this row is closed |
|
||||
| New functions (`SpellInfo`, `TrackUnit`/`UntrackUnit`, `SetMouseoverUnit`, `Clickthrough`, `SetAutoloot`, `UnitNameplate`, `CanLootUnit`, `CombatLogAdd`, Import/Export/AppendText, map/world coord conversions, `IsSwimming`/`IsMounted`/`IsIndoors`/`GetSpeed`, mail and quest-link getters) | **superseded: every one of them is built** | this row used to read "todo, blocked — each needs new app-side state, not a binding tweak", and three of its four stated blockers turned out to be wrong. The self-movement snapshot it asked for **already existed** (`SelfMovement`, built for ClassicAPI's `State` namespace), and the three posture getters are SuperWoW's own spelling — `IsIndoors`, capital-I, not the lowercase `isIndoors` this row guessed at — so they were live before the row was written. `CanLootUnit` needed no feed at all: `UnitState` already carried `dead` and raw `dynamic_flags`. `UnitNameplate` was one line over a lookup ClassicAPI's `C_NamePlate` had already built. Only `GetSpeed` really did need a new field, and `net::current_speed` already computed the number. The row is kept rather than deleted because that pattern — a "needs a feed" verdict answered by data the tree already had — is the expensive mistake this file exists to record. Each name's own row, with what it is measured from, is in the DLL-manifest section below |
|
||||
| `Clickthrough([0|1])` | **done** | the world pick skips a dead unit holding no loot for us while this is set, so a click lands on whatever is behind it — SuperAPI's own description is "click through corpses to loot corpses underneath them", the pile after an AoE pull. The test is `CanLootUnit`'s own, negated (`hover.rs`'s `clickthrough_skips`), so "lootless" means one thing to that verb, to this pick and to the loot sparkle; and because the bit is per-viewer on the server, a corpse somebody else may loot is lootless *to us*, which is exactly the pile the feature is for. A **living** unit is never skipped whatever its flags — that would make mobs unclickable. The flag is mirrored out of the VM once a frame (`target::ClickthroughCorpses`) rather than read in the pick, which is a `SystemParam` with no reach into the script; a one-frame lag on a preference set once is immaterial |
|
||||
| `UnitPosition(unitid)`, `CursorPosition()` | done | `UnitState.position` (`script/unit/mod.rs`), fed by `Transform` from `ui_unit.rs`'s `feed_units` (player/target/targettarget/npc) and `ui_tooltip.rs`'s `drive_mouseover_tooltip` (mouseover); functions in `compat/superwow.rs`. Party/raid/pet aren't wired (their own `snapshot()` call sites in `ui_party/feed.rs`/`ui_pet/unit.rs` pass `None` — each needs its own `Entity`→`Transform` lookup threaded through, not attempted this pass). Reading `Transform` broadly past the usual one-frame-stale tolerance raised `game_plugins.rs`'s `UPDATE_ACTIONABLE_CEILING` by 30 (cited there) — `Transform` is written by enough movement/animation/physics systems that `With<Guid>` alone doesn't shrink the ambiguity count |
|
||||
| `UNIT_CASTEVENT` | done | `crates/benilla-app/src/superwow_events.rs`: a `CastEventFeed` queue the cast handlers push into, drained once per tick by one VM-holding system that fires the event — the pattern `ui_mirror.rs` already uses, and the only way across, since `spell/net.rs`'s `Lifecycle`/`Scene` bundles deliberately hold no `UiScript` (`game_plugins.rs`'s `every_vm_holder_in_update_declares_its_side_of_the_tick` gates that). Edges wired: `START` from `SMSG_SPELL_START` with its wire cast time as `arg5` (an instant raises none — its only edge is `CAST`, which is what the wiki's own START/CAST split means), `CAST` from `SMSG_SPELL_GO`, `FAIL` from `SMSG_SPELL_FAILED_OTHER`, `CHANNEL` from `MSG_CHANNEL_START` with its duration. GUIDs go out in the same 16-hex-digit spelling every other `compat/` surface uses, so `arg1`/`arg2` compare directly against a `UnitExists`/`UnitGUID` return. Not wired: `MAINHAND`/`OFFHAND` — benilla's melee-swing path raises no cast edge at all, so there is nothing to forward. `MSG_CHANNEL_START` names no caster (1.12 sends it for the local player's own channel only), so `CHANNEL`'s `arg1` is self and its `arg2` is 0 |
|
||||
| `RAW_COMBATLOG` | **done** | `ui_chat/frames.rs`'s `route` — the one funnel every combat line already passes on its way to a window and to `WoWCombatLog.txt`. Two arguments, as its one consumer in grom's corpus destructures them: the printed sentence, and the same sentence with each unit name spelled as its GUID (`PendingCombat::guid_fills`, composed a second time through the same `compose_line`, so the two can only ever differ in the name slots). Fired **before** the `CHAT_MSG_*`, which is what "raw" means upstream — it hooks the combat-log parse, ahead of the client's own formatting. Gated on `has_listener` — the gate nampower's combat-log family already uses — **or on SuperWoW's raw log being open**, since that file is the same `arg2` form written to disk; the composition is skipped under the same one test (`frames::wants_raw_form`), because a raid's log is tens of lines a second. A variant whose template says "you" keeps saying it, because that word is in the *template* and not in a fill — and its consumer depends on exactly that, reading the word and then asking `UnitExists("player")` for its own guid. **Why this row came first:** of SuperWoW's whole surface, it and `CombatLogAdd` are the only two names grom's addons call that this client lacked (`scripts/dll-surface.py --wanted`), and `SuperCleveRoidMacros/Utility.lua:10206` funnels **five** subsystems through this single event — immunity and resist learning, hidden-CC ("afflicted by") detection, debuff-duration learning off the GUID half, and the reactive-proc window that lights **Overpower** and **Revenge**. Its own comment there is why the last of those can come from nowhere else: "SPELL_GO only provides hit/miss COUNTS, not miss TYPES, so it cannot distinguish dodge (Overpower proc) from parry/block/resist". This does **not** by itself fix the Overpower complaint: that needs the dodged-*ability* sentence to be emitted at all, which is its own open item (`benilla-overpower-aura-spelldodged`) |
|
||||
|
||||
Reference in New Issue
Block a user