Commit Graph
4 Commits
Author SHA1 Message Date
Sam SungandClaude Opus 5.5 f768cf3e60 CastSpellByName's onSelf and CastSpell's third argument cast the spell at the player, the pet book's at the player too, read through GetBoolOrDefault as the reference does (#634)
`CastSpellByName(name, onSelf)` dropped its second argument, and `CastSpell(slot, book, onSelf)` read only two. So a self-cast macro line such as `CastSpellByName("Power Word: Fortitude", 1)` buffed the selected friend instead of you. With no friendly target and `autoSelfCast` off, it did not cast at all.

In the reference, `CastSpellByName` (`0x4b4ab0`) reads its second argument through `GetBoolOrDefault(L, 2, 0)` (`0x4b4aea`-`0x4b4af1`). `CastSpell` (`0x4b42f0`) reads its third the same way (`0x4b4333`-`0x4b433c`). When the value is nonzero, the active player's guid replaces the selection as the guid handed to the dispatcher `0x4b3300` (`0x4b4afa`, `0x4b4345`). The player's book then binds it through `ArmCast`'s explicit-guid leg (`0x6e5393`). The pet book sends it as `CMSG_PET_ACTION`'s target (`0x4b34ce`).

- `ScriptCall::CastSpell` and `CastPetSpell` now carry `on_self`, read with `binding_abi::bool_or_default`. That is not Lua truthiness: `"0"`, `"false"` and `0.5` are false; `"yes"`, `"on"` and `1` are true.
- The action bar's self-bind swap is now `CastContext::self_bound`, which `UseAction`'s `onSelf` and the two script casts share. `ui_spellbook::cast_spell` applies it and hands `on_self` to the attack pick.
- The pet book's `CMSG_PET_ACTION` aims at the player under `onSelf`.

Tests:
- `cast_spell_by_name_reads_on_self_as_the_reference_coerces_it` and `cast_spell_reads_on_self_from_its_third_argument` cover the coercion.
- `on_self_casts_at_the_player_and_leaves_the_selection`, `on_self_binds_the_player_where_the_selection_is_not_assistable` and `a_pet_book_cast_on_self_aims_at_the_player` check the wire target through `apply_script_calls`.

Fixes #446

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-29 21:11:47 +07:00
Sam SungandClaude Opus 5.5 bacbb791db The player's Attack runs the attack validator's target pick: a friend selected retargets the nearest enemy in call order, a corpse or a fruitless scan says "You cannot attack that target.", nothing selected and no enemy says "There is nothing to attack." (#249) (#501)
The player's Attack (the Attack Target key, `AttackTarget()` and the Attack action button) now picks its target as 1.12.1's validator `0x612df0` does, where before it swung at whatever was selected, a friendly unit or a corpse included.

- With nothing selected, or a unit the player is friendly toward (reaction ≥ 4, `0x6130a3`), it runs the Tab cycler for the nearest enemy (`0x6130b5` → `0x493f60`), which commits the new selection at once, so `AttackTarget()` then `/cast` in one script casts at the new target.
- It then reads the selection again (`0x6130c1`): none raises "There is nothing to attack." (`0x6130d9`); a unit neither alive nor feigning, or one `CanAttack` (`0x606980`) rejects, raises "You cannot attack that target." (`0x613171`). A friendly unit with no enemy near stays selected and meets that second gate.
- The swing toggle runs only once a target is accepted (`0x6131a0`), so Attack at a corpse mid-swing gives the error and keeps the swing.
- The pet's attack order shares the same pick. vmangos stops a swing at a friendly or dead target server-side (`CombatHandler.cpp:46-59`), which the client no longer sends.

Fixes #249

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 13:32:22 +07:00
Sam SungandClaude Opus 5.5 06bc451bac The 48 movement, camera and targeting functions Bindings.xml calls are Lua globals on their keys' paths: Start/Stop hold the key's command inside a binding body, targeting and AttackTarget go through the call queue (#485)
The 48 functions the stock `Bindings.xml` bodies call are now Lua globals, registered from the reference's three tables (`0x8500b8`, `0x84f7a0`, `0x83de68`), so an addon, an addon's `Bindings.xml` body or a `/script` line that calls `TargetNearestEnemy`, `TargetLastTarget`, `AttackTarget` or the rest no longer raises. Each runs the code its native binding runs.

- **Targeting** goes through the ordered script-call queue, so `/script TargetNearestEnemy()` then `/cast` casts at the unit the cycle picked. `TargetNearestEnemy`, `TargetNearestFriend`, `TargetNearestPartyMember` and `TargetNearestRaidMember` share the Tab cycler (`0x493f60`, modes 1-4; party and raid modes are new: `0x493eed`, `0x493f15`), reading the reverse flag as `0x6f1c10` does. `TargetLastTarget` (`0x489b00`) swaps to the last-target pair `SetSelection` stamps, and deselects when it is empty. `AttackTarget` (`0x489b50`) shares the ATTACKTARGET key's body.
- **Movement:** 23 of the 26 movement-table functions open with the gate `0x494a50`, which lets a call through only while a binding runs; here that is a binding body running, stock or an addon's. Inside one, `MoveForwardStart`/`Stop` and the rest set and clear the same held state the key does (`0x515090`); a Start with no Stop keeps moving, and the window deactivate (`0x514490`) and the loading cover (`0x5144c0`) clear it. `Jump`, `ToggleRun`, `ToggleAutoRun`, `SitOrStand` (`0x48b920`) and `ToggleSheath` (`0x48a070`) fire their key's command. `IsMouselooking` (`0x514270`) answers 1 or nil.
- **Camera:** `CameraZoomIn`/`Out([yards])` (`0x50b400`, `0x50b450`) take a number or numeric string, 1.0 when absent, into the wheel's zoom.
- Not built, so they answer and do nothing: the pitch pair, `TurnOrAction*`, `CameraOrSelectOrMove*`, `MouselookStart`/`Stop`, the `MoveView*` pairs and `ToggleMouseMove`; each is named with its address where it is registered.

Fixes #334

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:09:45 +07:00
Sam SungandClaude Opus 5.5 03b1072220 A script's selection, cast and targeting-cursor calls apply in the order it made them, so a macro's /target then /cast casts at the new target (#473)
A script's calls that touch the selection, the player's cast or the targeting cursor now take effect in the order the script made them, as in 1.12.1, where each such call is done when it returns (the macro runner `0x4f14e0` fires every line in one pass; `TargetUnit` `0x4899d0` and `TargetByName` `0x489d60` commit through `SetSelection` `0x493540`; `CastSpellByName` `0x4b4ab0` casts at the selection as it stands then).

- The VM keeps one ordered queue of these calls (`ScriptCall`) in place of fifteen per-kind queues, and one system applies it front to back in the target chain, each call through its owner's applier.
- A macro's lines run inside the `UseAction` that pressed it (`0x4e6098 call 0x4f1460`), so the calls they make are applied before the next queued call.
- `ClearTarget` deselects whatever is selected when it applies, so `TargetUnit("player") ClearTarget()` ends with no target.
- `SpellStopCasting`, `SpellStopTargeting`, `TargetNearestFriend`, pet bar presses and orders and pet-book casts join the queue, since they touch the same state: `/script SpellStopCasting()` then `/cast`, and `/assist` then `/petattack`, now apply in order too.

Tests run the real dispatcher on the headless client: target then cast casts at the new target, cast then target at the old one, a clear after a target ends with nothing selected, and a macro's calls land inside the `UseAction` that ran it. Built on macOS; no platform seam.

Fixes #451

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:48:00 +07:00