docs: update spire-climbing-spec with non-combat room mechanics (recovery, treasure, library, puzzle)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m3s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m3s
This commit is contained in:
@@ -45,6 +45,9 @@ that need modification are noted separately.
|
|||||||
| `enterDescentMode()` | combatStore | **NEW** — snapshot peak floor/room, set `climbDirection = 'down'` |
|
| `enterDescentMode()` | combatStore | **NEW** — snapshot peak floor/room, set `climbDirection = 'down'` |
|
||||||
| `advanceRoomOrFloor()` | combatStore | **NEW** — move to next room/floor (ascending) or previous room/floor (descending) |
|
| `advanceRoomOrFloor()` | combatStore | **NEW** — move to next room/floor (ascending) or previous room/floor (descending) |
|
||||||
| `processCombatTick(...)` | combatStore | **MODIFY** — must become room-aware (see §4.4) |
|
| `processCombatTick(...)` | combatStore | **MODIFY** — must become room-aware (see §4.4) |
|
||||||
|
| `tickNonCombatRoom(hours)` | combatStore | **NEW** — tick non-combat room progress (library, recovery, treasure, puzzle) |
|
||||||
|
| `skipNonCombatRoom()` | combatStore | **NEW** — skip to next room (library, recovery, treasure only) |
|
||||||
|
| `stayLongerInRoom()` | combatStore | **NEW** — extend current room by 1 hour (library, recovery only, once per room) |
|
||||||
|
|
||||||
> **Removed vs. original draft:** `skipClearedRoom`, `markFloorReset`, `setCurrentRoom`,
|
> **Removed vs. original draft:** `skipClearedRoom`, `markFloorReset`, `setCurrentRoom`,
|
||||||
> `setClearedFloor`, and `initGuardianDefensiveState` are **not needed as separate public
|
> `setClearedFloor`, and `initGuardianDefensiveState` are **not needed as separate public
|
||||||
@@ -89,10 +92,11 @@ src/lib/game/stores/
|
|||||||
roomResetState, exitFloor fields
|
roomResetState, exitFloor fields
|
||||||
combatStore.ts — add enterDescentMode(), advanceRoomOrFloor()
|
combatStore.ts — add enterDescentMode(), advanceRoomOrFloor()
|
||||||
combat-actions.ts — make processCombatTick room-aware
|
combat-actions.ts — make processCombatTick room-aware
|
||||||
|
combat-descent-actions.ts — add non-combat room handlers (recovery, treasure, library, puzzle)
|
||||||
|
|
||||||
src/lib/game/utils/
|
src/lib/game/utils/
|
||||||
spire-utils.ts — ensure getRoomsForFloor accepts a seed
|
spire-utils.ts — ensure getRoomsForFloor accepts a seed; add generateTreasureLoot()
|
||||||
room-utils.ts — add generateSpireRoomType(), library XP helper
|
room-utils.ts — add generateSpireRoomType()
|
||||||
|
|
||||||
src/components/game/tabs/
|
src/components/game/tabs/
|
||||||
SpireCombatPage/
|
SpireCombatPage/
|
||||||
@@ -180,10 +184,10 @@ So across all rooms: ~40% of 10% = **~4% recovery**, ~30% of 10% = **~3% treasur
|
|||||||
| `swarm` | ~12% | 3–7 weak enemies |
|
| `swarm` | ~12% | 3–7 weak enemies |
|
||||||
| `speed` | ~10% | Single enemy with elevated dodge chance |
|
| `speed` | ~10% | Single enemy with elevated dodge chance |
|
||||||
| `guardian` | Every 10th floor, 1 room | Boss — high HP, shield, barrier, health regen |
|
| `guardian` | Every 10th floor, 1 room | Boss — high HP, shield, barrier, health regen |
|
||||||
| `recovery` | ~4% | No enemies; restores a portion of current mana pool |
|
| `recovery` | ~4% | No enemies; 1 hour; grants 10× mana regen & conversion rates for all unlocked mana types (see §4.8) |
|
||||||
| `treasure` | ~3% | No enemies; grants loot / equipment drop |
|
| `treasure` | ~3% | No enemies; 1 hour; grants 2–15 random items (mostly fabricator materials, rarely pre-crafted gear), scaling with floor (see §4.9) |
|
||||||
| `library` | ~3% | No enemies; grants discipline XP scaled to current floor (see §4.8) |
|
| `library` | ~3% | No enemies; 1 hour; grants discipline XP at 25× normal rate to a random unlocked discipline (see §4.10) |
|
||||||
| `puzzle` | ~1 per 7 floors | Attunement-based challenge; no combat |
|
| `puzzle` | ~1 per 7 floors | Attunement-based challenge; up to 24 hours base time, reduced by attunement levels (see §4.11) |
|
||||||
|
|
||||||
**Speed room interaction:** A `speed` room combined with an enemy that also has the
|
**Speed room interaction:** A `speed` room combined with an enemy that also has the
|
||||||
`agile` modifier results in an **additive dodge bonus** on top of the agile modifier
|
`agile` modifier results in an **additive dodge bonus** on top of the agile modifier
|
||||||
@@ -191,8 +195,9 @@ value. See combat spec §2.3 for modifier details.
|
|||||||
|
|
||||||
### 4.4 Ascending — Room and Floor Advancement
|
### 4.4 Ascending — Room and Floor Advancement
|
||||||
|
|
||||||
Rooms advance **automatically** when all enemies in the current room reach 0 HP
|
Rooms advance **automatically** when all enemies in the current room reach 0 HP.
|
||||||
(or immediately for non-combat rooms). The player does not press a button.
|
Non-combat rooms advance when their timed progression completes (or when the player
|
||||||
|
presses "Skip"). The player does not press a button for combat rooms.
|
||||||
|
|
||||||
```
|
```
|
||||||
advanceRoomOrFloor() [direction = 'up']:
|
advanceRoomOrFloor() [direction = 'up']:
|
||||||
@@ -214,8 +219,11 @@ advanceRoomOrFloor() [direction = 'up']:
|
|||||||
resetCastProgress()
|
resetCastProgress()
|
||||||
```
|
```
|
||||||
|
|
||||||
Non-combat rooms (recovery, treasure, library, puzzle) trigger `advanceRoomOrFloor()`
|
Non-combat rooms (recovery, treasure, library, puzzle) initialize timed progression
|
||||||
immediately on entry after applying their effect.
|
on entry. When progress reaches the required amount, `advanceRoomOrFloor()` is called
|
||||||
|
automatically. The player can press "Skip" to advance immediately (library, recovery,
|
||||||
|
treasure) or press "Stay 1 Hour More" (library, recovery only) to extend the time.
|
||||||
|
Puzzle rooms are mandatory — no skip or stay buttons.
|
||||||
|
|
||||||
### 4.5 Descent Initiation
|
### 4.5 Descent Initiation
|
||||||
|
|
||||||
@@ -292,26 +300,234 @@ Guardian rooms that reset on descent re-initialize the full guardian defensive s
|
|||||||
(shield pool, barrier %, health regen) as if the player is fighting the guardian for
|
(shield pool, barrier %, health regen) as if the player is fighting the guardian for
|
||||||
the first time.
|
the first time.
|
||||||
|
|
||||||
### 4.8 Library Rooms — Discipline XP
|
### 4.8 Recovery Rooms — Boosted Mana Regen & Conversion
|
||||||
|
|
||||||
|
When a `recovery` room is entered:
|
||||||
|
|
||||||
|
```
|
||||||
|
onEnterRecoveryRoom(floor):
|
||||||
|
recoveryProgress = 0
|
||||||
|
recoveryRequired = 1 // 1 hour
|
||||||
|
recoveryStayed = false
|
||||||
|
activityLog("Entered recovery room on Floor ${floor}")
|
||||||
|
// Do NOT call advanceRoomOrFloor() — wait for progress to complete
|
||||||
|
```
|
||||||
|
|
||||||
|
**Effect:** While in the recovery room, the player receives a **10× multiplier** to:
|
||||||
|
- **Mana regeneration rate** for all unlocked mana types (e.g., 1 raw/hour → 10 raw/hour)
|
||||||
|
- **Mana conversion efficiency** for all unlocked mana types (e.g., 10 raw → 1 transference/hour becomes 10 raw → 10 transference/hour)
|
||||||
|
|
||||||
|
The multiplier is applied through the mana store for the duration of the room.
|
||||||
|
|
||||||
|
**UI:**
|
||||||
|
- Progress bar showing time elapsed / 1 hour
|
||||||
|
- Thematic text: *"Resting and recovering in a mana-rich chamber"*
|
||||||
|
- **"Stay 1 Hour More" button** (once only) — adds 1 more hour to `recoveryRequired`, disabled after use
|
||||||
|
- **"Skip" button** — calls `advanceRoomOrFloor()` immediately
|
||||||
|
|
||||||
|
**Activity log events:**
|
||||||
|
- `"Entered recovery room on Floor {N}"`
|
||||||
|
- `"Recovery complete — mana regen and conversion boosted"`
|
||||||
|
|
||||||
|
### 4.9 Treasure Rooms — Loot
|
||||||
|
|
||||||
|
When a `treasure` room is entered:
|
||||||
|
|
||||||
|
```
|
||||||
|
onEnterTreasureRoom(floor):
|
||||||
|
treasureProgress = 0
|
||||||
|
treasureRequired = 1 // 1 hour
|
||||||
|
treasureLoot = generateTreasureLoot(floor)
|
||||||
|
treasureLootClaimed = []
|
||||||
|
activityLog("Entered treasure room on Floor ${floor}")
|
||||||
|
// Do NOT call advanceRoomOrFloor() — wait for progress to complete
|
||||||
|
```
|
||||||
|
|
||||||
|
**Loot generation** (`generateTreasureLoot`):
|
||||||
|
|
||||||
|
```
|
||||||
|
generateTreasureLoot(floor):
|
||||||
|
// 1. Determine item count based on floor:
|
||||||
|
// - Floors 1–10: 2–3 items
|
||||||
|
// - Floors 10–50: 4–7 items
|
||||||
|
// - Floors 50+: 8–15 items
|
||||||
|
// 2. For each item slot:
|
||||||
|
// - 85%+ chance: fabricator material (from LOOT_DROPS, filtered by minFloor)
|
||||||
|
// - ~15% chance: pre-crafted equipment (rare, higher floors only)
|
||||||
|
// 3. Weight by dropChance; higher floors get access to better items
|
||||||
|
// 4. Return array of LootDrop with amounts
|
||||||
|
```
|
||||||
|
|
||||||
|
**Loot delivery:** Items are granted progressively as the hour elapses:
|
||||||
|
- At **10%** progress: first item(s) granted
|
||||||
|
- At **50%** progress: mid-tier items granted
|
||||||
|
- At **95%** progress: more items granted
|
||||||
|
- At **100%** progress: final and best item(s) granted
|
||||||
|
|
||||||
|
Each item is added to the player's loot inventory and logged in the activity log.
|
||||||
|
|
||||||
|
**UI:**
|
||||||
|
- Progress bar showing time elapsed / 1 hour
|
||||||
|
- Thematic text: *"Rummaging through ancient chests and caches"*
|
||||||
|
- **"Skip" button** — calls `advanceRoomOrFloor()` immediately (forfeits remaining loot)
|
||||||
|
|
||||||
|
**Activity log events:**
|
||||||
|
- `"Entered treasure room on Floor {N}"`
|
||||||
|
- `"Found {itemName} x{amount}"` (for each item as it's granted)
|
||||||
|
- `"Treasure room looted — {count} items recovered"`
|
||||||
|
|
||||||
|
### 4.10 Library Rooms — Discipline XP
|
||||||
|
|
||||||
When a `library` room is entered:
|
When a `library` room is entered:
|
||||||
|
|
||||||
```
|
```
|
||||||
onEnterLibraryRoom(floor):
|
onEnterLibraryRoom(floor):
|
||||||
discipline = pickRandom(allActiveDisciplines)
|
discipline = pickRandom(allUnlockedDisciplines)
|
||||||
xpGrant = BASE_LIBRARY_XP × (1 + floor / 10)
|
libraryProgress = 0
|
||||||
discipline.addXP(xpGrant)
|
libraryRequired = 1 // 1 hour
|
||||||
activityLog("Ancient tome studied — ${discipline.name} gained ${xpGrant} XP")
|
libraryStayed = false
|
||||||
|
activityLog("Entered library room on Floor ${floor}")
|
||||||
|
// Do NOT call advanceRoomOrFloor() — wait for progress to complete
|
||||||
|
```
|
||||||
|
|
||||||
|
**Effect:** While in the library room, the selected discipline gains XP at **25× the
|
||||||
|
normal rate**. XP is granted continuously over the hour (not a lump sum). No mana cost.
|
||||||
|
|
||||||
|
- Target discipline is chosen randomly from all **unlocked** disciplines (not just active ones).
|
||||||
|
- If no disciplines are unlocked, nothing happens (edge case — player should always have at least one).
|
||||||
|
|
||||||
|
**UI:**
|
||||||
|
- Progress bar showing time elapsed / 1 hour
|
||||||
|
- Thematic text: *"Studying Mana Circulation from ancient tomes"*
|
||||||
|
- **"Stay 1 Hour More" button** (once only) — adds 1 more hour to `libraryRequired`, disabled after use
|
||||||
|
- **"Skip" button** — calls `advanceRoomOrFloor()` immediately
|
||||||
|
|
||||||
|
**Activity log events:**
|
||||||
|
- `"Entered library room on Floor {N}"`
|
||||||
|
- `"{Discipline} gained {XP} XP from ancient tomes"` (continuous, logged periodically)
|
||||||
|
- `"Library study complete"`
|
||||||
|
|
||||||
|
### 4.11 Puzzle Rooms — Attunement Challenge
|
||||||
|
|
||||||
|
When a `puzzle` room is entered:
|
||||||
|
|
||||||
|
```
|
||||||
|
onEnterPuzzleRoom(floor, puzzleId):
|
||||||
|
puzzleProgress = 0
|
||||||
|
puzzleRequired = calcPuzzleTime(floor, puzzleId)
|
||||||
|
activityLog("Entered puzzle room on Floor ${floor}")
|
||||||
|
// Do NOT call advanceRoomOrFloor() — wait for progress to complete
|
||||||
|
```
|
||||||
|
|
||||||
|
**Base time calculation** (scales with floor):
|
||||||
|
|
||||||
|
```
|
||||||
|
calcPuzzleBaseTime(floor):
|
||||||
|
if floor <= 20: return 4 // 4 hours
|
||||||
|
if floor <= 50: return 8 // 8 hours
|
||||||
|
if floor <= 100: return 16 // 16 hours
|
||||||
|
return 24 // 24 hours max
|
||||||
|
```
|
||||||
|
|
||||||
|
**Attunement-based time reduction:**
|
||||||
|
|
||||||
|
Each puzzle is associated with 1 or more attunements (defined in `PUZZLE_ROOMS`).
|
||||||
|
The player's attunement levels reduce the required time:
|
||||||
|
|
||||||
|
```
|
||||||
|
calcPuzzleTime(floor, puzzleId):
|
||||||
|
base = calcPuzzleBaseTime(floor)
|
||||||
|
puzzle = PUZZLE_ROOMS[puzzleId]
|
||||||
|
attunements = puzzle.attunements // e.g., ['enchanter'] or ['enchanter', 'invoker']
|
||||||
|
|
||||||
|
totalReduction = 0
|
||||||
|
for each attunementId in attunements:
|
||||||
|
attLevel = getAttunementLevel(attunementId)
|
||||||
|
maxLevel = getMaxAttunementLevel()
|
||||||
|
// Each attunement contributes up to (1 / attunements.length) * 0.90 reduction
|
||||||
|
share = 1 / attunements.length
|
||||||
|
reduction = share * 0.90 * (attLevel / maxLevel)
|
||||||
|
totalReduction += reduction
|
||||||
|
|
||||||
|
return base * (1 - totalReduction)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- Single-attunement puzzle (enchanter trial), max enchanter level: `base × (1 - 0.90) = base × 0.10` (90% reduction)
|
||||||
|
- Dual-attunement puzzle (enchanter + invoker), max both levels: `base × (1 - 0.45 - 0.45) = base × 0.10` (90% reduction)
|
||||||
|
- Dual-attunement puzzle, max enchanter only: `base × (1 - 0.45) = base × 0.55` (45% reduction from enchanter, 0% from invoker)
|
||||||
|
|
||||||
|
**UI:**
|
||||||
|
- Progress bar showing time elapsed / total time
|
||||||
|
- Thematic text based on puzzle type:
|
||||||
|
- Enchanter puzzle: *"Deciphering an enchanted lock"*
|
||||||
|
- Fabricator puzzle: *"Disassembling a mana-powered mechanism"*
|
||||||
|
- Invoker puzzle: *"Communing with residual guardian spirits"*
|
||||||
|
- Hybrid puzzle: *"Working through a complex attunement challenge"*
|
||||||
|
- **No "Skip" or "Stay" buttons** — puzzle rooms are mandatory
|
||||||
|
|
||||||
|
**Activity log events:**
|
||||||
|
- `"Entered puzzle room on Floor {N} — {puzzleName}"`
|
||||||
|
- `"Puzzle solved!"`
|
||||||
|
|
||||||
|
### 4.12 Non-Combat Room Tick Processing
|
||||||
|
|
||||||
|
Every game tick, if the current room is non-combat:
|
||||||
|
|
||||||
|
```
|
||||||
|
tickNonCombatRoom(hours):
|
||||||
|
room = currentRoom
|
||||||
|
|
||||||
|
if room.roomType === 'library':
|
||||||
|
room.libraryProgress += hours
|
||||||
|
xpThisTick = calcDisciplineXPRate(discipline) × 25 × hours
|
||||||
|
discipline.addXP(xpThisTick)
|
||||||
|
if room.libraryProgress >= room.libraryRequired:
|
||||||
|
advanceRoomOrFloor()
|
||||||
|
|
||||||
|
else if room.roomType === 'recovery':
|
||||||
|
room.recoveryProgress += hours
|
||||||
|
// 10× regen/conversion is applied passively via mana store flags
|
||||||
|
if room.recoveryProgress >= room.recoveryRequired:
|
||||||
|
advanceRoomOrFloor()
|
||||||
|
|
||||||
|
else if room.roomType === 'treasure':
|
||||||
|
room.treasureProgress += hours
|
||||||
|
// Check loot thresholds and grant items
|
||||||
|
progressPct = room.treasureProgress / room.treasureRequired
|
||||||
|
for each lootItem in room.treasureLoot:
|
||||||
|
if not claimed and progressPct >= lootItem.threshold:
|
||||||
|
grantLoot(lootItem)
|
||||||
|
activityLog("Found ${lootItem.name}")
|
||||||
|
if room.treasureProgress >= room.treasureRequired:
|
||||||
|
advanceRoomOrFloor()
|
||||||
|
|
||||||
|
else if room.roomType === 'puzzle':
|
||||||
|
room.puzzleProgress += hours
|
||||||
|
if room.puzzleProgress >= room.puzzleRequired:
|
||||||
|
activityLog("Puzzle solved!")
|
||||||
advanceRoomOrFloor()
|
advanceRoomOrFloor()
|
||||||
```
|
```
|
||||||
|
|
||||||
- `BASE_LIBRARY_XP` is a tunable constant (suggested starting value: 50).
|
**Player actions during non-combat rooms:**
|
||||||
- XP is granted to a **random active discipline** (one of the player's currently
|
|
||||||
running disciplines).
|
|
||||||
- If no disciplines are active, XP is granted to a random unlocked discipline.
|
|
||||||
- The grant scales linearly with floor: floor 10 = 2×, floor 20 = 3×, etc.
|
|
||||||
|
|
||||||
### 4.9 Exiting the Spire
|
```
|
||||||
|
skipNonCombatRoom():
|
||||||
|
// Only for library, recovery, treasure
|
||||||
|
if currentRoom.roomType in ['library', 'recovery', 'treasure']:
|
||||||
|
advanceRoomOrFloor()
|
||||||
|
|
||||||
|
stayLongerInRoom():
|
||||||
|
// Only for library and recovery, once per room
|
||||||
|
if currentRoom.roomType === 'library' and not libraryStayed:
|
||||||
|
libraryRequired += 1
|
||||||
|
libraryStayed = true
|
||||||
|
else if currentRoom.roomType === 'recovery' and not recoveryStayed:
|
||||||
|
recoveryRequired += 1
|
||||||
|
recoveryStayed = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.13 Exiting the Spire
|
||||||
|
|
||||||
The "Exit Spire" button is visible **only** when:
|
The "Exit Spire" button is visible **only** when:
|
||||||
- `isDescentComplete === true`
|
- `isDescentComplete === true`
|
||||||
@@ -348,7 +564,16 @@ Every meaningful state change appends an entry to the spire activity log. Requir
|
|||||||
| Floor ascended | `"Ascending to Floor {N}"` |
|
| Floor ascended | `"Ascending to Floor {N}"` |
|
||||||
| Floor descended | `"Descended to Floor {N}"` |
|
| Floor descended | `"Descended to Floor {N}"` |
|
||||||
| Non-combat room entered | `"Entered {roomType} room on Floor {N}"` |
|
| Non-combat room entered | `"Entered {roomType} room on Floor {N}"` |
|
||||||
| Library XP granted | `"{Discipline} gained {XP} XP from ancient tome"` |
|
| Library XP granted | `"{Discipline} gained {XP} XP from ancient tomes"` (continuous, logged periodically) |
|
||||||
|
| Library study complete | `"Library study complete"` |
|
||||||
|
| Recovery entered | `"Entered recovery room on Floor {N}"` |
|
||||||
|
| Recovery complete | `"Recovery complete — mana regen and conversion boosted"` |
|
||||||
|
| Treasure entered | `"Entered treasure room on Floor {N}"` |
|
||||||
|
| Treasure item found | `"Found {itemName} x{amount}"` (per item as granted) |
|
||||||
|
| Treasure room complete | `"Treasure room looted — {count} items recovered"` |
|
||||||
|
| Puzzle entered | `"Entered puzzle room on Floor {N} — {puzzleName}"` |
|
||||||
|
| Puzzle solved | `"Puzzle solved!"` |
|
||||||
|
| Stay longer activated | `"Decided to stay longer in {roomType} room"` |
|
||||||
| Descent initiated | `"Beginning descent from Floor {N} Room {R}"` |
|
| Descent initiated | `"Beginning descent from Floor {N} Room {R}"` |
|
||||||
| Descent complete | `"Descent complete — Exit Spire is now available"` |
|
| Descent complete | `"Descent complete — Exit Spire is now available"` |
|
||||||
| Exit spire | `"Exited the Spire"` |
|
| Exit spire | `"Exited the Spire"` |
|
||||||
@@ -374,6 +599,10 @@ descentPeak: { floor: number; roomIndex: number } | null
|
|||||||
roomResetState: Record<string, boolean> // key = "floor:roomIndex"
|
roomResetState: Record<string, boolean> // key = "floor:roomIndex"
|
||||||
clearedRooms: Record<string, boolean> // key = "floor:roomIndex"
|
clearedRooms: Record<string, boolean> // key = "floor:roomIndex"
|
||||||
isDescentComplete: boolean
|
isDescentComplete: boolean
|
||||||
|
|
||||||
|
// Non-combat room tracking (climbing spec §4.8–§4.12)
|
||||||
|
libraryStayed: boolean // true if player already used "Stay 1 Hour More" in current library room
|
||||||
|
recoveryStayed: boolean // true if player already used "Stay 1 Hour More" in current recovery room
|
||||||
```
|
```
|
||||||
|
|
||||||
> `isDescending: boolean` (legacy alias) can be removed in favour of `climbDirection === 'down'`.
|
> `isDescending: boolean` (legacy alias) can be removed in favour of `climbDirection === 'down'`.
|
||||||
@@ -399,8 +628,11 @@ isDescentComplete: boolean
|
|||||||
3. `advanceRoomOrFloor` ascending — increments roomIndex; on last room, increments floor and resets roomIndex to 0.
|
3. `advanceRoomOrFloor` ascending — increments roomIndex; on last room, increments floor and resets roomIndex to 0.
|
||||||
4. `advanceRoomOrFloor` descending — decrements roomIndex; at roomIndex 0, moves to previous floor at `roomsPerFloor - 1`; at exitFloor R0, sets `isDescentComplete`.
|
4. `advanceRoomOrFloor` descending — decrements roomIndex; at roomIndex 0, moves to previous floor at `roomsPerFloor - 1`; at exitFloor R0, sets `isDescentComplete`.
|
||||||
5. Per-room reset — each room rolls independently; two rooms on the same floor can have different outcomes.
|
5. Per-room reset — each room rolls independently; two rooms on the same floor can have different outcomes.
|
||||||
6. Library XP — grant scales with floor; targets an active discipline.
|
6. Library room — takes 1 hour, grants 25× XP to random unlocked discipline, stay button works once, skip button works.
|
||||||
7. `spireKey` — `startFloor` and `exitFloor` correctly reflect `1 + spireKey × 2`.
|
7. Recovery room — takes 1 hour, grants 10× regen/conversion, stay button works once, skip button works.
|
||||||
|
8. Treasure room — takes 1 hour, grants 2–15 items scaling with floor, loot logged, skip button works.
|
||||||
|
9. Puzzle room — base time scales with floor (4–24h), attunement reduction up to 90%, mandatory (no skip/stay).
|
||||||
|
10. `spireKey` — `startFloor` and `exitFloor` correctly reflect `1 + spireKey × 2`.
|
||||||
|
|
||||||
### Integration Tests
|
### Integration Tests
|
||||||
|
|
||||||
@@ -412,7 +644,10 @@ isDescentComplete: boolean
|
|||||||
|
|
||||||
## 9. Boundaries / Out of Scope
|
## 9. Boundaries / Out of Scope
|
||||||
|
|
||||||
- Loot generation inside treasure rooms (drop tables are a separate system).
|
- Visual animations for loot drops or room transitions.
|
||||||
|
- Sound effects.
|
||||||
|
- New loot drop definitions (use existing `LOOT_DROPS` data).
|
||||||
|
- New puzzle definitions (use existing `PUZZLE_ROOMS` data).
|
||||||
- Golem summoning lifecycle (see combat spec §6).
|
- Golem summoning lifecycle (see combat spec §6).
|
||||||
- DoT / debuff runtime processing (see combat spec §5).
|
- DoT / debuff runtime processing (see combat spec §5).
|
||||||
- Incursion's effect on mana regen during spire (handled in manaStore, not here).
|
- Incursion's effect on mana regen during spire (handled in manaStore, not here).
|
||||||
@@ -433,7 +668,13 @@ isDescentComplete: boolean
|
|||||||
| AC-6 | Descent traverses rooms in exact reverse (R_max → R0 per floor, then floor-1). |
|
| AC-6 | Descent traverses rooms in exact reverse (R_max → R0 per floor, then floor-1). |
|
||||||
| AC-7 | Each room on descent rolls its reset independently (50%); two rooms on the same floor can differ. |
|
| AC-7 | Each room on descent rolls its reset independently (50%); two rooms on the same floor can differ. |
|
||||||
| AC-8 | Skipped rooms (no reset) log an activity entry and auto-advance immediately. |
|
| AC-8 | Skipped rooms (no reset) log an activity entry and auto-advance immediately. |
|
||||||
| AC-9 | Library rooms grant discipline XP scaled by floor to a random active discipline and log the event. |
|
| AC-9 | Library room takes 1 hour, grants 25× XP to a random unlocked discipline, has skip + stay buttons. |
|
||||||
| AC-10 | "Exit Spire" is only visible when `isDescentComplete === true`. |
|
| AC-10 | Recovery room takes 1 hour, grants 10× mana regen and conversion rates for all unlocked types, has skip + stay buttons. |
|
||||||
| AC-11 | Guardian rooms that reset on descent re-initialize full guardian defensive state. |
|
| AC-11 | Treasure room takes 1 hour, grants 2–15 items scaling with floor (mostly materials, rare equipment), loot listed in activity log, has skip button. |
|
||||||
| AC-12 | Activity log contains an entry for every room skip, reset, clear, floor transition, and spire entry/exit. |
|
| AC-12 | Puzzle room takes up to 24 hours (floor-scaled), reduced by attunement levels (up to 90% reduction), no skip/stay buttons, mandatory completion. |
|
||||||
|
| AC-13 | All non-combat rooms show a progress bar with thematic description text. |
|
||||||
|
| AC-14 | "Stay 1 Hour More" button works once per library/recovery room, then disables. |
|
||||||
|
| AC-15 | "Skip" button on library/recovery/treasure advances immediately. |
|
||||||
|
| AC-16 | "Exit Spire" is only visible when `isDescentComplete === true`. |
|
||||||
|
| AC-17 | Guardian rooms that reset on descent re-initialize full guardian defensive state. |
|
||||||
|
| AC-18 | Activity log contains an entry for every room skip, reset, clear, floor transition, non-combat room event, and spire entry/exit. |
|
||||||
Reference in New Issue
Block a user