From 482a98307231e84b4f90b9bdee71fc5b2c49c302 Mon Sep 17 00:00:00 2001 From: chairmanpow Date: Tue, 6 Oct 2026 23:58:37 +0100 Subject: [PATCH] fix: battleground aura traps keep their inverted faction term, so flags capture Arathi Basin's visual-only aura GameObjects (entry 180102, type 6 TRAP) were offered inside a battleground because the whole shared gate was skipped, so they won the pick over the node banner and the CMSG_GAMEOBJ_USE landed on a no-op trap (the server only played its despawn anim). The TRAP inverse is not a team marker, so it still gates: a friendly or neutral aura trap stays at priority 0, the banner (BUTTON/GOOBER/CHEST) wins, and a neutral node captures. --- crates/benilla-app/src/target/cursor_mode.rs | 50 +++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/crates/benilla-app/src/target/cursor_mode.rs b/crates/benilla-app/src/target/cursor_mode.rs index f9861ed0..15d71927 100644 --- a/crates/benilla-app/src/target/cursor_mode.rs +++ b/crates/benilla-app/src/target/cursor_mode.rs @@ -306,23 +306,25 @@ fn highlightable_flags( if type_id == GO_TYPE_MEETINGSTONE { return !overrides.meeting_stone_queued; } - // Inside a battleground the whole shared gate is skipped for the objective types the type gate - // above let through: the objective's `GAMEOBJECT_FACTION` is a team marker the client cannot - // resolve against the player's race faction on a cross-faction realm, and the occupied banners' - // `GAMEOBJECT_FLAGS`/activate bit are the server's per-player bookkeeping. The server validates - // every battleground use (`CanUseBattleGroundObject`), so the client offer is safe. + // The TRAP faction term (`0x5f2fc6`) inverts the ordinary one: a trap is highlightable only to + // whoever it is hostile to. It is not a team marker, so it must keep gating inside a + // battleground: Arathi Basin's visual-only aura GameObjects (180100-180102, type 6) are + // friendly-or-neutral traps, and letting them through wins them the pick over the node banner + // and swallows the capture as a no-op use. + if type_id == GO_TYPE_TRAP { + return reaction.is_none_or(|r| r == 1); + } + // Inside a battleground the rest of the shared gate is skipped for the objective types the type + // gate above let through: the objective's `GAMEOBJECT_FACTION` is a team marker the client + // cannot resolve against the player's race faction on a cross-faction realm, and the occupied + // banners' `GAMEOBJECT_FLAGS`/activate bit are the server's per-player bookkeeping. The server + // validates every battleground use (`CanUseBattleGroundObject`), so the client offer is safe. if overrides.battleground { return true; } // The faction term (`0x5f3026`): the GameObject's reaction toward us must beat hostile. An // unresolved reaction passes, so a data gap never blanks the world. - let ordinary_faction_ok = |r: Option| r.is_none_or(|r| r > 1); - if type_id == GO_TYPE_TRAP { - // TRAP inverts it (`0x5f2fc6`): highlightable only to whoever it is hostile to. - if !reaction.is_none_or(|r| r == 1) { - return false; - } - } else if !ordinary_faction_ok(reaction) { + if !reaction.is_none_or(|r| r > 1) { return false; } if flags & GO_FLAG_IN_USE_OR_NO_INTERACT != 0 { @@ -987,6 +989,30 @@ mod tests { )); // mailbox } + /// A battleground's visual-only aura GameObjects are `TRAP` (Arathi Basin 180100-180102). Their + /// inverted faction term must keep gating there, or they win the pick over the node banner and + /// the `CMSG_GAMEOBJ_USE` lands on a no-op trap instead of the capture. + #[test] + fn a_battleground_aura_trap_keeps_its_inverted_faction_term() { + let bg = GoOverrides { + battleground: true, + ..Default::default() + }; + assert!( + !highlightable_flags(GO_TYPE_TRAP, 0, 0, Some(3), bg), + "a neutral aura trap stays out of the pick" + ); + assert!(highlightable_flags(GO_TYPE_TRAP, 0, 0, Some(1), bg)); + assert!(highlightable_flags(GO_TYPE_TRAP, 0, 0, None, bg)); + // The objective types are still offered in a battleground whatever the faction marker says. + for t in [1, 3, 10, 24] { + assert!( + highlightable_flags(t, 0x11, 0, Some(1), bg), + "objective type {t} is offered in a battleground" + ); + } + } + #[test] fn highlightable_gates_the_quest_object_but_not_the_plain_door() { assert!(highlightable_flags(0, 0, 0, None, GoOverrides::default())); // DOOR, no flags