Files
benilla-octo/crates/benilla-ui/src/script/calls.rs
T
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

98 lines
4.2 KiB
Rust

//! The script calls whose effects touch the selection, the player's cast or the targeting cursor,
//! held in one queue in the order the script made them. In the reference each such call is done
//! when it returns: the macro runner `0x4f14e0` fires every line in one pass, `TargetUnit`
//! (`0x4899d0`), `TargetByName` (`0x489d60`) and `ClearTarget` (`0x489ff0`) commit through
//! `SetSelection 0x493540` before returning, and `CastSpellByName` (`0x4b4ab0`) casts at the
//! selection as it stands then. So `/target Bob` then `/cast Flash Heal` heals Bob, and the app
//! applies this queue front to back at one point in the frame.
use super::{ActionUse, SelectionRequest, UiScript};
/// One queued call, in the reference's terms.
#[derive(Clone, Debug, PartialEq)]
pub enum ScriptCall {
/// `TargetUnit`, `AssistUnit`, `AssistByName` and `TargetLastEnemy`.
Select(SelectionRequest),
/// `TargetByName(name, exactMatch)` (`0x489d60`), the stock `/target`.
TargetByName { name: String, exact: bool },
/// `TargetNearestEnemy`, `TargetNearestFriend`, `TargetNearestPartyMember` and
/// `TargetNearestRaidMember` (`0x489a80`/`0x489aa0`/`0x489ac0`/`0x489ae0`): one TAB cycle,
/// `0x493f60(reverse, mode)`.
TargetNearest { mode: NearestMode, reverse: bool },
/// `TargetLastTarget()` (`0x489b00`): re-select the last-target pair, or deselect when it is
/// empty.
TargetLastTarget,
/// `ClearTarget()` (`0x489ff0` → `0x493540(0,0)`), a no-op when nothing is selected by then.
ClearTarget,
/// `AttackTarget()` (`0x489b50` → `0x6131a0(0,0)`): the attack toggle at the selection as it
/// stands then.
AttackTarget,
/// `SpellTargetUnit(unit)` (`0x6e6d90`), made while the cursor was up.
SpellTargetUnit(String),
/// `SpellStopTargeting()` (`0x6e6e30`), made while the cursor was up.
SpellStopTargeting,
/// `SpellStopCasting()` (`0x6e6e80`), made with something to stop.
SpellStopCasting,
/// `CastSpell(id, "spell")` and `CastSpellByName`, through `0x4b3300`'s player leg.
CastSpell(u32),
/// `CastSpell(id, "pet")`, `0x4b3300`'s `CMSG_PET_ACTION` fork (`0x4b34ce`).
CastPetSpell(u32),
/// `CastShapeshiftForm(index)` (`0x4b4810`), as the form's spell.
CastShapeshiftForm(u32),
/// `UseAction(slot, …)` (`0x4e7140` → `0x4e5ee0`); a macro slot runs its lines inside it.
UseAction(ActionUse),
/// `UseContainerItem(bag, slot)` outside repair mode.
UseContainerItem { bag: i64, slot: u32 },
/// `UseInventoryItem(id)` outside repair mode.
UseInventoryItem(u32),
/// `CastPetAction(slot)`, a 1-based pet bar slot.
PetAction(u32),
/// `PetAttack` and the other one-shot orders, as their bar slot's packed word.
PetOrder(u32),
}
/// The mode the four `TargetNearest*` shims hand the TAB cycler `0x493f60`, the only byte that
/// differs between them; it reaches only the per-candidate filter `0x493e40`.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NearestMode {
/// Mode 1, `TargetNearestEnemy`.
Enemy,
/// Mode 2, `TargetNearestFriend`.
Friend,
/// Mode 3, `TargetNearestPartyMember`: a party member other than us.
PartyMember,
/// Mode 4, `TargetNearestRaidMember`: a party or raid member other than us.
RaidMember,
}
impl UiScript {
/// Take every queued call, in call order.
pub fn take_script_calls(&mut self) -> Vec<ScriptCall> {
std::mem::take(&mut self.model_mut().script_calls)
}
/// Put calls back at the front of the queue, ahead of any queued since.
pub fn requeue_script_calls(&mut self, calls: Vec<ScriptCall>) {
let mut model = self.model_mut();
let later = std::mem::replace(&mut model.script_calls, calls);
model.script_calls.extend(later);
}
/// Take the queued calls `pick` answers for, in call order, leaving the rest queued.
pub(crate) fn take_calls_where<T>(
&mut self,
mut pick: impl FnMut(&ScriptCall) -> Option<T>,
) -> Vec<T> {
let mut model = self.model_mut();
let mut taken = Vec::new();
model.script_calls.retain(|call| match pick(call) {
Some(t) => {
taken.push(t);
false
}
None => true,
});
taken
}
}