- Introduce the MissionDebrief component to display after-action reports, including XP, rewards, and squad status. - Implement the MissionGenerator class to create procedural side missions, enhancing replayability and resource management. - Update mission schema to include mission objects for INTERACT objectives, improving mission complexity. - Enhance GameLoop and MissionManager to support new mission features and interactions. - Add tests for MissionDebrief and MissionGenerator to ensure functionality and integration within the game architecture.
116 lines
4.2 KiB
Markdown
116 lines
4.2 KiB
Markdown
---
|
|
description: Turn lifecycle logic - activation, reset, and tick system for unit turns
|
|
globs: src/systems/TurnSystem.js, src/systems/TurnSystem.ts
|
|
alwaysApply: false
|
|
---
|
|
|
|
# **Turn Lifecycle Rule**
|
|
|
|
This rule 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. TypeScript Interfaces**
|
|
|
|
```typescript
|
|
// src/types/TurnSystem.ts
|
|
|
|
export interface TurnState {
|
|
/** The ID of the unit currently acting */
|
|
activeUnitId: string | null;
|
|
/** How many "Ticks" have passed in total (Time) */
|
|
globalTime: number;
|
|
/** Ordered list of who acts next (predicted) for the UI */
|
|
projectedQueue: string[];
|
|
}
|
|
|
|
export interface TurnEvent {
|
|
type: 'TURN_CHANGE';
|
|
previousUnitId: string;
|
|
nextUnitId: string;
|
|
/** Did we wrap around a "virtual round"? */
|
|
isNewRound: boolean;
|
|
}
|
|
```
|
|
|
|
## **5. Conditions of Acceptance (CoA)**
|
|
|
|
**CoA 1: Speed determines frequency**
|
|
|
|
- If Unit A has Speed 20 and Unit B has Speed 10:
|
|
- Unit A should act roughly twice as often as Unit B over 10 turns
|
|
|
|
**CoA 2: Queue Prediction**
|
|
|
|
- The system must expose a `getPredictedQueue(depth)` method that "simulates" future ticks without applying them, so the UI can show the "Next 5 Units" list correctly
|
|
|
|
**CoA 3: Status Duration**
|
|
|
|
- A Status Effect with `duration: 1` applied on Turn X must expire exactly at the _start_ of the unit's _next_ turn (Turn X+1), ensuring it affects them for one full action phase
|
|
|
|
## **6. Implementation Requirements**
|
|
|
|
Create `src/systems/TurnSystem.js`:
|
|
|
|
1. **State:** Maintain a `globalTick` counter and reference to UnitManager
|
|
2. **End Turn Logic:** Implement `endTurn(unit)`. Reset the unit's charge to 0. Tick their cooldowns/statuses
|
|
3. **Time Loop:** Implement `advanceToNextTurn()`. Loop through all alive units, adding Speed to Charge. Stop as soon as one or more units reach 100
|
|
4. **Tie Breaking:** If multiple units pass 100 in the same tick, the one with the highest total charge goes first. If equal, Player beats Enemy
|
|
5. **Prediction:** Implement `simulateQueue(depth)` which clones the current charge state and runs the loop virtually to return an array of the next depth Unit IDs
|
|
|
|
|