fix: battleground objectives use the plain-use path and skip the client gate
This commit is contained in:
@@ -645,6 +645,13 @@ pub(crate) fn resolve_go_action(
|
||||
me_store: Option<&ObjectStore>,
|
||||
net: &NetCommands,
|
||||
) -> GoAction {
|
||||
// A battleground objective is a plain use: its template lock is the server's capture
|
||||
// bookkeeping, not a chest lock the client should resolve, and the server validates the use
|
||||
// itself. Without this the objective's lock chain can refuse (`Refuse`/`OpenLock`) a click the
|
||||
// reference sends as `CMSG_GAMEOBJ_USE`.
|
||||
if inputs.battleground.active() {
|
||||
return GoAction::Use;
|
||||
}
|
||||
let Some(tmpl) = inputs.templates.get(guid) else {
|
||||
return GoAction::Use;
|
||||
};
|
||||
|
||||
@@ -306,20 +306,24 @@ 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.
|
||||
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. Skipped inside a
|
||||
// battleground: the objective's faction is a team marker the client cannot resolve against the
|
||||
// player's race faction on a cross-faction realm, and the server gates the use itself.
|
||||
if !overrides.battleground {
|
||||
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) {
|
||||
// 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) {
|
||||
return false;
|
||||
}
|
||||
if flags & GO_FLAG_IN_USE_OR_NO_INTERACT != 0 {
|
||||
return false;
|
||||
@@ -608,23 +612,26 @@ pub(super) fn classify_cursor(
|
||||
// arm is not applied. The lock arm needs `GO_FLAG_LOCKED`, so an herb the player cannot
|
||||
// gather stays lit and refuses on the click, while a padlocked door grays.
|
||||
let facts = super::lock::go_facts(Some((store, crate::go_anim::go_state(anim, store))));
|
||||
let lock_unmet = go_inputs
|
||||
.locks
|
||||
.as_deref()
|
||||
.and_then(|l| l.0.slots(lock_id).filter(|_| lock_id != 0))
|
||||
.is_some_and(|slots| {
|
||||
super::lock::resolve_lock(
|
||||
slots,
|
||||
&player_actions.spells,
|
||||
go_inputs.spells.as_deref(),
|
||||
go_inputs.skill_lines.as_ref().map(|s| &s.catalog),
|
||||
Some(self_store),
|
||||
&go_inputs.objects,
|
||||
facts,
|
||||
&mut None,
|
||||
)
|
||||
.blocks_usable(facts.flag_locked)
|
||||
});
|
||||
// A battleground objective's lock is capture bookkeeping, not a chest lock: the plain-use
|
||||
// path skips it, so the gray arm must too.
|
||||
let lock_unmet = !go_inputs.battleground.active()
|
||||
&& go_inputs
|
||||
.locks
|
||||
.as_deref()
|
||||
.and_then(|l| l.0.slots(lock_id).filter(|_| lock_id != 0))
|
||||
.is_some_and(|slots| {
|
||||
super::lock::resolve_lock(
|
||||
slots,
|
||||
&player_actions.spells,
|
||||
go_inputs.spells.as_deref(),
|
||||
go_inputs.skill_lines.as_ref().map(|s| &s.catalog),
|
||||
Some(self_store),
|
||||
&go_inputs.objects,
|
||||
facts,
|
||||
&mut None,
|
||||
)
|
||||
.blocks_usable(facts.flag_locked)
|
||||
});
|
||||
let unable = kind != CursorKind::PickLock
|
||||
&& (lock_unmet || dist_sq > go_interact_range_sq(store.0.gameobject_type_id()));
|
||||
Some((kind, unable))
|
||||
|
||||
Reference in New Issue
Block a user