# Macro Game Loop - Full 5-Floor System

## Overview

This document describes the full game loop with 5 floors, deck management, robot recycling, and escalating difficulty. The core mechanic: **defeated robots shuffle into the next floor's deck**, creating an escalating challenge where early floors are varied, but later floors become longer and increasingly robot-dense.

---

## Game Flow

```
Title Screen 
  → [Start Game] 
  → Initialize 5 Floor Decks
  → Draw First Room (Floor 1)
  → Combat/Loot Loop 
    → Defeat Robots → Add to "Defeated Robot Pool"
    → Collect Equipment → Add to Inventory
    → Next Room
    → If Deck Empty → Award Orb → Go to Next Floor
  → Repeat for Floors 2-5
  → Floor 5 Complete → Victory Screen
  → [Play Again] → Restart
```

---

## Core Mechanic: Robot Recycling

### The Pattern

**Floor 1:** 
- 10 cards: mix of robots + equipment + elevator
- Defeat robots → They go into Floor 2's deck
- Collect equipment → Goes to your inventory

**Floor 2:** 
- 10 cards (original) + robots from Floor 1
- Now has ~12-15 cards depending on how many robots were in Floor 1
- Defeat robots → They go into Floor 3's deck
- Collect equipment → Inventory

**Floor 3-5:**
- Each floor gets longer (more cards)
- Each floor gets more robot-dense (equipment cards filtered out)
- By Floor 5: Expect 20-30+ cards, mostly robots, epic final gauntlet

### Why This Works

1. **Early Game:** Mixed encounters, player learns enemy patterns
2. **Mid Game:** Longer encounters, player has built up equipment
3. **Late Game:** Massive robot-dense floors, player uses full build
4. **Strategic Depth:** "I see these robots on Floor 1, I should build around them"
5. **Natural Escalation:** No artificial scaling needed, just filtering and recycling

---

## Initial Setup

### Floor Deck Creation (5 Decks of 10 Cards Each)

**Robot Pool (available robots per floor):**
- Floor 1: robot_weak, robot_balanced, robot_glass, chargeBot, shieldBot, healBot, flameBot, turtleBot (8 types)
- Floor 2: All Floor 1 robots + sparkShellBot, scrapBot (10 types)
- Floor 3: All Floor 2 robots + crabBot (11 types)
- Floor 4+: All robots available (11 types)

**Equipment Pool (same for all floors):**
- wideBeam (1)
- medkit (2)
- speedBooster (1)
- toolkit (1)
- Total: 5 equipment cards

**Special Cards:**
- elevator (1) - Only in Floor 1

### Deck Composition Algorithm

For each floor deck (Floors 1-5):
1. Start with empty deck
2. Filter robot pool by minFloor (only robots where `robot.minFloor <= currentFloor`)
3. Randomly select robots from filtered pool (with replacement allowed for variety)
4. Randomly select equipment cards (with replacement)
5. Target composition:
   - **Floor 1:** 5 robots, 5 equipment (balanced start)
   - **Floor 2:** 6 robots, 4 equipment (slightly harder)
   - **Floor 3:** 7 robots, 3 equipment (getting tough)
   - **Floor 4:** 7 robots, 3 equipment (consistent challenge)
   - **Floor 5:** 8 robots, 2 equipment (final gauntlet)

6. **Special handling for Floor 1:**
   - After creating deck, remove 1 card (to make room for elevator)
   - Shuffle deck
   - Split deck in half
   - Insert elevator card randomly in the **bottom half**
   - This ensures elevator appears in encounters 3-5 (of 5 encounters)

### Example Initial Setup

```javascript
Floor 1 Deck (10 cards after elevator):
  - 5 robots (robot_weak x2, robot_balanced, chargeBot, healBot)
  - 4 equipment (medkit, speedBooster, wideBeam, toolkit)
  - 1 elevator (shuffled into bottom half)

Floor 2 Deck (10 cards):
  - 6 robots (robot_weak, robot_glass x2, shieldBot, sparkShellBot, flameBot)
  - 4 equipment (medkit x2, wideBeam, speedBooster)

Floor 3 Deck (10 cards):
  - 7 robots (robot_balanced, chargeBot x2, crabBot, turtleBot, flameBot, scrapBot)
  - 3 equipment (medkit, toolkit, speedBooster)

Floor 4 Deck (10 cards):
  - 7 robots (various)
  - 3 equipment

Floor 5 Deck (10 cards):
  - 8 robots (various)
  - 2 equipment
```

---

## Game State Structure

### New State Fields

```javascript
G = {
  // === EXISTING STATE ===
  player: { hp, maxHp, equipment, upgradePoints },
  robots: [],
  level: 1,
  actionsRemaining: 1,
  actionsPerTurn: 2,
  isAnimating: false,
  lastMessage: null,
  
  // === NEW STATE FOR 5-FLOOR SYSTEM ===
  
  // Deck system (arrays of card IDs)
  floorDecks: {
    1: [...],  // Array of card IDs
    2: [...],
    3: [...],
    4: [...],
    5: [...],
  },
  
  // Robot recycling
  defeatedRobotsThisFloor: [],  // Array of robot templateIds defeated this floor
                                  // These will be shuffled into next floor
  
  // Progression tracking
  orbsCollected: { 1: false, 2: false, 3: false, 4: false, 5: false },
  elevatorUnlocked: false,
  gameComplete: false,
  
  // Encounter management
  pendingEquipment: [],  // Equipment waiting to be claimed after combat
}
```

---

## Initialization Logic

### `initializeGame(G)` Function

```javascript
function initializeGame(G) {
  console.log('[INIT] Creating 5-floor game');
  
  // Reset player state
  G.player = {
    hp: 12,
    maxHp: 12,
    equipment: {
    charger: createEquipment('charger'),
    blaster: createEquipment('blaster'),
    },
    upgradePoints: 0,
  };
  
  // Reset game state
  G.robots = [];
  G.level = 1;
  G.orbsCollected = { 1: false, 2: false, 3: false, 4: false, 5: false };
  G.elevatorUnlocked = false;
  G.gameComplete = false;
  G.actionsRemaining = 1;
  G.actionsPerTurn = 2;
  G.pendingEquipment = [];
  G.defeatedRobotsThisFloor = [];
  
  // Create 5 floor decks
  G.floorDecks = {
    1: createFloorDeck(1, true),   // true = include elevator
    2: createFloorDeck(2, false),
    3: createFloorDeck(3, false),
    4: createFloorDeck(4, false),
    5: createFloorDeck(5, false),
  };
  
  console.log('[INIT] Floor decks created:', Object.keys(G.floorDecks).map(f => `Floor ${f}: ${G.floorDecks[f].length} cards`));
}
```

### `createFloorDeck(floorNumber, includeElevator)` Function

```javascript
function createFloorDeck(floorNumber, includeElevator) {
  // Define composition
  const compositions = {
    1: { robots: 5, equipment: 4 },
    2: { robots: 6, equipment: 4 },
    3: { robots: 7, equipment: 3 },
    4: { robots: 7, equipment: 3 },
    5: { robots: 8, equipment: 2 },
  };
  
  const { robots: robotCount, equipment: equipCount } = compositions[floorNumber];
  
  // Get available robots for this floor (filter by minFloor)
  const availableRobots = Object.keys(ROBOT_CARDS).filter(robotId => {
    const template = ROBOT_CARDS[robotId];
    return !template.minFloor || floorNumber >= template.minFloor;
  });
  
  const equipmentPool = ['wideBeam', 'medkit', 'speedBooster', 'toolkit'];
  
  const deck = [];
  
  // Add robots (with replacement for variety)
  for (let i = 0; i < robotCount; i++) {
    const randomRobot = availableRobots[Math.floor(Math.random() * availableRobots.length)];
    deck.push(randomRobot);
  }
  
  // Add equipment (with replacement)
  for (let i = 0; i < equipCount; i++) {
    const randomEquip = equipmentPool[Math.floor(Math.random() * equipmentPool.length)];
    deck.push(randomEquip);
  }
  
  // Shuffle the deck
  shuffle(deck);
  
  // Special handling for Floor 1: add elevator to bottom half
  if (includeElevator) {
    // Split deck in half
    const halfPoint = Math.floor(deck.length / 2);
    const topHalf = deck.slice(0, halfPoint);
    const bottomHalf = deck.slice(halfPoint);
    
    // Insert elevator randomly in bottom half
    const insertPosition = Math.floor(Math.random() * bottomHalf.length);
    bottomHalf.splice(insertPosition, 0, 'elevator');
    
    // Recombine
    return [...topHalf, ...bottomHalf];
  }
  
  return deck;
}
```

---

## Encounter System

### Drawing Encounters

**When:** Player clicks "Next Room" button in loot phase

**How many cards:** 
- Floor 1-2: 2 cards per encounter
- Floor 3-4: 3 cards per encounter
- Floor 5: 4 cards per encounter

**Implementation:**

```javascript
function drawEncounter(G) {
  const cardsPerEncounter = {
    1: 2,
    2: 2,
    3: 3,
    4: 3,
    5: 4,
  };
  
  const numCards = cardsPerEncounter[G.level] || 2;
  const currentDeck = G.floorDecks[G.level];
  
  console.log(`[ENCOUNTER] Drawing ${numCards} cards from Floor ${G.level} (${currentDeck.length} cards remaining)`);
  
  // Draw cards
  const drawnCards = currentDeck.splice(0, numCards);
  
  const encounter = {
    robots: [],
    equipment: [],
    hasElevator: false,
    hasOrb: false,
  };
  
  // Process each card
  drawnCards.forEach(cardId => {
    if (cardId === 'elevator') {
      encounter.hasElevator = true;
    } else if (ROBOT_CARDS[cardId]) {
      encounter.robots.push(createRobot(cardId, G));
    } else {
      encounter.equipment.push(cardId);
    }
  });
  
  // Check if floor deck is now empty
  if (currentDeck.length === 0) {
    encounter.hasOrb = true;
    console.log(`[ENCOUNTER] Floor ${G.level} deck empty! Orb will be awarded.`);
  }
  
  return encounter;
}
```

### Processing Encounters

```javascript
function processEncounter(G, encounter) {
  // Add robots to combat
  G.robots = encounter.robots;
  
  // Store equipment as pending (claimed after victory)
  G.pendingEquipment = encounter.equipment;
  
  // Handle elevator
  if (encounter.hasElevator) {
    G.elevatorUnlocked = true;
    G.lastMessage = 'Elevator access granted!';
    console.log('[ENCOUNTER] Elevator unlocked!');
  }
  
  console.log('[ENCOUNTER] Processed:', G.robots.length, 'robots,', encounter.equipment.length, 'equipment pending');
}
```

---

## Robot Recycling System

### When Robots Are Defeated

**Option A: Immediate Recycling (Simpler)**
- When robots are defeated in combat, add their templateId to `G.defeatedRobotsThisFloor`
- This happens in the existing `removeDeadRobots()` helper

**Option B: Batch Recycling (Cleaner)**
- Track defeated robots throughout the floor
- When floor ends (deck empty), shuffle all defeated robots into next floor

**Recommendation: Option A (Immediate)** - Simpler, no need to track separately

### Implementation (in combat.js)

```javascript
export function removeDeadRobots(G) {
  const deadRobots = G.robots.filter(robot => robot.hp <= 0);
  const aliveRobots = G.robots.filter(robot => robot.hp > 0);
  
  // If any robots died, trigger onAllyDeath for survivors
  if (deadRobots.length > 0 && aliveRobots.length > 0) {
    const { getRobotDefinition } = require('../cards');
    
    aliveRobots.forEach(robot => {
      const template = getRobotDefinition(robot);
      if (template && template.onAllyDeath) {
        deadRobots.forEach(() => {
          template.onAllyDeath(robot, G);
        });
      }
    });
  }
  
  // === NEW: Track defeated robots for recycling ===
  deadRobots.forEach(robot => {
    G.defeatedRobotsThisFloor.push(robot.templateId);
    console.log(`[RECYCLE] ${robot.name} (${robot.templateId}) will be shuffled into next floor`);
  });
  
  G.robots = aliveRobots;
}
```

### Shuffling Into Next Floor (When Floor Completes)

**When:** Deck becomes empty (in loot phase `onBegin` or `continueToNextEncounter`)

**What:** Take all `G.defeatedRobotsThisFloor` and shuffle into next floor's deck

**Implementation:**

```javascript
function completeFloor(G) {
  console.log(`[FLOOR] Floor ${G.level} complete!`);
  
  // Award orb for current floor
  G.orbsCollected[G.level] = true;
  
  // Shuffle defeated robots into next floor's deck (if not final floor)
  if (G.level < 5) {
    const nextFloorDeck = G.floorDecks[G.level + 1];
    
    // Shuffle defeated robots into next floor
    G.defeatedRobotsThisFloor.forEach(robotId => {
      nextFloorDeck.push(robotId);
    });
    
    // Shuffle the next floor's deck to mix in the new robots
    shuffle(nextFloorDeck);
    
    console.log(`[FLOOR] Shuffled ${G.defeatedRobotsThisFloor.length} robots into Floor ${G.level + 1}`);
    console.log(`[FLOOR] Floor ${G.level + 1} now has ${nextFloorDeck.length} cards`);
    
    // Reset defeated robots tracker
    G.defeatedRobotsThisFloor = [];
  }
}
```

---

## Elevator & Floor Progression

### Using the Elevator

**Available:** Only after elevator card is found (Floor 1 only)

**Behavior:**
- Can be used at any time in loot phase after unlocking
- Moves to next floor (if floors remain)
- If on Floor 5 and deck is empty: VICTORY!

**Implementation:**

```javascript
useElevator: ({ G, effects, events }) => {
  console.log(`[ELEVATOR] Using elevator from Floor ${G.level}`);
  
  if (!G.elevatorUnlocked) {
    effects.showMessage('Elevator not unlocked yet!');
    return;
  }
  
  // If current floor deck not empty, complete it first
  if (G.floorDecks[G.level].length > 0) {
    completeFloor(G);
  }
  
  // Check if game is complete (Floor 5 finished)
  if (G.level >= 5) {
    G.gameComplete = true;
    G.lastMessage = 'Victory! You completed all 5 floors!';
    console.log('[ELEVATOR] GAME COMPLETE!');
    return;
  }
  
  // Move to next floor
  G.level += 1;
  console.log(`[ELEVATOR] Moving to Floor ${G.level}`);
  effects.showMessage(`Arriving at Floor ${G.level}...`);
  
  // Draw first encounter of new floor
  const encounter = drawEncounter(G);
  processEncounter(G, encounter);
  
  // Transition to combat or stay in loot
  if (G.robots.length > 0) {
    events.setPhase('combat');
  } else {
    claimPendingEquipment(G);
    // Stay in loot phase
  }
},
```

### Auto-Progression (Alternative: No Elevator After Floor 1)

**Option:** After Floor 1, elevator is always available (no need to find again)

**Rationale:** Elevator is a Floor 1 mechanic to teach players. After that, it's a simple "Continue to Next Floor" button.

**Implementation:**
- Set `G.elevatorUnlocked = true` permanently after first unlock
- Or rename button to "Next Floor" after Floor 1

---

## Victory Condition

**Win Condition:** Complete Floor 5 (deck empty + use elevator)

**Victory Screen Shows:**
- Congratulations message
- Total orbs collected: `Object.values(G.orbsCollected).filter(Boolean).length` (should be 5)
- Final HP: `G.player.hp`
- Equipment count: `Object.keys(G.player.equipment).length`
- "Play Again" button

---

## Balancing Notes

### Expected Progression

**Floor 1:**
- 9 cards + elevator = 10 total
- 5 encounters (2 cards each, elevator guarantees 5th encounter)
- ~5 robots defeated → Floor 2 becomes ~15 cards

**Floor 2:**
- 10 base + ~5 recycled = 15 total
- 7-8 encounters (2 cards each)
- ~9 robots defeated → Floor 3 becomes ~19 cards

**Floor 3:**
- 10 base + ~9 recycled = 19 total
- 6-7 encounters (3 cards each)
- ~13 robots defeated → Floor 4 becomes ~23 cards

**Floor 4:**
- 10 base + ~13 recycled = 23 total
- 7-8 encounters (3 cards each)
- ~16 robots defeated → Floor 5 becomes ~26 cards

**Floor 5:**
- 10 base + ~16 recycled = 26 total
- 6-7 encounters (4 cards each)
- FINAL GAUNTLET!

### Player Power Curve

**Floor 1:** 
- Starting equipment only (charger + blaster)
- Learn enemy patterns
- Collect first few equipment cards

**Floor 2:**
- ~4-5 equipment cards
- Start building strategy
- Upgrade a few key items

**Floor 3:**
- ~7-8 equipment cards
- Build is coming together
- Should have upgraded weapon + utility

**Floor 4-5:**
- ~9-11 equipment cards
- Full build realized
- Need full build to handle massive robot encounters

---

## Implementation Steps

### Phase 1: Core Multi-Floor System
1. [ ] Add `floorDecks` object to G (5 floor decks)
2. [ ] Add `defeatedRobotsThisFloor` array to G
3. [ ] Implement `createFloorDeck(floorNumber, includeElevator)` helper
4. [ ] Update `initializeGame()` to create all 5 floor decks
5. [ ] Update `drawEncounter()` to use `G.floorDecks[G.level]` instead of `G.currentLevelDeck`
6. [ ] Test: Verify 5 decks are created correctly

### Phase 2: Robot Recycling
7. [ ] Update `removeDeadRobots()` in `combat.js` to add defeated robots to `defeatedRobotsThisFloor`
8. [ ] Implement `completeFloor()` helper function
9. [ ] Call `completeFloor()` when floor deck becomes empty (in loot phase)
10. [ ] Test: Verify robots from Floor 1 appear in Floor 2 deck

### Phase 3: Floor Progression
11. [ ] Update `useElevator` move to handle floor progression
12. [ ] Add check for victory condition (Floor 5 complete)
13. [ ] Update cards-per-encounter logic (1-2: 2 cards, 3-4: 3 cards, 5: 4 cards)
14. [ ] Test: Play through all 5 floors

### Phase 4: Polish & Balance
15. [ ] Add floor transition messages ("Arriving at Floor 2...")
16. [ ] Update victory screen to show all stats
17. [ ] Test full game loop multiple times
18. [ ] Adjust deck compositions if too hard/easy
19. [ ] Adjust card-per-encounter numbers if pacing is off

### Phase 5: UI Updates
20. [ ] Add floor indicator to UI ("Floor 3/5")
21. [ ] Show orb collection status (5 orbs total)
22. [ ] Update elevator button text after Floor 1 ("Next Floor" instead of "Use Elevator")
23. [ ] Add deck size indicator ("12 cards remaining")

---

## Edge Cases & Considerations

### What if player uses elevator before floor deck is empty?
**Answer:** Allow it! Player can skip remaining encounters to progress faster (risk/reward: miss equipment but save HP)

**Implementation:** Just call `completeFloor()` when elevator is used, regardless of deck state

### What if player dies on Floor 3?
**Answer:** Game Over screen → "Try Again" button → Restart from Floor 1 (full reset)

**No checkpoints** - This is a roguelike, death means restart

### What if Floor 5 gets too long?
**Answer:** Add a cap to prevent infinite growth

**Option:** Max 30 cards per floor deck (stop recycling if deck exceeds cap)

```javascript
function completeFloor(G) {
  // ... existing code ...
  
  // Only recycle if next floor won't exceed max size
  const MAX_DECK_SIZE = 30;
  if (nextFloorDeck.length + G.defeatedRobotsThisFloor.length <= MAX_DECK_SIZE) {
    // Shuffle in robots
  } else {
    console.log('[FLOOR] Next floor at max capacity, robots discarded');
  }
}
```

### What about robot diversity in later floors?
**Answer:** Weighted random selection when creating initial decks

**Option:** Prefer less-common robots in later floors (more variety)

Not critical for MVP, can tune later.

---

## Testing Plan

### Test 1: Initial Setup
- [ ] Start game → Verify 5 floor decks created
- [ ] Check each deck has correct card counts
- [ ] Verify Floor 1 has elevator in bottom half

### Test 2: Floor 1 Progression
- [ ] Play through Floor 1 encounters
- [ ] Verify robots defeated are tracked
- [ ] Reach end of Floor 1 deck → Verify orb awarded
- [ ] Check Floor 2 deck has recycled robots from Floor 1

### Test 3: Multi-Floor Progression
- [ ] Use elevator to go to Floor 2
- [ ] Verify Floor 2 deck includes recycled robots
- [ ] Play through Floors 2-3-4-5
- [ ] Verify each floor gets progressively longer

### Test 4: Victory Condition
- [ ] Complete Floor 5
- [ ] Use elevator
- [ ] Verify victory screen appears
- [ ] Check stats are correct (5 orbs, etc.)

### Test 5: Edge Cases
- [ ] Use elevator before Floor 1 deck is empty (skip encounters)
- [ ] Die on Floor 3 → Verify restart works
- [ ] Check max deck size cap (if implemented)

---

## Future Enhancements (Post-MVP)

### Difficulty Scaling
- [ ] Robot stats boost per floor (+1 HP per floor, +1 ATK every 2 floors)
- [ ] Boss encounters (special robot on Floor 5)
- [ ] Floor hazards/modifiers

### Meta Progression
- [ ] Permanent unlocks between runs
- [ ] Starting equipment choices
- [ ] Difficulty modes (easy/normal/hard)

### Content Expansion
- [ ] More robot types (20+ total)
- [ ] More equipment cards (30+ total)
- [ ] Special floor-specific cards

---

## Summary

This 5-floor system with robot recycling creates:
1. **Natural escalation** - Later floors get longer and harder without artificial scaling
2. **Strategic depth** - Players learn enemy patterns early and build accordingly
3. **Satisfying finale** - Floor 5 is a massive gauntlet that tests the player's full build
4. **Replayability** - Random deck generation + recycling = different every time

The implementation is straightforward:
- Create 5 decks at start (respecting minFloor)
- Track defeated robots
- Shuffle into next floor when current floor completes
- Elevator advances floors, victory at Floor 5 completion

Let's build it! 🚀
