# Combat Proposal UI Design

## Overview

Add a centered combat forecast overlay to the existing CombatUI that displays a `CombatProposal` with attacker/defender stats and Fight/Cancel buttons. The system uses parent-wired signals — no node references outside parent-child relationships.

## Signal Flow

When a player clicks a defender while an attacker is selected:

1. `PlayerController` emits `combat_requested(attacker, defender)`
2. Parent scene script catches it, calls `CombatSystem.create_proposal(attacker, defender)`
3. Parent calls `CombatUI.show_proposal(proposal)`
4. Player sees the combat forecast panel

On Fight:
1. `CombatUI` emits `fight_confirmed(proposal)`
2. Parent catches it, calls `CombatSystem.apply_proposal(proposal)`
3. Panel closes

On Cancel (Cancel button or right-click):
1. `CombatUI` emits `fight_cancelled`
2. Parent catches it (no action needed beyond the signal existing for future use)
3. Panel closes

## Architecture

### Encapsulation Rules

- **CombatUI** knows only about its own children (UnitPanel, CombatProposalPanel, buttons). Exposes signals and public methods.
- **PlayerController** knows nothing about CombatUI or CombatSystem. Emits `combat_requested`.
- **CombatSystem** knows nothing about UI. Exposes `create_proposal()` and `apply_proposal()`.
- **Parent scene script** (`strategy_phase.tscn` root node) is the only place that references sibling nodes via `@onready`. It wires signals in `_ready()` and contains the orchestration logic.

### Parent Scene Script

A new script on the `strategy_phase.tscn` root node (~20 lines) that:

- Holds `@onready` references to `PlayerController`, `CombatSystem`, and `CombatUI`
- Connects `PlayerController.combat_requested` to a local method that creates a proposal and passes it to `CombatUI.show_proposal()`
- Connects `CombatUI.fight_confirmed` to a local method that calls `CombatSystem.apply_proposal()`
- Connects `CombatUI.fight_cancelled` (no-op for now, but wired for future use)

This replaces the current direct signal connection from `PlayerController.combat_requested` to `CombatSystem.process_combat` in the scene editor.

## CombatUI Changes

### New Signals

- `fight_confirmed(proposal: CombatProposal)` — emitted when Fight button pressed
- `fight_cancelled` — emitted when Cancel button pressed or right-click detected

### New Public Methods

- `show_proposal(proposal: CombatProposal)` — stores the proposal reference, populates all labels and progress bars from the proposal's `CombatantStats`, shows the CombatProposalPanel
- `_hide_proposal()` — hides the panel, clears stored proposal reference

### Input Handling

Right-click while the proposal panel is visible triggers `_hide_proposal()` and emits `fight_cancelled`. Handled via `_unhandled_input` on CombatUI.

### Existing Unit Panel

The bottom-left unit info panel remains visible and unaffected. It continues to show the selected unit's info, which provides useful context alongside the forecast.

## CombatProposalPanel Layout

New `PanelContainer` child of the CombatUI CanvasLayer, centered on screen via anchor presets. Hidden by default.

```
┌─────────────────────────────────────────────┐
│              COMBAT FORECAST                │
│                                             │
│   [Attacker]           [Defender]           │
│   Name                     Name             │
│   HP ████████░░  HP ██████░░░░              │
│   ATK: 12              ATK: 8              │
│   DEF: 5               DEF: 6              │
│   HIT: 78%             HIT: 65%            │
│   SPD: 10              SPD: 7              │
│                                             │
│         [ Fight ]    [ Cancel ]             │
└─────────────────────────────────────────────┘
```

### Scene Structure

```
CombatUI (CanvasLayer) — combat_ui.gd
├── UnitPanel (PanelContainer) — existing, bottom-left
│   └── MarginContainer
│       └── VBoxContainer
│           ├── NameLabel (Label)
│           └── HPBar (ProgressBar)
└── CombatProposalPanel (PanelContainer) — new, centered
    └── MarginContainer
        └── VBoxContainer
            ├── TitleLabel (Label) — "COMBAT FORECAST"
            ├── StatsContainer (HBoxContainer)
            │   ├── AttackerStats (VBoxContainer)
            │   │   ├── AttackerNameLabel (Label)
            │   │   ├── AttackerHPBar (ProgressBar)
            │   │   ├── AttackerATKLabel (Label) — "ATK: {value}"
            │   │   ├── AttackerDEFLabel (Label) — "DEF: {value}"
            │   │   ├── AttackerHITLabel (Label) — "HIT: {value}%"
            │   │   └── AttackerSPDLabel (Label) — "SPD: {value}"
            │   └── DefenderStats (VBoxContainer)
            │       ├── DefenderNameLabel (Label)
            │       ├── DefenderHPBar (ProgressBar)
            │       ├── DefenderATKLabel (Label) — "ATK: {value}"
            │       ├── DefenderDEFLabel (Label) — "DEF: {value}"
            │       ├── DefenderHITLabel (Label) — "HIT: {value}%"
            │       └── DefenderSPDLabel (Label) — "SPD: {value}"
            └── ButtonContainer (HBoxContainer) — centered
                ├── FightButton (Button) — "Fight"
                └── CancelButton (Button) — "Cancel"
```

### Data Mapping

All values come from `CombatProposal.CombatantStats`:

| Panel Field | Source |
|---|---|
| Name | `combatant_stats.unit.info.name` |
| HP bar max | `combatant_stats.max_hp` |
| HP bar value | `combatant_stats.hp` |
| ATK | `combatant_stats.atk` |
| DEF | `combatant_stats.def` |
| HIT | `combatant_stats.hit` (already adjusted: attacker hit - defender eva) |
| SPD | `combatant_stats.spd` |

### CombatProposal Change

`CombatantStats` in `resources/resource_definitions/combat_proposal.gd` needs a new `max_hp: int` field added so the HP bar can display a meaningful ratio. `CombatSystem._snapshot()` must populate it from `unit.current_stats.max_hp`.

### Styling

- Uses the existing `main_ui_theme.tres` (Minecraft font)
- Panel background: default theme PanelContainer style
- Centered via `anchors_preset = CENTER`
- No fixed pixel size — let the content determine width naturally with margin padding

## Button Wiring

FightButton and CancelButton `pressed` signals connect to methods on `combat_ui.gd` within the scene (parent-child, no encapsulation issue):

- `FightButton.pressed` → `_on_fight_pressed()` — emits `fight_confirmed(stored_proposal)`, calls `_hide_proposal()`
- `CancelButton.pressed` → `_on_cancel_pressed()` — emits `fight_cancelled`, calls `_hide_proposal()`

## Files Changed

| File | Change |
|---|---|
| `resources/resource_definitions/combat_proposal.gd` | Add `max_hp` field to `CombatantStats` |
| `nodes/combat_system.gd` | Populate `max_hp` in `_snapshot()` |
| `scripts/combat_ui.gd` | Add signals, `show_proposal()`, `_hide_proposal()`, `_unhandled_input` for right-click cancel, button handlers |
| `prefabs/combat_ui.tscn` | Add CombatProposalPanel subtree, wire button signals |
| `scenes/strategy_phase.tscn` | Add root script, remove direct PlayerController→CombatSystem signal connection |

## Files Added

| File | Purpose |
|---|---|
| `nodes/strategy_phase.gd` | Parent orchestration script for strategy_phase.tscn root node |
