- `TryOn` looks its item up when the verb is applied, in call order with `Undress` and `Dress`: a cached record lands there, as `0x504540` sets the components inside the call (`0x5045dd`-`0x5045ee`), and only a missing one waits for the callback `0x504ba0`. So `TryOn` then `Undress` in one chunk shows nothing.
- `Dress` keeps a try-on still waiting for its record: the rebuild (`0x504cd0` → `0x505b50`) cancels no callback.
- `GetGMTicket` and `DeleteGMTicket` send nothing without an active player (`0x48c9bd`, `0x48cb8d`).
- More test fixtures build the client without an `Update` pass: about 25% less CPU for the `benilla-app` suite.
Each fix has a test that fails without it.
Fixes#2329Fixes#2330Fixes#2328🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Over open ground the classifier's no-cursor exits clear to the base mode (`0x523d30`), so the repair and wrap cursors stay.
- While targeting, hover and click skip `IsSelectable` (`0x4828f4`, `0x4935d5`). An object is picked only for a word in `0x4800` that the lock test passes (`0x6e62e0`, `0x5f8260`), keys (effect 59) included.
- Object use refuses dead, locked out, mounted or shapeshifted, in combat and immune in `0x5f3130`'s order, with columns read by the object's own type. An auto-closing door at rest opposite its start says the object is in motion (`0x5f42f0`).
- `SMSG_GAMEOBJECT_PAGETEXT` opens the reader on the object's page (`0x5f88c0`), with a byte-exact golden.
- The interior trace keeps the nearest hit across every building (`0x6a8840`), with the portal tie as a fraction of the ray (`0x6be421`).
Each fix has a test that fails without it.
Fixes#436Fixes#680Fixes#677Fixes#1710Fixes#1711Fixes#91Fixes#2146🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
About 35 Lua verbs queued their sends and events for a drain after the chunk; 1.12.1 acts inside the call.
- The NPC window closes (bank, trainer, auction, gossip, mail, merchant, stable, trade, taxi, trade skill, craft, quest) run their cores in the call, and fire CLOSED twice while the window held the latch (`0x493310`).
- Battleground, death, duel, loot roll, binder, summon, instance and PvP verbs send and signal in the call, with their gates: `RepopMe` only dead or a ghost (`0x605f30`), `AcceptXPLoss` in the healer's range (`0x5df7c1`), `PurchaseSlot`'s money and count (`0x4f86b0`).
- `AcceptAreaSpiritHeal` and the time packet rescan before acting (`0x4923b0`).
- The positions getters read the last packet with no slot gate (`0x4abf09`, `0x4ac1c6`).
- A unit is dead at health 0 or below (`0x605f3b`).
Each system has a test mixing its verb with `RandomRoll`, which fails without the change.
Part of #2202Fixes#2321🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `Undress` empties both hands (`0x504490`). A tried-on one-hander takes the off hand only with Dual Wield (`0x504930`, `0x5eab70`). A try-on with no display does nothing, and an unequippable one raises `ERR_NOT_EQUIPPABLE` (`0x50458b`, `0x504b2d`).
- The coin row puts each denomination on its own line (`0x4c2550`).
- The talent tooltip reads all three prerequisite slots, then the tier, then the spell, with the words read at the call (`0x52b390`).
- An empty ticket shows `ERR_TICKET_NO_TEXT` (`0x5ef802`).
- A meeting stone names its area and level range (`0x5f6b10`).
- A reload keeps the login's combat text font (`0x401620`).
- A duration that arrives before its aura joins it (`0x4e4450`).
Each fix has a test that fails without it.
Fixes#53Fixes#198Fixes#54Fixes#57Fixes#142Fixes#1495Fixes#190Fixes#757🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spell text, per-level terms, cast gates, ranges and travelling missiles answered or drew differently from 1.12.1.
- `$c` and `$p` expand (`0x6e31b0`, `0x6e3300`). A weapon spell's level reads the wielded weapon's skill (`0x5ea5d0`), and skill lines are indexed by race and class (`0x6de040`).
- The validator refuses a known learn, a pet's unlearnable spell and a second pet locally (`0x609854`, `0x609fd7`).
- A ranged spell's max range takes the ranged-slot item's scale (`0x6e36aa`).
- The launcher flies a ground cast as one missile at its point, and spawns none without a missile visual or ammo (`0x6e8a50`, `0x6e8a7f`). A reflected missile turns back at its caster (`0x61e17b`). Launch timing and the victim clips follow `0x61cfda` and `0x61d784`.
Tests fail on main, and the GO decode has a golden.
Fixes#2042Fixes#2049Fixes#1202Fixes#683Fixes#2069Fixes#2070Fixes#62🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Camera, swim-pitch, sound and name-check verbs were inert or answered differently from 1.12.1.
- `MoveView*` drive the camera's keyboard axes, and `ToggleMouseMove` toggles mouselook (`0x50fd40`, `0x50f910`).
- `CameraOrSelectOrMoveStop(1)` keeps the camera where it was left (`0x50f940`), and a customised view arms no auto-return (`0x50fc30`).
- The pitch keys pitch a swimmer and send the pitch moves (`0x515010`), and mouselook sends `MSG_MOVE_SET_PITCH` and `MSG_MOVE_SET_FACING` under the 0.1 rad throttle (`0x60e1ea`).
- `PlaySound` finds type-2 rows only (`0x45c306`). `PlaySoundFile` plays on bus 3, two at a time.
- Server-pushed music has its own slot (`0x460520`), and a repeat of a duplicate-flagged track is refused at its open (`0x7a66a0`). Zone silences use `0x460100`'s fixed point.
- Every VM checks names against the client's word lists (`0x402b7f`).
Tests fail on main, and the pitch body has a golden.
Fixes#1190Fixes#1642Fixes#1651Fixes#1615Fixes#1617Fixes#49Fixes#2116Fixes#1694Fixes#1689Fixes#1139🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1 a Lua verb sends, changes state and signals before it returns. Here, the session, tutorial and window-close verbs queued their work for a drain later in the frame.
- `Logout`, `Quit`, `CancelLogout`, `ForceLogout` and `ReloadUI` act in the call (`0x5ab000`), as do the tutorial writers (`0x4b59dc`) and the registrar and tabard closes (`0x4f5010`, `0x4f58a0`). Each system's calls go through one host variant and applier.
- `AddFriend` flags its tutorial before `CMSG_ADD_FRIEND` (`0x5ae677`).
- `LOGOUT_CANCEL` fires only while a logout is pending: on the ack, a world load, a completed logout and a refused login (`0x5aae70`).
- A logout runs the leave-world sweep, which had needed the avatar it had just despawned (`0x5dd543`). The logout clears follow `PLAYER_LOGOUT` (`0x490c2a`).
- Two lanes with equal heads fire in lane order instead of stalling the drain.
Tests fail on main.
Part of #2202Fixes#2267Fixes#2138Fixes#2297Fixes#287Fixes#297🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The profession, craft and loot windows, and two death timers, disagreed with 1.12.1 across a `/reload`, a group header and a busy frame.
- A `/reload` keeps the profession book open and unshown (`0x4fc750`), and its record waits outlive the VM. Clicking an open profession closes it, as the toggle `0x4fc910` does.
- The world-enter cascade, and so a reload or a worldport, closes the craft window silently (`0x4f5ee0`).
- The craft list heads each group once there are two (`0x4f60c0`). It sorts them with the comparators' header skip over the CRT qsort, answers `"header"` and folds.
- A replaced loot session's steps run on their own latch, stash and copier wait. An inventory failure re-signals `LOOT_OPENED` under the auto-loot byte (`0x5e3aff`).
- The release and corpse timers answer whole seconds (`0x48b5ae`, `0x48b5f0`).
Tests fail on main.
Fixes#2295Fixes#2296Fixes#2291Fixes#2210Fixes#2256Fixes#1530🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Several unit, aura and world-entry answers came from the wrong source or at the wrong moment.
- `"pet"` is the player's charm, else summon (`0x515a5e`), not the pet bar. The pet's buffs go through `UnitBuff`'s unit gate (`0x5195aa`), and other units' auras through `RequiredAuraVision` (`0x519860`).
- `SetPetGuid` signals in place, in its order (`0x4bc84f`-`0x4bc90c`). World entry clears the pet guid and asks for pet info (`0x4bc6f0`, opcode 0x279).
- World entry always signals `UPDATE_BONUS_ACTIONBAR` (`0x4e5018`). `PLAYER_LEVEL_UP` and the level tutorials fire from `SMSG_LEVELUP_INFO` (`0x5e413e`).
- `GetZonePVPInfo` answers at the call (`0x48d540`), and the zone commit waits for our player (`0x5db95d`). After a live worldport into Stormwind, main answered nil inside `ZONE_CHANGED_NEW_AREA`; this answers friendly.
Tests fail on main. The 0x279 golden is byte-exact.
Fixes#2252Fixes#261Fixes#262Fixes#2228Fixes#1158Fixes#2293🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1, attack, selection and death run their steps and signal their events in place. benilla queued several of them for later in the frame.
- The right-click select, StartAttack's own select (`0x5eccaa`) and the engine's selection writers now signal in place. A teardown reports its StopAttack before `PET_BAR_UPDATE` (`0x493a08`).
- StartAttack sets the attack target locally at its send (`0x5ecd51` → `0x47bf70`). A seated player stands before the swing (`0x5ecc9b`).
- A right-click that falls out of the attack arm greets (`0x60c257` → `0x60c270`).
- Your own death runs `0x5e99b0`'s steps in order, inside its update packet, then signals `PLAYER_DEAD` (`0x6058ae`).
- Every Lua verb's refusal shows inside its call.
Tests go through the packets and the calls, and fail on main.
Part of #2271Fixes#2287Fixes#2268Fixes#2114Fixes#2009Fixes#2207Fixes#2289🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The cursor, equip, container and vendor verbs gated, waited and sent differently from 1.12.1.
- A swap that asks the bind-on-equip question keeps the item held, and asks inside the call (`0x5e0c40`). An uncached record parks the equip and replays it when the record arrives (`0x5e0dbe`, `0x5e17a0`, `0x5e1020`). `USE_BIND_CONFIRM` fires inside the use (`0x5d9222`).
- A split stack is equipped or swapped whole, since `0x5e1480` and `0x4c7300` never read the split count.
- An enchant on an uncached item gates on zeroed fields (`0x495d60`).
- Bank slots read empty to the container and inventory verbs while no banker is open (`0x6228a0`).
- A vendor row dropped on a bank bag buys into it (`0x4f9f32`), and `CMSG_BUY_ITEM` ends with 1 (`0x4fb933`).
Tests go through the verbs and fail on main. The vendor golden is byte-exact.
Fixes#2249Fixes#2251Fixes#2250Fixes#2246Fixes#2248Fixes#2247Fixes#2227🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trade skill, craft and reputation verbs answered from benilla's own view of the lists, not 1.12.1's.
- Beast Training keeps a spell with no ability row, and its rows read none or used (`0x60c740`).
- `GetCraftInfo` answers the training-point cost and level, `GetAdjustedSkillPoints` the free profession count, and `GetCraftDisplaySkillLine` answers only for Enchanting (`0x4f6ce3`).
- Trade skill and reputation indices cover the full list in learn order or adder order, sorted as the CRT qsort sorts it (`0x73f727`).
- `TRADE_SKILL_UPDATE` and `CRAFT_UPDATE` fire per learn, unlearn, pet-spell packet and waited record, at the packet's place (`0x5e9e12`, `0x5ea240`, `0x4bdafa`). A pending product holds the sort and the update (`0x4fcf34`).
- Reputation messages add and sort at their handlers' places.
Tests pin each order and trigger, and fail on main.
Fixes#1264Fixes#1265Fixes#1266Fixes#1276Fixes#2128Fixes#2129Fixes#2255Fixes#2237🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1 an event's listener reads state as its own packet left it: each packet's handler writes and then signals in place. benilla read party, raid and pet units, the by-guid chain ends, the group records and auras as the whole frame left them. So an earlier listener could see a later packet's member, pet or aura.
The unit views lane now rewinds every unit a view can read, and every unit those name during the frame, to each entry's own packet, then replays. `PLAYER_AURAS_CHANGED` fires from the aura watcher's callback (`0x4e4140` → `0x4e4170`), not a frame diff. `PARTY_MEMBER_ENABLE`/`DISABLE` fire at the member object's create (`0x4e86b6`) and release (`0x5e9b78`), and no longer at a worldport, which frees each member without a release (`0x467800`). A frame-final read in a lane now fails a guard test.
Tests go through the dispatch and fail on main.
Part of #2217Fixes#2280🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
More of 1.12.1's signal points fire their events in place, before the packet they precede:
- TryCast's pick (`0x6e4efb`, before `0x6e585a`), and the attack validator's pick, split from its re-read (`0x61305d`-`0x6130c1`), for Attack, the pet bar's Attack (`0x4bd40d`-`0x4bd44c`, possessed units included) and a unit caster;
- the world click, the plate click, a death and a despawn's selection change;
- a deselect outside a call fires `PET_BAR_UPDATE` from the stop (`0x4bd6b4`) before `CMSG_SET_SELECTION`;
- the engine's target writers test the old selection's latches and send ATTACKSTOP as `0x493a08` does;
- chat, channel-join, emote and party refusals show inside their calls (`0x49b9c5`, `0x48a485`);
- the dev capture and probe Lua runs after its turn with the host lent, and the host refuses Lua in place behind a held signal.
Each has a test that fails without it.
Part of #2271Fixes#2272Fixes#2282Fixes#2266Fixes#2185🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A unit event's listener read the unit as the frame ended: two `UNIT_HEALTH` listeners for a 100→70→40 target both read 40.
In 1.12.1 a packet's handler writes every block (`0x4651a0`, `0x46520e`, `0x465222`), then calls its watchers synchronously (`0x465570`). `UnitHealth` and `UnitMana` read the live object (`0x517553`, `0x5176f7`). So a listener sees its own packet's state.
Each update packet now logs the dwords it moved, along with its destroys and teardowns. A "unit views" lane rewinds the player, target, targettarget and npc stores to frame start, then replays the log in stamp order between the other lanes' items. A debug check compares the rewind with a frame-start snapshot. `READS_FRAME_FINAL` lists every view push still reading frame-final state.
Tests: per-packet `UnitHealth`/`UnitMana`, `UNIT_COMBAT` ahead of its update, coalescing, an unlogged write, a worldport, and a stalled drain.
Part of #2217🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven window verbs read, keep and fire what 1.12.1 does:
- `QueryAuctionItems` takes its text through `lua_tostring`, `usable` as a number's `_ftol` sent as a byte, and quality -1 for a non-number (`0x4ce9cd`-`0x4cec32`).
- `GetInboxInvoiceInfo` needs the item entry (`0x4af3e8`), names an uncached item "" (`0x5d8b25`), and reads its body as `sscanf` with zeroed outputs and no return test (`0x4af440`-`0x4af471`).
- The who list reads names and guilds at the `0x30`/`0x60` caps and keeps a short answer (`0x5ae013`-`0x5ae0c1`).
- The gossip greeting expands once per message (`0x4e229a`).
- A trainer's headers are built per list (`0xb73a14`).
- The repair flag alone fires no `MERCHANT_UPDATE`.
- A staged guild rank survives a roster reload (`0x4d0a37`).
Each has a test that fails without its fix.
Fixes#2214Fixes#2220Fixes#2222Fixes#2262Fixes#2263Fixes#2264Fixes#2245🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Three fixes for timing and unusual input.
- **NPC windows.** Seven NPC windows could fire their show before the `npc` token was set: gossip, quest, bank, taxi, stable, tabard and registrar. In 1.12.1 the packet handler sets `[0xb4e2d0]` before the show (`0x4e1fa0` → `0x4930d0`, then `0x4e27df`), so a show never draws the book icon with an empty name. All eleven latched window feeds now run after the unit feed, and a schedule test names any that do not.
- **Faction slot.** The faction slot pick shifts a race or class byte as `dec ecx; shl` does (`0x4d5512`): 0 tests bit 31, 33 tests bit 0, and nothing panics.
- **Pet clock.** The pet bar and pet book read cooldowns at the frame clock, so their expiry tests step time instead of sleeping.
Three wrong comments are fixed.
Fixes#2278Fixes#2048Fixes#2275Fixes#1479Fixes#1227Fixes#2144🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
UI texture loading and widget method calls now follow 1.12.1.
- A UI texture's decoder is chosen by its extension (`0x449e16`), not by the file's content. A refused BLP draws green (`0x44a50e`). A TGA side below 8 fails (`0x58ab40`). A refused TGA minimap mask draws white.
- Portraits, `SetCursor` and drag icons open only `.blp` (`0x525e0f`, `0x449610`), and so do the tabard emblem cells (`0x50338a`).
- `SetPortraitToTexture` takes a Texture object and ignores a bad path (`0x48d780`).
- Every widget method checks its receiver first: a table, then an object, then the class. A bad receiver raises the reference's own lines. The 8 of 481 methods that skip it at the bytes are exempt. Texture arguments to the portrait and taxi globals, `AddFontStrings`' FontStrings and `SetBuybackItem`'s slot are checked the same way.
A test asserts that every reference method is wrapped.
Fixes#1685Fixes#2100Fixes#2099Fixes#2203Fixes#2125🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A verb's events used to fire only after its call returned. So a `PLAYER_TARGET_CHANGED` listener ran after the swing had gone out and the cast had resumed. In 1.12.1, `SignalEvent` (`0x703e50`) runs every listener before it returns.
`UiScript::signal_point` now lends the host back at the reference's signal points inside a call. Held items replay, and a listener's own calls nest. The selection verbs signal `PET_BAR_UPDATE` (`0x493a18`) before the send, and `PLAYER_TARGET_CHANGED` (`0x49389c`) before the re-swing.
StartAttack now selects its target (`0x5eccaa`) and stops again when its target test refuses (`0x5ecc37`). The switch latch tests the old selection (`0x49365f`-`0x4936a3`). A clear sends the selection as it stands (`0x493a49`). Nesting stops at 8 levels (`Deviation:`; the reference allows 200, `0x6f65ae`).
Tests pin the wire order, nesting, a panic, the stack and the census.
Part of #2271Fixes#2276Fixes#2277🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Combat log, monster chat and loot lines now follow 1.12.1's rules.
- An aura update writes every loss before any gain, with flags masked `& 0xe`. Stack lines print only for a `StackAmount` above 1.
- Dispel lines check only the bearer, which must be held and in range.
- A line whose item the server refuses is dropped.
- Melee `UNIT_COMBAT` carries the school of the swing's first sub-damage block.
- Monster lines expand against the addressee alone (`0x49daab`). `$R` and `$C` name a creature. A held unit whose name is missing reads `UNKNOWNOBJECT` (`0x609210`). At most 64 lines wait for a name, oldest dropped first (`Deviation:`).
- Loot-roll and item-received lines read their GlobalStrings templates, with the roll's property word, a signed count, and another member's push (`0x491d9f`).
Each rule has a test that failed before the fix.
Fixes#2075Fixes#2074Fixes#2092Fixes#1587Fixes#206Fixes#356Fixes#352Fixes#369🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The item Lua API answers links, suffixes, makers and repair costs as 1.12.1 does.
- `GetInventoryItemLink`, `GetContainerItemLink` and `GetTradePlayerItemLink` answer nothing for an empty slot, and a white, nameless link for an uncached item (`0x4c8c56`, `0x52ae00`).
- Inspected crafted gear shows its maker line, read from the slot's creator (`0x5333cd`, `0x52e1d5`).
- `GetItemInfo` names an item with its link's random suffix through `ITEM_SUFFIX_TEMPLATE`, and keeps signed link numbers (`0x5d8b00`, `0x842d4c`); every item name uses that one format.
- A gift-wrapped item hides the roll of the item inside and costs nothing to repair (`0x5d8bc9`, `0x5da387`).
Tests pin each address. Built on macOS.
Fixes#2174Fixes#2175Fixes#1543Fixes#282Fixes#1160🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Macros and the action bar's queries follow 1.12.1.
- Create, edit and delete fire `UPDATE_MACROS` and the edited slots' `ACTIONBAR_SLOT_CHANGED` in the reference's order, inside the call, and derive the bound spell there (`0x4f1322`, `0x4f1329`, `0x4f1313`).
- A macro's spell is re-derived only at load, arrival, create and edit (`0x4efe00`'s four callers).
- A rank supersede rewrites bar macros, and clears slots still on the old rank, items included (`0x4e5e60`, `0x5e6392`); an unlearn clears every slot on the spell (`0x4e5e20`).
- The macro file reads and writes as `0x4f0600` and its writer do: final newlines, `END`, quote-aware names, signed ids, 255-byte lines.
- Item slots answer range by their on-use spell; `ActionHasRange` tests "not below" 2^-22 (`0x4e5a50`, `0x8029d4`).
A line cut that would split a UTF-8 character is a recorded Deviation. Tests pin each address. Built on macOS.
Fixes#2115Fixes#2111Fixes#2107Fixes#2065Fixes#2062🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Targeting and the player's own unit follow 1.12.1.
- `TargetUnit` raises its usage error for a non-string and "Unknown unit name" for an unknown token (`0x4899d0`, `0x515970`).
- The selection call refreshes the usable state, then fires `PLAYER_TARGET_CHANGED`, inside the call (`0x493892`, `0x49389c`).
- A deselect stops the attack, then the pet's attack behind its gate, then sends (`0x493a08`-`0x493a2a`); `PET_BAR_UPDATE` fires in place.
- Your own death clears the target, closes loot, cancels the cast and auto-releases when flagged (`0x5e99b0`).
- Sitting and Z act on the active player under possession (`0x5ed430`, `0x5eb480`).
- `UnitIsPVPFreeForAll` reads the controlling player's flag (`0x516540`, `0x606170`).
- A race's side comes from ChrRaces and FactionTemplate (`0x5efe00`).
Tests pin each address. Built on macOS.
Fixes#2192Fixes#1233Fixes#2212Fixes#2136Fixes#2004Fixes#2259
Part of #2207🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The chat verbs follow 1.12.1, in its order (Part of #2202).
- `SendChatMessage` shows its away lines and sends the tutorial flag inside the call, ahead of the chat packet (`0x49a870`, `0x4b54c0`).
- A drunk player's line is slurred by `0x508680`'s rules.
- `SendAddonMessage` cuts prefix and message at their first NUL (`0x49f958`).
- The chat cache saves the confirmed custom slots at teardown (`0x499b90`).
- `/chatlist <channel>` prints the members by name once every name is known (`0x49c7e0`).
- `/played` prints once, through the stock frame (`0x401850`).
- `DoEmote` sends the stow, the stand change with its loot release and attack stop, then the emote, all in the call (`0x5ef560`, `0x6127b0`).
Tests pin each address. Built on macOS.
Fixes#2160Fixes#2158Fixes#2157Fixes#2206Fixes#226Fixes#194Fixes#2205🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One live reputation store: the wire, the pane's verbs and the reaction read and write the same slots, as the client's one array does (`0xb73290`).
- `FactionToggleAtWar` and the inactive verbs write the store inside their call, so `UnitReaction` and `UnitCanAttack` on the next line answer the new reaction (`0x4d5fd0`, `0x60640c`).
- `SMSG_SET_FACTION_ATWAR` copies the at-war bit (`0x4d56b0`). A standing message sets it below Unfriendly and clears it only on a rise (`0x4d5833`, `0x4d583f`).
- A rank change refreshes the player's reactions and sweeps the units, once per message (`0x4d5905`, `0x606f20`).
- `UPDATE_FACTION` fires per message, as the four handlers do.
- Logout zeroes all 64 slots (`0x4d541f`).
Tests pin each address. Built on macOS.
Fixes#2240Fixes#77Fixes#1410🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trainer window, its filters and the NPC service windows' lifecycle follow 1.12.1.
- `SetTrainerServiceTypeFilter` takes `"all"`, an exclusive third argument and its own errors; `GetTrainerServiceTypeFilter` matches case-blind (`0x4da260`, `0x4da510`).
- A mount or pet trainer always builds its known group first (`0x4d7609`); rows of state 3 and up show under no filter (`0x4da390`).
- An item record a getter waited on re-derives, re-sorts and fires, once per callback (`0x4d9140`, `0x4d7d40`).
- An NPC that loses a service flag closes that window (`0x60b420`).
- No window fires its event when the NPC's name arrives; `QUEST_ITEM_UPDATE` comes only from the item callback (`0x5018c0`).
Tests pin each address. Built on macOS.
Fixes#2241Fixes#2242Fixes#2243Fixes#2244Fixes#1494Fixes#203🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Piece 4 of the event order: the bag, lock, bank-slot and money events fire inside their watchers' runs, on the item lane, at their packet's stamp (Part of #2168).
- `BAG_UPDATE`, `BAG_CLOSED` and `PLAYERBANKSLOTS_CHANGED` come from the watchers (`0x4f9480`'s five callers, `0x4f8db0`, `0x4f90d0`, `0x4f91a0`, `0x5ddcf0`), not a diff.
- `PLAYERBANKSLOTS_CHANGED` has its two producers (`0x5ddcf0`, `0x4c7180`).
- The unlocks open each run, for the item now in the seat (`0x5ddd2f`).
- `PLAYER_MONEY` is a coinage notice (`0x5ddf4b`).
- Events stamped before the frame's first bag write read the bags as they stood.
- The guard now fails on a host-held queue a packet fills with no row.
Goldens pin each event against another packet's notice and line.
Fixes#2165🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The console commands and CVar behaviours 1.12.1 has, and Lua's local time.
- `date` and `time` use the local zone, `time{}` by `_mktime`'s sum (`0x740811`, `0x740836`); screenshots and the logon challenge's bias follow.
- In fullscreen `GetCVar("gxResolution")` answers the screen's own size; the windowed size is what is saved.
- The twelve world commands (`0x66f6c0`), with `showDetailDoodads` gating the clutter and `waterRipples` the wakes.
- `quit`, `ver`, `setmap`, `showplayer` (`0x6400e0`, `0x4049e0`) and `SkySunGlare` (`0x6d1dc0`).
- `ffx`, `ffxGlow`, `ffxDeath` and `ffxRectangle` print enabled or disabled (`0x6cde30`).
- `widescreen` 0 gives the four 4:3 modes and a 4:3 portrait aspect.
Tests pin each address. Built on macOS; Linux and Windows compiled.
Fixes#2231Fixes#2232Fixes#1628Fixes#1641Fixes#1667Fixes#2098Fixes#1687Fixes#1665
Part of #1577🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pet bar, pet unit, portrait, cooldowns and stable follow 1.12.1, and the pet and stable verbs send inside their call (Part of #2202).
- Pet cooldowns from `SMSG_PET_SPELLS` start back-dated through `0x6e2c60`, with only bit 31 masked (`0x4bdaab`).
- Feeding casts first and binds the food only on success (`0x6ea266`); `DropItemOnUnit` takes any token naming the pet (`0x48da6c`).
- `CastPetAction` presses empty slots, drops a held action and plays its sound (`0x4be330`).
- A pet with no object exists, unseen, with an empty portrait (`0x4e80cb`, `0x51a0ce`).
- `UNIT_COMBAT` fires once per token naming the unit (`0x494600`).
- A pet rename evicts the name cache by pet number (`0x604ac6`).
- `UnstablePet` and the stable drops send to the stored master, as `0x4caf20` does.
Tests pin each address. Built on macOS.
Fixes#2161Fixes#1598Fixes#298Fixes#180Fixes#2006Fixes#340Fixes#187Fixes#1414🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bag, cursor and paper-doll verbs follow 1.12.1 and send inside their call (Part of #2202).
- `EquipCursorItem` acts only on a held item (`0x489660`).
- The enchant pick exits silently on a miss and tests the charter flag first (`0x495d60`, `0x495e05`, `0x5da2e3`).
- `PutItemInBag` and `PutItemInBackpack` follow the bag worker: a vendor row buys into the bag, and a backpack item dropped on the backpack cancels (`0x4c7c00`).
- A world click drops a held vendor row or bar item on every leg (`0x492c90`).
- Bank bags are no bags to the container verbs until the bank opens (`0x4f93e0`).
- A bag item with template page text reads as readable (`0x5d9e10`).
A vendor row the list no longer holds, and the backpack's 0x71 bound (`0x4f990f`), are recorded Deviations. Tests pin each address. Built on macOS.
Fixes#2155Fixes#2154Fixes#2153Fixes#152Fixes#151Fixes#149Fixes#146🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The skill and reputation lists keep their folds and selection, and the talent, skill and reputation verbs act inside their call (Part of #2202).
- A standing change keeps the reputation folds: the mask `0x84a0a4` is keyed by header (`0x4d5b16`, `0x4d5bdc`).
- `SetSelectedFaction` and `GetFactionInfo` treat a header as a row with its own values (`0x4d6bec`, `0x4d654f`).
- `CollapseSkillHeader` and `ExpandSkillHeader` fold the row's group and fire `SKILL_LINES_CHANGED` (`0x4d3210`, `0x4d3270`, `0x4d2ed0`).
- The skill watchers rebuild the list on a max or bonus change and speak the skill-up line (`0x5dda6c`, `0x5de36a`).
- `GetTradeSkillLine` adds the permanent bonus to rank and max (`0x4fddf7`).
- `FactionToggleAtWar` writes the at-war bit into the reputation store, not only the pane's copy (`0x4d5fd0`).
The fold-by-position quirk is a recorded Deviation. Tests pin each address. Built on macOS.
Fixes#1238Fixes#1231Fixes#1230Fixes#274Fixes#272Fixes#271Fixes#288🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Item, item-push and loot events now fire in packet order, on the stamped drain of #2225 (Part of #2168).
- Each item watcher's callback fires whole at its packet, in its handler's order (`0x5d94e1`-`0x5d9519`, `0x5d85ce`-`0x5d8606`, `0x5d8798`-`0x5d87c1`).
- One record answer's callbacks run in the order they were asked (`0x5d8321`).
- The loot window's steps and the item push share one lane: a waiting copier ends at its record's answer (`0x4c2afc`), and a waiting push shows there (`0x491ee0`).
- Two responses in one frame give each window its own LOOT_OPENED and LOOT_CLOSED.
- The owed close of #2230 still settles before the window opens.
`BAG_UPDATE` and `ITEM_LOCK_CHANGED` remain in the guard, with their reason. Goldens pin each interleaving against another packet's update. Built on macOS.
Part of #2168🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guild roster, charters and tabard designer now follow 1.12.1, and their verbs act inside their call (Part of #2202).
- `SetGuildRosterShowOffline` compares the integer it is given (`0x4d0f70`), and re-sorts only on a change with members held. The flag and the selection outlive a `/reload`.
- The away, busy and GM tags, overhead and in `GetGuildRosterInfo`, read the `CHAT_FLAG_*` globals (`0x5eca17`, `0x4d13ed`).
- `BuyGuildCharter`, `RenamePetition` and `SortGuildRoster` raise their own usage lines.
- `RenamePetition` with no charter open does nothing (`0x4f495e`).
- `ClosePetition` fires `PETITION_CLOSED` while the record is still readable (`0x4f402c`).
Tests pin each address. Built on macOS.
Fixes#1437Fixes#371Fixes#2124Fixes#2123🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trainer, flight-master and merchant windows now follow 1.12.1, and their verbs act inside their call (Part of #2202).
- **`IsTalentTrainer`** answers 1 with no trainer open (`0x4d8ed0`).
- **The trade-skill and learn-spell queries** read each service spell's effects (`0x4d9f70`).
- **`GetTrainerServiceAbilityReq`** counts a known higher rank as met (`0x4d96e0`).
- **Trainer rows** sort as the client's one persistent list, with a case-sensitive compare. The order is pinned against the client's own sort, run in benilla-re's emulator.
- **`CloseTaxiMap` and `TakeTaxiNode`** act in the call. A map with fewer than two known nodes shows `ERR_TAXINOPATHS` and opens nothing (`0x5ecf39`).
- **Buying an item a quest needs** shows its progress line (`0x5dced0`).
- **The merchant's sale and drop verbs** send as `0x4fb760` does.
Tests pin each address. Built on macOS.
Fixes#1272Fixes#213Fixes#212Fixes#166Fixes#2148Fixes#2147Fixes#1804🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The quest windows, the quest log and gossip now follow 1.12.1, and their verbs act inside their call (Part of #2202).
- The quest log is ordered by the client's two `qsort` passes (`0x4de751`, `0x4dea63`), and collapse is keyed by group id.
- `GetAbandonQuestItems` lists the quest items an abandon destroys.
- `GetQuestLogRewardMoney` adds the level-60 money.
- `GetQuestReward` latches, so a double click sends one choice.
- An elite quest shows its tutorial.
- Rewards the player can't use read unusable, and an item of inventory type 0 reads quality -1.
- A coded gossip option asks for its code (`GOSSIP_ENTER_CODE`).
- A close drops the NPC inside its call (`0x501201`, `0x4e1fcc`).
- The gossip list reads at most 15 options (`0x4e2722`).
Tests pin each address, and a golden pins the code's bytes. Built on macOS.
Fixes#2194Fixes#1508Fixes#1507Fixes#386Fixes#385Fixes#29Fixes#1513🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In 1.12.1 each packet's handler runs whole, and its events fire inside it in arrival order (`0x5384d0`, `0x537aa0`). benilla fired each producer's queue in system order instead. This lays the foundation:
- One stamp clock orders every queued event.
- A single drain fires them by stamp, with nested items placed straight after their cause.
- Chat lines and field notices are its first two lanes.
- A line waiting on a player name, creature record or item fires at its answer's place (`0x49ccc0`, `0x6294b0`).
- A notice's own chat line fires `CHAT_MSG_SYSTEM` in place, as `DisplayError` does (`0x496720` → `0x49a870`).
A guard test lists every handler-filled queue not yet on a lane, so the rest of #2168 can follow. A depth cap on nesting is a written `Deviation:` until #2202 finishes. Goldens pin the order. Built on macOS.
Part of #2168Fixes#2173🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The trade window now follows 1.12.1, and the trade verbs act inside their call (Part of #2202).
- The open leg folds held coins only for a trade started by a drop (`0x4bf5f1`), with one `PLAYER_TRADE_MONEY`.
- Bag, equipped and trade-slot links go through one path (`0x52ae00`), with the enchant and suffix.
- `ClickTargetTradeButton(7)` with an enchant asks before replacing one (`TRADE_REPLACE_ENCHANT`), then casts at the partner's slot.
- Dropping or clicking an item onto a player starts or fills a trade (`0x48d960`, `0x4925d0`), and a refused placement changes nothing.
- The accept packet carries the partner's status word.
- The accept and un-accept signals fire before their packets.
- The money events fire where `0x4bf2b0` fires them.
Wire goldens pin the new bodies. Built on macOS.
Fixes#277Fixes#279Fixes#280Fixes#281Fixes#88Fixes#292🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The /who search, its list and its chat lines now follow 1.12.1, and the who and LFG verbs act inside their call (Part of #2202).
- The filter is built token by token, as `0x5aebb0` does. Tags match a case-insensitive substring of the localized zone, race and class names, the walk stops at the fourth term, and an unclosed quote drops its token.
- The sort order resets at each world entry (`0x5adc50`).
- An id with no row reads `UNKNOWN`, in the list and in chat.
- The answer keeps at most 50 entries (`0x5adf87`), and the count line gets the raw count.
- `GetWhoInfo`'s bounds and miss tuple match `0x5ad71a` and `0x5ad836`.
Three `Deviation:` lines name the memory and platform quirks not copied. Built on macOS.
Fixes#39Fixes#1329Fixes#1114Fixes#181Fixes#200🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The mailbox now follows 1.12.1's inbox and send code, and the mail verbs act inside their call (Part of #2202).
- Expired mail stays listed.
- Mail from an ignored player is dropped on type 0 only.
- Delete and return use the reference's tests, and the postage sum matches.
- Reply follows `0x4aefa5`.
- `GetInboxText` reads the mail's template id, and `CMSG_MAIL_CREATE_TEXT_ITEM` now carries it, pinned byte for byte.
- A row with no paper takes the first usable one, in `0x4ada90`'s order.
- The send slot refuses as `0x4adf38` does.
- The profanity filter masks subjects and bodies.
- The header icons and non-player sender names come from the bytes.
- Keeping the wire id where the reference crashes on a null paper is a written `Deviation:`.
Tests pin each address. Built on macOS.
Fixes#1241Fixes#1235Fixes#247Fixes#240Fixes#55Fixes#140🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The party and raid verbs now act inside their call, as 1.12.1's do (Part of #2202). Their gates and lines now match the reference:
- `ConfirmReadyCheck` sends nothing once the deadline has passed (`0x4bb2f0`), and sends its answer as one byte (`0x4bc0bf`).
- `DoReadyCheck` works for the leader only, refuses while a check runs, and shows its start line in place.
- A ready check survives a `/reload`. Only the leader's roster leg closes it (`0x4bac5a`-`0x4bacb4`).
- `UninviteByName` uses its usage line (`0x48a610`), and so do `InviteByName` and the promote verbs.
- Name lookups answer their waits in the order they were asked (`0x55f426`).
- A groupmate's death shows `ERR_PLAYER_DIED_S` (`0x4e7f70`).
- A `/reload` seeds the group without firing group events.
- An invite fires its event, then shows its line (`0x5e6747`, `0x5e6752`).
Tests pin each address. Built on macOS.
Fixes#2122Fixes#1436Fixes#2200Fixes#2183Fixes#2208Fixes#2022🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The friends and ignore lists went wrong:
- Names showed blank, online friends came unsorted, and the list announced itself early.
- Several status results printed the wrong line or kept the wrong rows.
- The ignore list had no 25-slot cap, and an ignored player's emote still printed.
The reference:
- The list handler (`0x5ae350`) names a slot only from the cache, and signals at a pending count of 0.
- The CRT `qsort` with comparators `0x5ae870` and `0x5ae940` sorts the lists.
- Statuses follow the table at `0x5ace1c`, and refused names send their delete (`0x5acec8`, `0x5acf8c`).
- `SMSG_TEXT_EMOTE` drops an ignored sender (`0x49dc3d`).
The friend and ignore verbs act inside their call, and listeners see the list as signalled. Each fact is tested against its address.
Part of #2202Fixes#133Fixes#137Fixes#138Fixes#1109Fixes#1112Fixes#1113Fixes#1668Fixes#1769Fixes#1328Fixes#765🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Auction commands went wrong:
- A bid or cancel was sent with no local check.
- Results showed the wrong line or none.
- Notices re-queried instead of editing the lists, and the list verbs sent unarmed.
- Outbid auctions never reached the Bids tab.
The reference:
- `PlaceAuctionBid` and `CancelAuction` refuse in the call (`0x4cf762`-`0x4cf8a8`, `0x4d0338`).
- The result table `0x4cc7b4` maps 1 through `0x622630`, 4 to `0x17` and 13 to `0x1be`, and 5 always shows `0x175`.
- Notices remove through `0x4cdfe0`, and patches signal one event each, in the handler.
- The one-shot flags `0xb7263c`/`0xb72640` gate the list verbs.
Every auction verb but the close now acts inside its call. Each fact is tested against its address.
Part of #2202Fixes#47Fixes#48Fixes#252Fixes#1525Fixes#1526Fixes#1527Fixes#1533Fixes#2195Fixes#2196Fixes#2197Fixes#2198🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a death or a stop:
- A cast at a unit that died ran on, and Auto Shot kept firing after its target's death.
- Stopping a repeat left its kit running and fired its events a frame late.
- An attack restarted within a round trip of a stop sent no swing.
- A possessed unit's area melee ordered no attack, and its ground cursor measured from you.
- A game object's go drew a caster's kit.
The reference:
- `OnDied` (`0x605860`) cancels a cast whose target dies (`0x6058e0`), then deselects and stops the repeat (`0x605901`, `0x605906`).
- `StopAutoRepeatSpell` (`0x6ea080`) reaps, pops and signals where the key clears.
- The stop word `+0xc54` lets a start swing again (`0x5eccda`).
- A go's caster legs need a unit (`0x6e7f8b`).
Each fact is tested against its address.
Fixes#2177Fixes#44Fixes#2187Fixes#2013Fixes#2140Fixes#2171Fixes#2172Fixes#2188🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Loot went wrong:
- Takes, closes and master gives queued, then ran in a fixed order.
- The last row closed a frame late.
- Auto-loot opened before it swept, read Shift when the loot arrived, and looted over an open window.
- `GetNumLootItems` counted taken rows, and a response for an object the client lacked opened a window.
The reference:
- `LootSlot` is the confirm form (`0x4c2e70`). A row click takes after its `<OnClick>` (`0x4c1867`). `CloseLoot` zeroes only the source (`0x4c1da0`).
- `SMSG_LOOT_REMOVED` closes an empty window in the packet (`0x4c1f8d`).
- The sweep runs before `LOOT_OPENED` (`0x4c1ed3`, `0x4c2240`). Each interaction stores the byte (`0x5df460`), and `CanLootNow` refuses an open session (`0x5ec1e1`).
- A missing object shows `ERR_LOOT_DIDNT_KILL` (`0x5ebb3f`).
Each fact is tested against its address, with the stock `LootFrame` for the replacing window.
Part of #2202Fixes#2167Fixes#2181Fixes#2180Fixes#2190Fixes#1228Fixes#784Fixes#2189Fixes#72🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chat channels went wrong:
- An eleventh join printed nothing, and only `YOU_LEFT` freed a slot.
- Lookups by number found suspended and pending channels.
- Admin verbs sent a number as the name.
- Defense broadcasts reached unconfirmed channels.
- Trade outside a city took "City", and zone channels answered full names.
- `/chatlist` printed a line of its own.
The reference:
- A join takes its slot when sent (`0x49edf3`), and refusals free it (`0x49c446`). A freed slot keeps its row id, so the walk skips it (`0x49bef0`).
- Number lookups take confirmed slots only. Admin verbs resolve the slot (`0x49e880`).
- The city word needs flag 0x20 and a city (`0x49a2d0`).
Channel verbs, `DoEmote`, `UninviteByName`, `SendAddonMessage` and `ListChannels` now act inside their call. Each fact is tested against its address.
Part of #2202Fixes#276Fixes#2169Fixes#229Fixes#2159Fixes#225Fixes#223Fixes#230Fixes#231🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Targeting went wrong for units the client does not hold:
- A groupmate out of range could not be targeted.
- Assist with nothing to assist was silent.
- `StartDuelUnit`, `SetRaidTarget` and `InviteToParty` ignored most tokens.
The reference:
- `SetSelection` (`0x493540`) stores a guid whose object is not held (`0x4935c8`), and `TargetUnit` selects a group member by guid (`0x489a64`).
- The roster tears that selection down for an offline seat (`0x4e845a`), a departed member (`0x5e6df7`) and a lost pet (`0x5e2719`).
- A guid-only select plays `igCharacterSelect` (`0x4936f7`). A long-range spell at it sends untargeted (`0x6e545b`).
- Assist shows `ERR_GENERIC_NO_TARGET` (`0x489c0e`), and the token verbs resolve any token (`0x515970`).
Every reader of the selection now reads the guid: the target frame, the cast binder, the swing, the plates and the portrait. Each fact is tested against its address.
Fixes#1965Fixes#1234Fixes#322Fixes#174Fixes#199Fixes#2178Fixes#2179🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Auction lists went wrong:
- They kept the server's order until a header click, and re-sorted every frame.
- Time Left, High Bidder, Lvl and Seller sorted on the wrong keys, and names matched case.
- Time left froze at arrival, and expired rows stayed.
- The sell slot took items 1.12.1 refuses.
The reference:
- World entry seeds eight-key sort stacks (`0x4cc236`). A page sorts once, when its last item record lands (`0x4cc9e8`).
- The comparator `0x4cd940` and the CRT `qsort` (`0x73f727`) order the rows, ties included.
- Time left counts down from the deadline, and rows 10 s past it leave the lists (`0x4cdfe0`).
- The sell slot refuses in `0x4ce362`'s order.
benilla now has one CRT `qsort`, which the frame batches, macros, guild roster and addon list share. Each fact is tested against its address.
Fixes#260Fixes#428Fixes#253Fixes#254Fixes#255Fixes#256Fixes#1539Fixes#258Fixes#1524Fixes#2191🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spell packets and cast state went wrong:
- A start or go with no record, or a non-unit's start, drew a cast.
- An enemy casting at you never became your target.
- Cooldown packets for other guids were dropped.
- Esc and move cancels reaped the wrong spell.
- Timers freed the cast slot.
- Spell state outlived a relog.
- Openers were scanned by id.
The reference:
- Both handlers exit first (`0x6e7876`, `0x6e7cd7`).
- A start aimed at you selects its caster (`0x6e799c`).
- Only the pet's guid routes to the pet (`0x6e9696`).
- The cancel reaps the record's spell (`0x6e4a8e`).
- No timer frees the slot; an inventory refusal cancels its item's cast (`0x5e3ab3`).
- World entry zeroes the spell state (`0x6e7150`).
- Openers keep the server's order (`0x5f83d0`).
Hold stops are now keyed by spell (`0x614167`). Each fact is tested against its address.
Fixes#2162Fixes#1302Fixes#1292Fixes#1301Fixes#2163Fixes#2025Fixes#1327Fixes#293🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A verb called from Lua, or a packet's event, applied a frame later, after the call or handler had returned. 1.12.1 applies each verb inside its call and fires each event at its packet.
What changed:
- Verbs apply inside the call: targeting, item picks, refused casts' log lines.
- A packet's chat items and field notices fire in wire order.
- Combat, unghost and death-tutorial events fire at their packets.
- Signals raised while something waits are replayed with the lock and cursor state they had.
- Loot picks and bind confirms are one call-ordered queue. `LOOT_BIND_CONFIRM` fires before its slot is stored (`0x4c290f`, `0x4c2917`).
- A forked script no longer panics on a lend, and two comments now match the reference.
Each order is tested against its address.
Fixes#1993Fixes#2045Fixes#2051Fixes#1419Fixes#2016Fixes#1987Fixes#1237Fixes#2139Fixes#797Fixes#2094🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The unit getters answered for the player alone:
- Stat functions gave zeros past player and pet.
- `GetGuildInfo` failed on chained tokens.
- A group member out of view had no race, class or sex.
- `UnitRangedDamage` ignored the weapon's school.
- `UnitIsUnit` resolved equal strings first.
- `UNIT_INVENTORY_CHANGED` fired only for the player.
- Drunk thresholds printed nothing and left `UnitLevel` alone.
- `CanInspect` answered nil.
The reference:
- `UnitIsUnit` compares the strings first.
- The getters read the named unit's fields and the name cache.
- The visible-item watch (`0x51bc3c`) signals for every player holding a token.
- The drunk watcher (`0x5e2a90`) prints and lowers the level.
What changed: each getter reads the unit its token names. The item watches, the ammo callback (`0x4c84f0`) and the inspect view signal where the reference does.
Each fact is tested against its address.
Fixes#1544Fixes#718Fixes#1239Fixes#1232Fixes#2005Fixes#169Fixes#1408Fixes#1157Fixes#1409🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>