neon-sprawl/docs/decomposition/modules/pvp_combat_integration.md

3.6 KiB

PvP and the combat engine (#4)

This doc fixes how E6.M1 — PvPEligibilityAndFlagState connects to E5.M1 — CombatRulesEngine: one pipeline vs a forked “PvP combat” module, and how denials surface on the wire.

Complements client_server_authority.md (authority) and contracts.md (artifact types).


Decision: single combat rules engine

Default for Neon Sprawl: E5.M1 is the only combat resolution engine for tab-target play. Player vs player uses the same code path as player vs NPC for formulas that should stay aligned (hit checks, cooldowns, resources, ability application), with extra gating when the target (or victims in an AoE) include players.

Do not introduce a separate “PvP combat engine” for the prototype unless design later proves irreconcilable rule divergence. If that happens, prefer a shared resolution kernel (pure functions / shared module) plus policy profiles (PvE vs PvP) rather than two divergent engines.

Rationale: One engine reduces duplicate bugs, keeps CombatAction / CombatResolution as a single wire story, and matches optional-PvP product goals (PvP is policy on top of the same abilities, not a different game).


Where E6.M1 plugs in

  • When: During validation of a CombatAction (or internal equivalent) before applying damage, debuffs, displacement, or other hostile effects to another player character.
  • Who calls whom: E5.M1 calls into E6.M1 (or an abstraction it owns, implemented by E6.M1) — e.g. CanApplyHostility(attacker, target, actionContext) → Allow | Deny(reason).
  • Consistency: Use current server eligibility and zone policy for that tick/request; avoid stale client-only flags. If eligibility is cached per session, invalidate on zone transition and on PvPStateChanged.

NPC targets: No E6.M1 check for ordinary PvE (unless a future rule ties NPC combat to zone policy — then call out explicitly).

Register note: Epic 5 lists E5.M1 dependencies as E1.M3 and E2.M2 only. Enabling player-target hostility adds a logical dependency on E6.M1; PvE-only milestones may ship with a stub implementation that denies all player-target hostility until Epic 6 wiring lands. See cross-cutting note in the dependency register.


Deny reasons and telemetry

PvP blocks must be first-class in combat outcomes, not silent failures:

  • Extend the combat deny / CombatResolution reason set (or parallel field) with stable codes such as: pvp_forbidden_in_zone, attacker_pvp_not_eligible, target_pvp_not_eligible, pvp_consent_required, pvp_flag_mismatch (exact names to be frozen when Protobuf enums are defined).
  • Align high-signal events with E9.M1 (e.g. ability denied with reason) and Epic 6 hooks (pvp_state_changed, zone enter/exit) without double-counting.

E6.M3 and beyond

E6.M3 (loss, spawn protection, grief strikes) applies after hostility is allowed by E6.M1 and resolved by E5.M1 (e.g. on death in PvP context). Order: eligibility → combat resolution → loss/anti-grief rules.