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>
98 lines
4.2 KiB
Rust
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
|
|
}
|
|
}
|