From 66340800a93035e4ffb416031b8d54fdb18c8f6c Mon Sep 17 00:00:00 2001 From: Sam Sung <51686767+samwhosung@users.noreply.github.com> Date: Tue, 29 Sep 2026 21:36:15 +0700 Subject: [PATCH] unit tokens: partypetN and raidpetN resolve to the member's held pet, tested before party and raid, and the reach feeds cover them (#635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `UnitTokens::resolve` had no arm for `partypetN` or `raidpetN`. `"partypet1"` fell into the `party` arm and failed to parse `pet1`, and `"raidpet1"` did the same in the `raid` arm. As a result, the stock TARGETPARTYPET1-4 bindings, the pet half of the TARGETPARTYMEMBER toggle, `TargetUnit`, `AssistUnit` and `SpellTargetUnit` all did nothing on a party or raid pet. The reference's resolver `0x515970` tests `partypet` before `party` (`0x515a9a`) and `raidpet` before `raid` (`0x515b1c`): - `partypetN` (`0x491930` → `0x4e81d0`) takes the Nth party slot's member. It names that member's `CHARM`, else its `SUMMON`, while the member is held. Otherwise it names the pet guid on the party record, behind the member's online bit (`0x4e8221`-`0x4e8230`). - `raidpetN` (`0x491960`) does the same for the Nth raid row, bounded by the raid count, with no online test. Changes: - The two arms resolve through the existing `GroupState::party_pet_guid` and `raid_pet_guid`, then the object manager. - The reference's `partypetN` has no bound. Here an index outside 1-4 names nobody, as it does for `partyN`. - The four group arms now share one digit parse, `slot_number`, which is digits-only as the reference's is. So `"party+1"` no longer resolves. - `reach_tokens` now covers the pet tokens, so `CheckInteractDistance`, `CanInspect` and `SpellCanTargetUnit` measure them. - The token tables `ui_aura` kept privately now live in `ui_party` beside `PARTY_TOKENS` and `RAID_TOKENS`. Tests: - `ui_unit.rs`'s `group_pet_tokens` covers the order against `party`/`raid`, case folding, the bounds, charm-else-summon, the record leg and its online bit, raid rows and the reach feed. - `target/script.rs` adds `target_unit_selects_a_party_pet`. This is the resolver half of #448. The per-token unit snapshot that `UnitExists`, `UnitName` and `UnitHealth` read does not push the pet tokens yet, so the party pet frames stay hidden until that half lands. Part of #448 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 5.5 (1M context) --- crates/benilla-app/src/target/script.rs | 125 +++++++- crates/benilla-app/src/ui_aura/units.rs | 37 +-- crates/benilla-app/src/ui_party/feed.rs | 14 + crates/benilla-app/src/ui_party/mod.rs | 2 +- crates/benilla-app/src/ui_unit.rs | 408 +++++++++++++++++++++++- 5 files changed, 530 insertions(+), 56 deletions(-) diff --git a/crates/benilla-app/src/target/script.rs b/crates/benilla-app/src/target/script.rs index 82b0d554..bb92fb82 100644 --- a/crates/benilla-app/src/target/script.rs +++ b/crates/benilla-app/src/target/script.rs @@ -113,22 +113,10 @@ impl ScriptSelect<'_, '_> { mod tests { use super::*; - /// The selection calls from Lua, through the binding bodies of ASSISTTARGET (`AssistUnit`) and - /// TARGETLASTHOSTILE (`TargetLastEnemy`): an empty basis, a garbage token and a stale memory - /// are each a no-op (`0x489a40`'s bare `ret`), never a deselect or a panic. - #[test] - fn the_selection_calls_run_assist_and_last_enemy_without_ever_deselecting() { - use crate::net::{Guid, NetCommands, ObjectStore, SelfPlayer}; + /// A world holding what [`ScriptSelect`] reads, with a bare VM and no units. + fn script_world() -> World { + use crate::net::NetCommands; use benilla_ui::script::UiScript; - use bevy::ecs::system::RunSystemOnce; - - const ME: u64 = 1; - const BASIS: u64 = 0xB0A2; - const VICTIM: u64 = 0xC0DE; - const GONE: u64 = 0x6017; - // `UNIT_FIELD_TARGET` is a 2-field guid at index 16; HEALTH/MAXHEALTH keep the units live. - let store = - |pairs: &[(u16, u32)]| ObjectStore(benilla_protocol::ObjectFields::from_pairs(pairs)); let (tx, _rx) = crossbeam_channel::unbounded(); let mut world = World::new(); @@ -148,6 +136,27 @@ mod tests { world.init_resource::(); world.init_resource::