Files
MaidEngine/docs/superpowers/specs/2026-04-02-combat-system-design.md
2026-04-02 08:29:24 -04:00

3.4 KiB
Raw Blame History

Combat System

Overview

A stateless CombatSystem node that creates pre-combat proposals and applies combat resolution between two units. The system encapsulates all combat stat calculation so other systems don't need to know proposal logic.

CombatProposal Resource

resources/resource_definitions/combat_proposal.gd

A Resource containing two sides — attacker and defender. Each side holds:

  • A reference to the Unit node
  • Pre-combat snapshot stats via an inner class CombatantStats:
    • hp: int — from current_stats.current_hp
    • sp: int — from current_stats.current_sp
    • hit: int — calculated as own hit - opponent's eva
    • atk: int — from current_stats.phys_atk
    • def: int — from current_stats.phys_def
    • spd: int — from current_stats.spd

Stat Calculation

For the attacker side:

  • atk = attacker's phys_atk
  • def = attacker's phys_def
  • hit = attacker's hit - defender's eva
  • hp, sp, spd copied directly from attacker's current_stats

For the defender side:

  • atk = defender's phys_atk
  • def = defender's phys_def
  • hit = defender's hit - attacker's eva
  • hp, sp, spd copied directly from defender's current_stats

In the future, different attacks may swap between physical and magic stats, but for now only physical stats are used.

CombatSystem Node

nodes/combat_system.gd

A Node added to the strategy phase scene tree. Stateless — operates purely on units passed in.

create_proposal(attacker: Unit, defender: Unit) -> CombatProposal

  • Reads both units' current_stats
  • Builds a CombatProposal with snapshot stats for each side
  • Applies cross-calculations (hit - eva)
  • Returns the proposal without modifying any unit state

apply_proposal(proposal: CombatProposal) -> void

  1. Attacker strikes: Roll random int 1100. If roll <= attacker's calculated hit, apply max(attacker.atk - defender.def, 0) damage to the defender Unit's current_stats.current_hp (modifies the actual unit, not the snapshot).
  2. Counterattack: If the defender Unit's current_stats.current_hp > 0 after the attack, roll 1100 for defender. If roll <= defender's calculated hit, apply max(defender.atk - attacker.def, 0) damage to the attacker Unit's current_stats.current_hp.
  • Damage has a floor of 0 (no negative damage / healing).
  • Range is ignored for now. In the future, counterattack will depend on whether the defender has skills at appropriate range (defaulting to a "defend" action if not).

Scene Integration

The CombatSystem node is added to scenes/strategy_phase.tscn as a sibling of PlayerController, CombatMap, etc. No exports needed.

CombatTest (Node2D)
  ├─ CombatUI
  ├─ CombatMap
  ├─ PlayerController
  ├─ CombatSystem       <-- new
  ├─ Camera2D
  └─ AudioStreamPlayer

Files Changed

File Change
resources/resource_definitions/combat_proposal.gd New — CombatProposal resource with CombatantStats inner class
nodes/combat_system.gd New — CombatSystem node with create_proposal and apply_proposal
scenes/strategy_phase.tscn Add CombatSystem node to scene tree

Out of Scope

  • Magic attack/defense selection (future: different attack types swap stats)
  • Range-based counterattack eligibility (future: defend action when no skills in range)
  • Critical hits
  • UI for displaying the combat proposal
  • Animations or visual feedback for combat resolution