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

88 lines
3.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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