aether-shards/specs/TurnLifecycle.spec.md

78 lines
2.9 KiB
Markdown
Raw Normal View History

# **Turn Lifecycle Specification: Activation & Reset**
This document defines the exact state changes that occur when a unit becomes active (Start Turn) and when they finish (End Turn).
## **1. Start of Turn (Activation Phase)**
This logic runs immediately when TurnSystem identifies a unit as the new active actor.
### **A. Action Point (AP) Regeneration**
The unit must be given their budget for the turn.
- **Formula:** Base AP (3) + Math.floor(Speed / 5).
- **Constraint:** AP does _not_ roll over. It resets to this max value every turn. This encourages players to use their actions rather than hoard them.
### **B. Cooldown Reduction**
- Iterate through all skills in unit.skills.
- If cooldown > 0, decrement by 1.
- _Note:_ This ensures a skill used on Turn 1 with a 1-turn cooldown is ready again on Turn 2.
### **C. Status Effect Tick (The "Upkeep" Step)**
- Iterate through unit.statusEffects.
- **Apply Effect:** If the effect is a "DoT" (Damage over Time) or "HoT" (Heal over Time), apply the value now.
- **Decrement Duration:** Reduce the effect's duration by 1.
- **Expire:** If duration reaches 0, remove the effect immediately (unless it is "Permanent").
- **Stun Check:** If a STUN status is active, skip the Action Phase and immediately trigger **End Turn**.
## **2. End of Turn (Resolution Phase)**
This logic runs when the player clicks "End Turn" or the AI finishes its logic.
### **A. Charge Meter Consumption**
- **Logic:** We do _not_ set Charge to 0. We subtract 100.
- **Why?** If a unit has 115 Charge (because they are very fast), setting it to 0 deletes that extra 15 speed advantage. Subtracting 100 lets them keep the 15 head-start on the next race.
- **Formula:** unit.chargeMeter = Math.max(0, unit.chargeMeter - 100).
### **B. Action Slot Reset**
- Reset flags like hasMoved, hasAttacked, or standardActionUsed to false so the UI is clean for the next time they act.
## **3. The Tick Loop (The "Race")**
This runs whenever no unit is currently active.
```js
while (no_unit_has_100_charge) {
globalTick++;
for (all_units as unit) {
// Gain Charge
unit.chargeMeter += unit.stats.speed;
// Cap?
// No cap, but we check for >= 100 break condition
}
}
// Sort by Charge (Descending) -> Highest Charge wins
```
## **4. Prompt for Coding Agent**
"Update src/systems/TurnSystem.js to implement the full Lifecycle.
1. **startTurn(unit)**:
- Calculate Max AP (3 + floor(speed/5)). Set currentAP to this.
- Loop unit skills: cooldown--.
- Loop unit statuses: Apply DoT/HoT logic via EffectProcessor, decrement duration, remove if 0.
- Check for Stun. If stunned, skip to endTurn.
2. **endTurn(unit)**:
- unit.chargeMeter -= 100.
- Triggers advanceToNextTurn().
3. **advanceToNextTurn()**:
- While no unit has >= 100 charge: loop all units, add speed to chargeMeter.
- Once threshold met: Sort candidates by Charge. Pick winner. Call startTurn(winner)."