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.
This commit is contained in:
chairmanpow
2026-10-06 23:58:37 +01:00
parent 5be4300401
commit 482a983072
+38 -12
View File
@@ -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<u8>| 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