From fa78c7a93a393a8ca0d397c7ced01a0e7fe2650d Mon Sep 17 00:00:00 2001
From: n8n-gitea
Date: Mon, 1 Jun 2026 13:54:28 +0200
Subject: [PATCH] fix: bugs #238,#240,#244,#246 + docs #248 update
- #238: Fix spire tab inconsistent state (Max Floor 1 but Floors Cleared 0) by not inflating maxFloorReached on enterSpireMode and preserving it on exitSpireMode
- #240: Fix guardian armor display stray text by extracting stat formatters in SpireSummaryTab
- #244: Improve discipline auto-pause UX with log messages and visual feedback on DisciplineCard
- #246: Fix raw mana exceeding max cap by recomputing maxMana after discipline XP gains
- #248: Update AGENTS.md (remove gitea_get_project_boards, add gitea_start_session, 22 mana types, 8 stores, updated guardian tiers)
- #248: Update README.md (remove Prisma/SQLite refs, update mana types/guardian tiers/discipline counts)
- #248: Update GAME_BRIEFING.md (8 stores, 22 mana types, 64 disciplines, 8-tier guardians, correct code architecture)
---
AGENTS.md | 19 +-
README.md | 138 ++++----
docs/GAME_BRIEFING.md | 50 +--
docs/circular-deps.txt | 2 +-
docs/dependency-graph.json | 12 +-
docs/project-structure.txt | 1 +
src/components/game/tabs/DisciplineCard.tsx | 10 +-
src/components/game/tabs/DisciplinesTab.tsx | 10 +-
src/components/game/tabs/ElementalSubtab.tsx | 5 +-
.../game/tabs/SpireSummaryTab.helpers.tsx | 249 ++++++++++++++
src/components/game/tabs/SpireSummaryTab.tsx | 321 ++----------------
src/lib/game/stores/combatStore.ts | 9 +-
src/lib/game/stores/discipline-slice.ts | 12 +-
src/lib/game/stores/gameStore.ts | 15 +
src/lib/game/types/disciplines.ts | 2 +
15 files changed, 448 insertions(+), 407 deletions(-)
create mode 100644 src/components/game/tabs/SpireSummaryTab.helpers.tsx
diff --git a/AGENTS.md b/AGENTS.md
index d4e39ce..a0bd3c5 100755
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -24,13 +24,14 @@ git add -A && git commit -m "type: desc" && git push origin master
1. `docs/project-structure.txt`
2. `docs/dependency-graph.json`
-3. `gitea_get_project_boards` → resume in-progress or pick top todo
-4. `gitea_update_issue_status` → `ai_state: "in-progress"`
-5. Work, log with `gitea_add_comment`, then `gitea_update_issue_status` → `ai_state: "done"`
+3. `gitea_start_session` → retrieve active task registry and issues
+4. Evaluate the queue to find the highest-priority `ai_state: todo` item (or locate an existing `in-progress` task if resuming work)
+5. `gitea_update_issue_status` → `ai_state: "in-progress"`
+6. Work, log with `gitea_add_comment`, then `gitea_update_issue_status` → `ai_state: "done"`
## Labels
-`ai_state: todo` | `in_state: in-progress` | `ai_state: review` | `ai_state: blocked` | `ai_state: done`
+`ai_state: todo` | `ai_state: in-progress` | `ai_state: review` | `ai_state: blocked` | `ai_state: done`
## Terminal Tool
@@ -44,7 +45,7 @@ Use for 3+ sequential independent calls. Zero context from parent — paste ever
- **Stack:** Next.js 16, TS 5, Tailwind 4 + shadcn/ui, Zustand+persist, Vitest, Bun
- **No backend:** Pure client-side. No Prisma, no database. State persisted to localStorage.
-- **Active stores (7 Zustand stores):**
+- **Active stores (8 Zustand stores):**
- `useGameStore` — Coordinator/tick pipeline, imports all other stores
- `useManaStore` — Mana pools, regen, element conversion
- `useCombatStore` — Spire/floors, combat, spells, achievements
@@ -120,9 +121,9 @@ ManaDrainPerTick = drainBase × (1 + (XP / difficultyFactor)^0.4)
### Guardian System
- Guardians on every 10th floor
- **Base (floors 10–80):** 7 base elements + Transference, static definitions with unique names
-- **Compound (floors 90–110):** Metal, Sand, Lightning — procedurally named
-- **Exotic (floors 120–140):** Crystal, Stellar, Void — procedurally named
-- **Combination bosses (floor 150+):** Dual-element procedural guardians cycling through 9 element pairs, scaling indefinitely
+- **Tier 2 — Composite (floors 90–160):** 8 composite elements (Metal, Sand, Lightning, Frost, BlackFlame, RadiantFlames, Miasma, ShadowGlass)
+- **Tier 3 — Exotic (floors 170–240):** 6 exotic elements (Crystal, Stellar, Void, Soul, Time, Plasma)
+- **Tier 4+ — Procedural (floors 250+):** Dual-element → multi-element combination bosses cycling through element pairs, scaling indefinitely through 8 tiers
- HP formula: `floor(5000 × (floor/10) ^ (1.1 + floor/200))`
- Pact signing: costs raw mana + time, grants permanent boons
@@ -165,3 +166,5 @@ Lifesteal/healing, scroll crafting, ascension skills, LabTab, pause mechanics, f
**Utility (1):** Transference 🔗
**Composite (8):** Fire+Earth=Metal ⚙️, Earth+Water=Sand ⏳, Fire+Air=Lightning ⚡, Air+Water=Frost ❄️, Dark+Fire=BlackFlame 🌋, Light+Fire=Radiant Flames 🌟, Air+Death=Miasma ☁️, Earth+Dark=Shadow Glass 🖤
**Exotic (6):** Sand+Sand+Light=Crystal 💎, Plasma+Light+Fire=Stellar ⭐, Dark+Dark+Death=Void 🕳️, Light+Dark+Transference=Soul 💫, Soul+Sand+Transference=Time ⏱️, Lightning+Fire+Transference=Plasma ⚡
+
+**Total: 22 mana types** (7 base + 1 utility + 8 composite + 6 exotic)
diff --git a/README.md b/README.md
index 456a212..7f44e49 100755
--- a/README.md
+++ b/README.md
@@ -46,51 +46,57 @@
### Core Game Loop
-1. **Gather Mana** - Click to collect mana or let it regenerate automatically (14 total mana types)
-2. **Practice Disciplines** - Continuously train abilities that drain mana each tick in exchange for growing stat bonuses
-3. **Climb the Spire** - Battle through procedurally-generated floors; every 10th floor is a guardian encounter
-4. **Craft & Enchant** - 3-stage equipment enchantment system with capacity limits
-5. **Summon Golems** - Magical constructs that fight alongside you (4 base + 6 hybrid types)
-6. **Prestige (Loop)** - Reset progress for Insight currency, gain permanent bonuses
+1. **Gather Mana** — Click to collect mana or let it regenerate automatically (22 total mana types)
+2. **Practice Disciplines** — Continuously train abilities that drain mana each tick in exchange for growing stat bonuses
+3. **Climb the Spire** — Battle through procedurally-generated floors; every 10th floor is a guardian encounter
+4. **Craft & Enchant** — 3-stage equipment enchantment system with capacity limits
+5. **Summon Golems** — Magical constructs that fight alongside you (1 base + 3 elemental + 6 hybrid types)
+6. **Prestige (Loop)** — Reset progress for Insight currency, gain permanent bonuses
---
## Features
### 🔮 Mana System
-- **14 Mana Types**: 7 base elements + 1 utility + 3 compound + 3 exotic
+
+- **22 Mana Types**: 7 base elements + 1 utility + 8 composite + 6 exotic
- Elemental conversion, regeneration mechanics, and meditation bonuses
-- Mana types: Fire, Water, Air, Earth, Light, Dark, Death (base), Transference (utility), Metal, Sand, Lightning (compound), Crystal, Stellar, Void (exotic)
+- Mana types: Fire, Water, Air, Earth, Light, Dark, Death (base), Transference (utility), Metal, Sand, Lightning, Frost, BlackFlame, RadiantFlames, Miasma, ShadowGlass (composite), Crystal, Stellar, Void, Soul, Time, Plasma (exotic)
### 📜 Discipline System
-- Practice-based progression - no discrete levels, only continuous XP growth
+
+- Practice-based progression — no discrete levels, only continuous XP growth
- Disciplines drain mana each tick; stat bonuses grow as a power curve of accumulated XP
- Perks unlock at XP thresholds (once, capped, or infinite stacking)
-- Attunement-gated discipline pools (Base / Enchanter / Invoker / Fabricator)
+- Attunement-gated discipline pools (Base / Elemental / Enchanter / Invoker / Fabricator)
- Concurrent discipline slots unlock as total XP grows (max 4)
### ⚔️ Combat & Spire
+
- Cast-speed based combat system with elemental effectiveness
- Multi-spell support from equipped weapons
-- Every 10th floor is a guardian: base elements (10–80), compound (90–110), exotic (120–140), then procedural combination bosses (150+)
+- Every 10th floor is a guardian: base elements (10–80), composite (90–160), exotic (170–240), then procedural combination bosses (250+)
- Golem allies that deal automatic damage each tick
- Enemy modifiers: Armored, Agile, Mage, Shield, Swarm
### 🛡️ Equipment & Enchanting
+
- 3-stage enchantment process: Design → Prepare → Apply
- Equipment capacity system limiting total enchantment power
- Enchantment effects: stat bonuses, multipliers, spell grants
- Disenchanting to recover mana (only in Prepare stage)
-- Weapon/armor slots with 2-handed weapon support
+- 8 equipment slots with 50 equipment types across 9 categories
### 🤖 Golemancy System
-- Summon magical constructs (Earth, Steel, Crystal, Sand + 6 hybrid types)
+
+- 10 golems total: 1 base (Earth) + 3 elemental (Steel, Crystal, Sand) + 6 hybrid types
- Golem slots unlock every 2 Fabricator levels (max 5 slots at Level 10)
- Hybrid golems require Enchanter 5 + Fabricator 5
### 🔄 Prestige (Insight)
+
- Reset progress for permanent Insight currency
-- Insight upgrades across multiple categories
+- Insight upgrades across 14 categories
- Signed pacts and attunements persist through prestige
- Three attunement classes: Enchanter (Transference), Invoker (Spells/Pacts), Fabricator (Golems/Equipment)
@@ -99,27 +105,24 @@
## Tech Stack
| Technology | Version | Purpose |
-|------------|---------|---------|
+|------------|---------|---------|
| **Next.js** | ^16.1.1 | Full-stack framework (App Router) |
| **React** | ^19.0.0 | UI library |
| **TypeScript** | ^5 | Type-safe development |
| **Tailwind CSS** | ^4 | Utility-first styling |
| **shadcn/ui** | Radix-based | Reusable UI components |
| **Zustand** | ^5.0.6 | Client state management (with persist) |
-| **Prisma ORM** | ^6.11.1 | Database abstraction (SQLite) |
| **Bun** | Latest | JavaScript runtime & package manager |
| **Vitest** | ^4.1.2 | Unit testing framework |
| **ESLint** | ^9 | Code linting |
-| **@tanstack/react-query** | ^5.82.0 | Data fetching/caching |
-| **Framer Motion** | ^12.23.2 | Animation library |
---
## Getting Started
### Prerequisites
+
- **Bun** runtime (recommended) or Node.js 18+
-- **SQLite** (for local development, included with Prisma)
- Git
### Installation
@@ -134,11 +137,6 @@ bun install
# Or using npm
npm install
-
-# Set up the database
-bun run db:push
-# or
-npm run db:push
```
### Development
@@ -162,10 +160,6 @@ The game will be available at `http://localhost:3000`.
| `lint` | Run ESLint |
| `test` | Run Vitest tests |
| `test:coverage` | Run tests with coverage report |
-| `db:push` | Push Prisma schema to database |
-| `db:generate` | Generate Prisma client |
-| `db:migrate` | Run database migrations |
-| `db:reset` | Reset database |
---
@@ -178,25 +172,17 @@ Mana-Loop/
│ │ ├── layout.tsx # Root layout (metadata, fonts, providers)
│ │ ├── page.tsx # Main game UI
│ │ ├── globals.css # Global styles
-│ │ └── api/ # API routes (minimal)
+│ │ └── components/ # App-level components
│ ├── components/ # React components
│ │ ├── ui/ # shadcn/ui components (20+ components)
│ │ └── game/ # Game-specific components
│ │ ├── tabs/ # Tab components (SpireTab, DisciplinesTab, etc.)
│ │ ├── ManaDisplay.tsx, ActionButtons.tsx, TimeDisplay.tsx
-│ │ └── crafting/, debug/, shared/, stats/ subdirectories
+│ │ └── crafting/, debug/, LootInventory/ subdirectories
│ ├── hooks/ # Custom React hooks (use-mobile, use-toast)
│ └── lib/ # Utility libraries
│ └── game/ # Core game logic
-│ ├── stores/ # Modular Zustand stores
-│ │ ├── gameStore.ts # Core state & tick logic
-│ │ ├── manaStore.ts # Mana gathering & conversion
-│ │ ├── combatStore.ts # Combat, spells, floor progression
-│ │ ├── prestigeStore.ts # Prestige/loop & insight
-│ │ ├── discipline-slice.ts # Discipline activation & XP
-│ │ ├── attunementStore.ts # Attunement classes
-│ │ ├── craftingStore.ts # Crafting state
-│ │ └── uiStore.ts # UI state & modals
+│ ├── stores/ # 8 Modular Zustand stores (+ supporting files)
│ ├── crafting-actions/ # Modular crafting stage handlers
│ ├── constants/ # Elements, spells, rooms, prestige
│ ├── data/ # Game data
@@ -204,13 +190,11 @@ Mana-Loop/
│ │ ├── enchantments/ # Enchantment effects by category
│ │ ├── equipment/ # Equipment type definitions
│ │ ├── golems/ # Golem definitions
-│ │ ├── guardian-data.ts # Static guardian definitions (floors 10–140)
-│ │ └── guardian-encounters.ts # Procedural guardian lookup & combo bosses
+│ │ ├── guardian-data.ts # Static guardian definitions (floors 10–240)
+│ │ └── guardian-encounters.ts # Procedural guardian lookup & combo bosses (250+)
│ ├── effects/ # Unified stat computation
-│ │ └── discipline-effects.ts # Discipline → getUnifiedEffects()
│ ├── types/ # TypeScript types (disciplines, elements, etc.)
│ └── utils/ # Combat, floor, enemy, discipline math helpers
-├── prisma/ # Database schema and migrations
├── public/ # Static assets
├── docs/ # Project documentation
│ ├── AGENTS.md # Architecture guide for AI agents
@@ -229,16 +213,19 @@ For detailed architecture patterns and coding guidelines, see [AGENTS.md](./AGEN
## Game Systems
### Mana System
-The core resource of the game with 14 distinct types organized in a hierarchy:
+
+The core resource of the game with 22 distinct types organized in a hierarchy:
+
- **Base Elements (7)**: Fire, Water, Air, Earth, Light, Dark, Death
- **Utility (1)**: Transference (Enchanter attunement)
-- **Compound (3)**: Metal (Fire+Earth), Sand (Earth+Water), Lightning (Fire+Air)
-- **Exotic (3)**: Crystal (Sand+Sand+Light), Stellar (Fire+Fire+Light), Void (Dark+Dark+Death)
+- **Composite (8)**: Metal (Fire+Earth), Sand (Earth+Water), Lightning (Fire+Air), Frost (Air+Water), BlackFlame (Dark+Fire), RadiantFlames (Light+Fire), Miasma (Air+Death), ShadowGlass (Earth+Dark)
+- **Exotic (6)**: Crystal (Sand+Sand+Light), Stellar (Plasma+Light+Fire), Void (Dark+Dark+Death), Soul (Light+Dark+Transference), Time (Soul+Sand+Transference), Plasma (Lightning+Fire+Transference)
**Key Files**: `src/lib/game/stores/manaStore.ts`, `src/lib/game/constants/elements.ts`
### Discipline System
-Disciplines replace the old skill system entirely. There are no discrete levels - disciplines grow **continuously** through practice. The player activates a discipline and it drains mana each tick in exchange for permanent stat growth within the run.
+
+Disciplines replace the old skill system entirely. There are no discrete levels — disciplines grow **continuously** through practice. The player activates a discipline and it drains mana each tick in exchange for permanent stat growth within the run.
- **Stat bonus** grows as a power curve of XP: `baseValue × (XP / scalingFactor)^0.65`
- **Mana drain** also increases with XP: `drainBase × (1 + (XP / difficultyFactor)^0.4)`
@@ -248,16 +235,18 @@ Disciplines replace the old skill system entirely. There are no discrete levels
**Key Files**: `src/lib/game/data/disciplines/`, `src/lib/game/stores/discipline-slice.ts`, `src/lib/game/utils/discipline-math.ts`
### Guardian & Spire System
-Every 10th floor is a guardian encounter. Guardians progress through four tiers of complexity:
+
+Every 10th floor is a guardian encounter. Guardians progress through multiple tiers of complexity:
1. **Base Elements (Floors 10–80)**: One guardian per base element + Transference. Static definitions with named guardians (Ignis Prime, Aqua Regia, etc.). Defeating them unlocks their associated mana types.
-2. **Compound Elements (Floors 90–110)**: Metal, Sand, and Lightning guardians with procedurally generated names.
-3. **Exotic Elements (Floors 120–140)**: Crystal, Stellar, and Void guardians - the most powerful single-element encounters.
-4. **Combination Bosses (Floor 150+)**: Fully procedural dual-element guardians. Each one wields two base elements simultaneously (e.g. Fire+Water, Light+Dark) and grows stronger every 10 floors.
+2. **Composite Elements (Floors 90–160)**: 8 composite element guardians (Metal, Sand, Lightning, Frost, BlackFlame, RadiantFlames, Miasma, ShadowGlass) with procedurally generated names.
+3. **Exotic Elements (Floors 170–240)**: Crystal, Stellar, Void, Soul, Time, and Plasma guardians.
+4. **Combination Bosses (Floor 250+)**: Fully procedural multi-element guardians through 8 scaling tiers, growing stronger every 10 floors.
**Key Files**: `src/lib/game/data/guardian-data.ts`, `src/lib/game/data/guardian-encounters.ts`
### Combat System
+
- Cast-speed based spell casting with elemental effectiveness multipliers
- Enemy modifiers: Armored, Agile, Mage (barrier), Shielded, Swarm
- Golem allies deal automatic damage each tick
@@ -266,25 +255,29 @@ Every 10th floor is a guardian encounter. Guardians progress through four tiers
**Key Files**: `src/lib/game/stores/combatStore.ts`, `src/lib/game/utils/combat-utils.ts`, `src/lib/game/utils/enemy-generator.ts`
### Enchanting System
+
3-stage equipment enchantment process:
1. **Design**: Choose effects for your equipment type
2. **Prepare**: Ready equipment (ONLY stage where disenchanting is possible)
-3. **Apply**: Apply designed enchantments (cannot re-enchant already enchanted gear)
+3. **Apply**: Apply designed enchantments
**Key Files**: `src/lib/game/crafting-actions/`, `src/lib/game/data/enchantments/`
### Golemancy System
-- **Base Golems**: Earth (Fabricator 2), Steel (Metal), Crystal, Sand
+
+- **Base Golems**: Earth (Fabricator 2)
+- **Elemental Golems**: Steel (Metal), Crystal, Sand
- **Hybrid Golems** (Enchanter 5 + Fabricator 5): Lava, Galvanic, Obsidian, Prism, Quicksilver, Voidstone
- **Golem Slots**: 1 slot at Fabricator Level 2, +1 every 2 levels (max 5 at Level 10)
**Key Files**: `src/lib/game/data/golems/`, `src/lib/game/stores/gameStore.ts`
### Prestige (Insight)
+
Reset progress to gain Insight currency for permanent upgrades:
- Signed pacts persist through prestige
- Attunement choices affect gameplay (Enchanter/Invoker/Fabricator)
-- Insight upgrades provide bonuses across all loops
+- 14 insight upgrade types provide bonuses across all loops
---
@@ -302,14 +295,17 @@ docker run -p 3000:3000 mana-loop
```
### CI/CD Pipeline
+
- **Gitea Actions**: `.gitea/workflows/docker-build.yaml` automatically builds and pushes Docker images to `gitea.tailf367e3.ts.net/anexim/mana-loop:latest` on push to `master`/`main`
- **Multi-platform**: Builds for linux/amd64 architecture
- **Image Tags**: Branch name, commit SHA, "latest"
### Reverse Proxy
+
A `Caddyfile` is included for reverse proxy setup (forwards port 81 to 3000).
### Production Build
+
```bash
bun run build
NODE_ENV=production bun .next/standalone/server.js
@@ -322,6 +318,7 @@ NODE_ENV=production bun .next/standalone/server.js
We welcome contributions! Please follow these guidelines:
### Development Workflow
+
1. **Pull latest changes** before starting work: `git pull origin master`
2. **Create a feature branch** for your changes: `git checkout -b feature/your-feature`
3. **Follow existing patterns** in the codebase (see AGENTS.md)
@@ -330,6 +327,7 @@ We welcome contributions! Please follow these guidelines:
6. **Commit and push** to your branch, then create a pull request
### Code Style
+
- TypeScript throughout with strict typing
- Use existing shadcn/ui components over custom implementations
- Follow the modular store pattern (`src/lib/game/stores/`)
@@ -337,6 +335,7 @@ We welcome contributions! Please follow these guidelines:
- Use path aliases: `@/*` maps to `./src/*`
### Adding New Features
+
For detailed patterns on adding new effects, disciplines, spells, or systems, see the comprehensive [AGENTS.md](./AGENTS.md) guide, which includes architecture overview, coding patterns, and git workflow.
---
@@ -346,21 +345,24 @@ For detailed patterns on adding new effects, disciplines, spells, or systems, se
The following content has been removed from the game and must not be re-added:
### Banned Mechanics
-- **Lifesteal** - Player cannot heal from dealing damage
-- **Healing** - Player cannot heal themselves (floors take damage, not the player)
-- **Scroll crafting** - Violates the no-instant-finishing design pillar
-- **Ascension skills** - Removed; no replacement
+
+- **Lifesteal** — Player cannot heal from dealing damage
+- **Healing** — Player cannot heal themselves (floors take damage, not the player)
+- **Scroll crafting** — Violates the no-instant-finishing design pillar
+- **Ascension skills** — Removed; no replacement
### Banned Mana Types
-- **Life** - Removed (healing theme conflicts with core design)
-- **Blood** - Removed (life derivative)
-- **Wood** - Removed (life derivative)
-- **Mental** - Removed
-- **Force** - Removed
+
+- **Life** — Removed (healing theme conflicts with core design)
+- **Blood** — Removed (life derivative)
+- **Wood** — Removed (life derivative)
+- **Mental** — Removed
+- **Force** — Removed
### Banned Systems
-- **Familiar System** - Removed in favour of Golemancy and Pact systems
-- **Skill System** (study, tiers T1–T5, milestone upgrades) - Fully replaced by the Discipline System
+
+- **Familiar System** — Removed in favour of Golemancy and Pact systems
+- **Skill System** (study, tiers T1–T5, milestone upgrades) — Fully replaced by the Discipline System
---
@@ -385,7 +387,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+LIABILITY, WHETHER AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```
@@ -396,7 +398,7 @@ SOFTWARE.
- Built with modern web technologies (Next.js, React, TypeScript, Tailwind CSS)
- UI components from [shadcn/ui](https://ui.shadcn.com/)
-- State management with [Zustand](https://github.com/pmndrs/zustand)
+- State management with [Zustand](https://github.com/pmndrs/zustand/)
- Game icons from [Lucide React](https://lucide.dev/)
- Special thanks to the open-source community for the amazing tools that make this project possible.
@@ -404,4 +406,4 @@ SOFTWARE.
Climb the spire. Master the mana. Uncover the loop.
-
\ No newline at end of file
+
diff --git a/docs/GAME_BRIEFING.md b/docs/GAME_BRIEFING.md
index c32530c..c661a79 100644
--- a/docs/GAME_BRIEFING.md
+++ b/docs/GAME_BRIEFING.md
@@ -1,7 +1,7 @@
# Mana-Loop: Comprehensive Game Briefing Document
-**Document Version:** 3.0
-**Updated:** Post-refactoring — skills removed, disciplines procedural guardians, combo bosses, 14 mana types, localStorage-only
+**Document Version:** 4.0
+**Updated:** Post-refactoring — 22 mana types, 8-tier guardian system, 64 disciplines, 8 Zustand stores, localStorage-only
---
@@ -35,11 +35,11 @@
- Equipment-based spell system (spells come from enchanted gear and learned spells)
- Practice-based Discipline system — no discrete skill levels, only continuous XP growth
- Time pressure through the incursion mechanic (starts day 5)
-- Guardian progression: base → compound → exotic → combination bosses (150+)
+- Guardian progression: base (10-80) → composite (90-160) → exotic (170-240) → combo bosses (250+, 8 tiers)
- Guardian pacts provide permanent multipliers that persist through prestige
- No backend — pure client-side with localStorage persistence
-**Code Architecture:** Modular Zustand stores, crafting actions, discipline data, and constants. No legacy store files remain.
+**Code Architecture:** 8 modular Zustand stores, crafting actions, discipline data, and constants. No legacy store files remain.
---
@@ -61,8 +61,8 @@
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ DEFEAT GUARDIANS → SIGN PACTS (every 10th) │ │
-│ │ Base(10-80) → Compound(90-110) → Exotic(120-140)│ │
-│ │ → Combination Bosses(150+) │ │
+│ │ Base(10-80) → Composite(90-160) → Exotic(170-240)│ │
+│ │ → Combo Bosses(250+, 8 tiers) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
@@ -376,7 +376,7 @@ Miasma ↔ Light Shadow Glass ↔ Light
Floors 90–240 have procedurally generated names via `generateGuardianName()`.
-#### Tier 4 — Combination Bosses (Floor 150+)
+#### Tier 4 — Dual Element Pairs (Floors 250–280)
Nine dual-element combinations cycle every 10 floors:
@@ -392,14 +392,20 @@ Nine dual-element combinations cycle every 10 floors:
| 7 | air + light | Radiant wind |
| 8 | earth + death | Fossil |
-**Scaling (per floor above 150):**
+#### Tiers 5–8 — Scaling Combo Bosses (Floors 290+)
+
+- **Tier 5 (290–330)**: Dual composite + components
+- **Tier 6 (340–380)**: Exotic + components
+- **Tier 7 (390–430)**: Exotic + composite + components
+- **Tier 8 (440+)**: Full fusion — 1 exotic + 2 composites + all base elements
+
+**Scaling (per tier):**
```
-armor = min(0.5, 0.25 + (floor - 150) × 0.002)
-pactMultiplier = 6.0 + (floor - 150) × 0.05
-pactCost = floor(hp × 0.5)
-pactTime = 20 + floor((floor - 150) / 10)
-damageMult = 3.0 + (floor - 150) × 0.02
-insightMult = 2.5 + (floor - 150) × 0.01
+armor = min(0.7, 0.30 + floor_increment × 0.002-0.003)
+pactMultiplier = 7.5+ (scales with tier)
+pactTime = 20+ hours (scales with tier)
+damageMult = 3.5+ (scales with tier)
+insightMult = 3.0+ (scales with tier)
```
Combo guardians grant boons to both elements (+10% each) and dual-aspect perks (+20% effectiveness to both element spells). Names generated from combined prefixes (e.g., "Ignis-Aqua the Warden").
@@ -950,7 +956,7 @@ getElementalBonus(spellElement, floorElement):
### Modular Structure Overview
-#### Store Architecture (7 Zustand Stores)
+#### Store Architecture (8 Zustand Stores)
**Active Stores (`src/lib/game/stores/`):**
- **gameStore.ts** — Coordinator/tick pipeline, combines all stores
@@ -962,6 +968,8 @@ getElementalBonus(spellElement, floorElement):
- **discipline-slice.ts** — Discipline activation, XP ticking, perk evaluation
- **uiStore.ts** — Logs, pause, game over/victory flags
+(Note: `useDisciplineStore` is exported from `discipline-slice.ts`, making it the 8th store.)
+
**Supporting store files:**
- `tick-pipeline.ts` — `buildTickContext()` / `applyTickWrites()` pattern
- `combat-actions.ts` — Combat tick processing
@@ -974,12 +982,12 @@ getElementalBonus(spellElement, floorElement):
**No legacy store files remain.** The old `store.ts`, `store/`, and `store-modules/` directories have been fully removed.
#### Data Layer (`src/lib/game/data/`)
-- `disciplines/` — 11 files: per-attunement discipline definitions (34 disciplines total)
+- `disciplines/` — 11 files: per-attunement discipline definitions (64 disciplines total)
- `enchantments/` — 7 files + `spell-effects/` subdirectory: enchantment effects by category
- `equipment/` — 13 files: equipment type definitions by slot
- `golems/` — 7 files: golem definitions (base, elemental, hybrid)
-- `guardian-data.ts` — Static guardian definitions (floors 10–140)
-- `guardian-encounters.ts` — Procedural guardian lookup & combo bosses (150+)
+- `guardian-data.ts` — Static guardian definitions (floors 10–240)
+- `guardian-encounters.ts` — Procedural guardian lookup & combo bosses (250+, 8 tiers)
- `attunements.ts` — Attunement definitions
- `achievements.ts` — Achievement definitions (24 achievements)
- `crafting-recipes.ts` / `fabricator-recipes.ts` — Crafting recipes
@@ -992,7 +1000,7 @@ getElementalBonus(spellElement, floorElement):
- `prestige.ts` — Prestige upgrade definitions
- `rooms.ts` — Room type configs (armor, speed, swarm, puzzle)
- `spells.ts` — Spell constants barrel
-- `spells-modules/` — 10 files: spell definitions by category (raw, basic, advanced, master, legendary, lightning, compound, AOE, utility, enchantment)
+- `spells-modules/` — 15 files: spell definitions by category (raw, basic, advanced, master, legendary, lightning, compound, compound+, frost, blackflame, radiantflames, miasma, shadowglass, AOE, utility, enchantment, plasma, soul, time)
#### Types (`src/lib/game/types/`)
- `game.ts` — Core game types (GameState, ActivityLogEntry, etc.)
@@ -1094,10 +1102,10 @@ The following systems no longer exist and should not be re-introduced:
| Scroll crafting | Deleted (violates no-instant-finishing pillar) |
| Lifesteal/healing | Banned permanently |
| Familiar System | Replaced by Golemancy and Pact systems |
-| Legacy static guardians (named Primordialis, The Awakened One) | Procedural guardian system with 4 tiers |
+| Legacy static guardians (named Primordialis, The Awakened One) | Procedural guardian system with 8 tiers |
| Prisma / database | Removed; localStorage-only persistence |
---
-*Document Version: 3.0 — Post-Refactoring Update*
+*Document Version: 4.0 — Updated: 8 stores, 22 mana types, 64 disciplines, 8-tier guardians, localStorage-only*
*End of Game Briefing Document*
diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt
index d900ce6..e12c6b3 100644
--- a/docs/circular-deps.txt
+++ b/docs/circular-deps.txt
@@ -1,4 +1,4 @@
# Circular Dependencies
-Generated: 2026-06-01T09:05:01.898Z
+Generated: 2026-06-01T10:58:05.599Z
No circular dependencies found. ✅
diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json
index 1ec15bc..c5cd177 100644
--- a/docs/dependency-graph.json
+++ b/docs/dependency-graph.json
@@ -1,6 +1,6 @@
{
"_meta": {
- "generated": "2026-06-01T09:04:59.927Z",
+ "generated": "2026-06-01T10:58:03.834Z",
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
},
@@ -650,6 +650,7 @@
"stores/gameStore.types.ts",
"stores/manaStore.ts",
"stores/pipelines/combat-tick.ts",
+ "stores/pipelines/enchanting-tick.ts",
"stores/pipelines/pact-ritual.ts",
"stores/prestigeStore.ts",
"stores/tick-pipeline.ts",
@@ -687,6 +688,15 @@
"effects/special-effects.ts",
"effects/upgrade-effects.types.ts"
],
+ "stores/pipelines/enchanting-tick.ts": [
+ "constants.ts",
+ "crafting-apply.ts",
+ "crafting-design.ts",
+ "crafting-prep.ts",
+ "effects/upgrade-effects.types.ts",
+ "stores/craftingStore.ts",
+ "stores/tick-pipeline.ts"
+ ],
"stores/pipelines/equipment-crafting.ts": [
"crafting-equipment.ts",
"crafting-fabricator.ts",
diff --git a/docs/project-structure.txt b/docs/project-structure.txt
index 34a6a2e..4be63f3 100644
--- a/docs/project-structure.txt
+++ b/docs/project-structure.txt
@@ -121,6 +121,7 @@ Mana-Loop/
│ │ │ │ ├── PrestigeTab.test.ts
│ │ │ │ ├── PrestigeTab.tsx
│ │ │ │ ├── SpellsTab.tsx
+│ │ │ │ ├── SpireSummaryTab.helpers.tsx
│ │ │ │ ├── SpireSummaryTab.test.ts
│ │ │ │ ├── SpireSummaryTab.tsx
│ │ │ │ ├── StatsTab.tsx
diff --git a/src/components/game/tabs/DisciplineCard.tsx b/src/components/game/tabs/DisciplineCard.tsx
index e9abbad..76e4438 100644
--- a/src/components/game/tabs/DisciplineCard.tsx
+++ b/src/components/game/tabs/DisciplineCard.tsx
@@ -12,6 +12,7 @@ export interface DisciplineCardProps {
definition: DisciplineDefinition;
xp: number;
paused: boolean;
+ autoPaused?: boolean;
activeIds: string[];
concurrentLimit: number;
isLocked: boolean;
@@ -23,7 +24,7 @@ export interface DisciplineCardProps {
// ─── Component ────────────────────────────────────────────────────────────────
export const DisciplineCard: React.FC = ({
- definition, xp, paused: isPaused, activeIds, concurrentLimit,
+ definition, xp, paused: isPaused, autoPaused, activeIds, concurrentLimit,
isLocked, missingPrereqs, missingSourceMana, onToggle,
}) => {
const {
@@ -159,6 +160,13 @@ export const DisciplineCard: React.FC = ({
)}
+ {/* Auto-paused mana feedback (fix #244) */}
+ {isActive && isPaused && autoPaused && (
+
+ ⏸️ Auto-paused — insufficient {manaName} mana to continue practicing.
+
+ )}
+
{/* Action Button */}
);
}
-function StatCell({ value, label, color }: { value: number | string; label: string; color: string }) {
+function StatCell({ value, label, color, isFmt }: { value: number; label: string; color: string; isFmt?: boolean }) {
return (
-
{value}
+
{isFmt ? value : value}
{label}
);
@@ -147,16 +45,14 @@ function StatCell({ value, label, color }: { value: number | string; label: stri
// ─── Next Guardian Card ──────────────────────────────────────────────────────
-function NextGuardianCard({ nextGuardian, nextGuardianData }: { nextGuardian: number; nextGuardianData: GuardianDef }) {
+function NextGuardianCard({ nextGuardian, nextGuardianData }: { nextGuardian: number; nextGuardianData: ReturnType }) {
+ if (!nextGuardianData) return null;
const counterElement = getCounterElement(nextGuardianData.element[0]);
const nextFloorElement = FLOOR_ELEM_CYCLE[(nextGuardian - 1) % FLOOR_ELEM_CYCLE.length];
return (
-
+
{nextGuardianData.name}
-
-
+
+
{nextGuardianData.element.join(' + ')}
- Health: {fmt(nextGuardianData.hp)}
- {nextGuardianData.armor && (
-
- Armor: {Math.round(nextGuardianData.armor * 100)}%
-
- )}
- {nextGuardianData.shield && nextGuardianData.shield > 0 && (
-
- Shield: {fmt(nextGuardianData.shield)}
-
- )}
- {nextGuardianData.barrier && nextGuardianData.barrier > 0 && (
-
- Barrier: {Math.round(nextGuardianData.barrier * 100)}%
-
- )}
- {nextGuardianData.healthRegen && nextGuardianData.healthRegen > 0 && (
-
- Regen: {nextGuardianData.healthRegenIsPercent ? nextGuardianData.healthRegen + '%/tick' : nextGuardianData.healthRegen + '/tick'}
-
- )}
+ Health: {nextGuardianData.hp}
+ {fmtArmor(nextGuardianData.armor)}
+ {fmtShield(nextGuardianData.shield)}
+ {fmtBarrier(nextGuardianData.barrier)}
+ {fmtRegen(nextGuardianData.healthRegen, nextGuardianData.healthRegenIsPercent)}
-
-
0.15)}
- />
+ 0.15)} />
);
}
-function PreparationTips({ counterElement, nextFloorElement, hasHighArmor }: { counterElement: string | null; nextFloorElement: string | null; hasHighArmor: boolean }) {
- return (
-
-
Recommended Preparation:
-
- {counterElement && (
-
- ⚡
-
- Use {counterElement} spells for super effective damage (+50%)
-
-
- )}
- {nextFloorElement && (
-
- 🔄
-
- Floor element: {nextFloorElement}
-
-
- )}
- {hasHighArmor && (
-
- 🛡️
- High armor — consider armor-piercing or raw damage spells
-
- )}
-
- 💡
- Ensure mana pools are full before attempting
-
-
-
- );
-}
-
-// ─── Guardian Roster ─────────────────────────────────────────────────────────
-
-function GuardianRoster({ clearedFloors }: { clearedFloors: Record }) {
- return (
-
-
-
-
- {GUARDIAN_FLOORS.map((floor) => {
- const guardian = getGuardianForFloor(floor);
- return guardian ? (
-
- ) : null;
- })}
-
-
-
- );
-}
-
-function GuardianRosterItem({ floor, guardian, isDefeated }: { floor: number; guardian: GuardianDef; isDefeated: boolean }) {
- return (
-
-
-
- {floor}
-
-
-
- {guardian.name}
-
-
-
- {guardian.element.join(' + ')}
-
- Health: {fmt(guardian.hp)}
-
-
-
-
- {isDefeated ? (
-
- ✓ Defeated
-
- ) : (
-
- Undefeated
-
- )}
-
-
- );
-}
-
// ─── Main Component ───────────────────────────────────────────────────────────
export function SpireSummaryTab() {
- const {
- maxFloorReached,
- clearedFloors,
- enterSpireMode,
- } = useCombatStore(useShallow((s) => ({
+ const { maxFloorReached, clearedFloors, enterSpireMode } = useCombatStore(useShallow((s) => ({
maxFloorReached: s.maxFloorReached,
clearedFloors: s.clearedFloors,
enterSpireMode: s.enterSpireMode,
})));
+ const { insight } = usePrestigeStore(useShallow((s) => ({ insight: s.insight })));
- const { insight } = usePrestigeStore(useShallow((s) => ({
- insight: s.insight,
- })));
-
- const defeatedGuardians = useMemo(() => {
- return GUARDIAN_FLOORS.filter((floor) => clearedFloors[floor]);
- }, [clearedFloors]);
-
- const nextGuardian = useMemo(() => {
- return GUARDIAN_FLOORS.find((floor) => !clearedFloors[floor]) || null;
- }, [clearedFloors]);
-
+ const defeatedGuardians = useMemo(() => getAllGuardianFloors().filter((floor) => clearedFloors[floor]), [clearedFloors]);
+ const nextGuardian = useMemo(() => getAllGuardianFloors().find((floor) => !clearedFloors[floor]) || null, [clearedFloors]);
const nextGuardianData = nextGuardian ? getGuardianForFloor(nextGuardian) : null;
-
- const totalFloorsCleared = useMemo(() => {
- return Object.values(clearedFloors).filter(Boolean).length;
- }, [clearedFloors]);
+ const totalFloorsCleared = useMemo(() => Object.values(clearedFloors).filter(Boolean).length, [clearedFloors]);
return (
-
-
- {nextGuardianData && nextGuardian && (
-
- )}
-
+
+ {nextGuardianData && nextGuardian && }
-
diff --git a/src/lib/game/stores/combatStore.ts b/src/lib/game/stores/combatStore.ts
index ea0cf88..2f0bfe6 100644
--- a/src/lib/game/stores/combatStore.ts
+++ b/src/lib/game/stores/combatStore.ts
@@ -153,7 +153,7 @@ export const useCombatStore = create()(
},
exitSpireMode: () => {
- set({
+ set((s) => ({
spireMode: false,
currentAction: 'meditate',
climbDirection: null,
@@ -164,8 +164,9 @@ export const useCombatStore = create()(
currentRoom: generateFloorState(1),
castProgress: 0,
clearedFloors: {},
- maxFloorReached: 0,
- });
+ // Preserve maxFloorReached — don't reset to 0 on spire exit (fix #238)
+ maxFloorReached: Math.max(s.maxFloorReached, 1),
+ }));
},
startClimbUp: () => set({ climbDirection: 'up', currentAction: 'climb' }),
@@ -219,7 +220,7 @@ export const useCombatStore = create()(
climbDirection: null,
isDescending: false,
clearedFloors: {},
- maxFloorReached: Math.max(s.maxFloorReached, s.currentFloor),
+ // Don't inflate maxFloorReached — it should reflect actual progress (fix #238)
}));
},
diff --git a/src/lib/game/stores/discipline-slice.ts b/src/lib/game/stores/discipline-slice.ts
index c92815d..ad108c0 100644
--- a/src/lib/game/stores/discipline-slice.ts
+++ b/src/lib/game/stores/discipline-slice.ts
@@ -55,6 +55,7 @@ export interface DisciplineStoreActions {
elements: Record;
unlockedEffects: string[];
unlockedRecipes: string[];
+ autoPausedNames: string[];
};
setPracticingCallbacks(callbacks: { onStartPracticing: () => void; onStopPracticing: () => void }): void;
resetDisciplines: () => void;
@@ -83,10 +84,10 @@ export const useDisciplineStore = create()(
// Allow re-activation if discipline exists but is paused
const existing = s.disciplines[id];
if (activeIds.includes(id)) {
- // If already active and paused, un-pause it
+ // If already active and paused (manually or auto), un-pause it
if (existing?.paused) {
return {
- disciplines: { ...s.disciplines, [id]: { ...existing, paused: false } },
+ disciplines: { ...s.disciplines, [id]: { ...existing, paused: false, autoPaused: false } },
};
}
return s;
@@ -166,10 +167,12 @@ export const useDisciplineStore = create()(
const newProcessedPerks = [...(s.processedPerks ?? [])];
const drainedIds: string[] = [];
+ const drainedNames: string[] = [];
for (const id of s.activeIds ?? []) {
const disc = newDisciplines[id];
if (!disc) continue;
if (disc.paused) continue;
+ if (disc.autoPaused) continue; // already auto-paused, don't re-process
const def = DISCIPLINE_MAP[id];
if (!def) continue;
@@ -179,8 +182,9 @@ export const useDisciplineStore = create()(
const available = def.manaType === 'raw' ? rawMana : element?.current;
if (!available || available < drain) {
- newDisciplines[id] = { ...disc, paused: true };
+ newDisciplines[id] = { ...disc, paused: true, autoPaused: true };
drainedIds.push(id);
+ drainedNames.push(def.name);
continue;
}
@@ -261,7 +265,7 @@ export const useDisciplineStore = create()(
processedPerks: newProcessedPerks,
});
- return { rawMana, elements, unlockedEffects: newUnlockedEffects, unlockedRecipes: newUnlockedRecipes };
+ return { rawMana, elements, unlockedEffects: newUnlockedEffects, unlockedRecipes: newUnlockedRecipes, autoPausedNames: drainedNames };
},
}),
{ storage: createSafeStorage(), name: 'mana-loop-discipline-store', version: 1, partialize: (state) => ({ disciplines: state.disciplines, activeIds: state.activeIds, concurrentLimit: state.concurrentLimit, totalXP: state.totalXP, processedPerks: state.processedPerks }) }
diff --git a/src/lib/game/stores/gameStore.ts b/src/lib/game/stores/gameStore.ts
index 9588346..a2c1a69 100644
--- a/src/lib/game/stores/gameStore.ts
+++ b/src/lib/game/stores/gameStore.ts
@@ -218,6 +218,21 @@ export const useGameStore = create()(
rawMana = disciplineResult.rawMana;
elements = disciplineResult.elements;
+ // Log auto-paused disciplines for better UX feedback (fix #244)
+ if (disciplineResult.autoPausedNames.length > 0) {
+ const names = disciplineResult.autoPausedNames.join(', ');
+ addLog('⏸️ Auto-paused (insufficient mana): ' + names);
+ }
+
+ // Recompute maxMana after discipline XP gains so clamping uses updated value (fix #246)
+ const updatedDisciplineEffects = computeDisciplineEffects();
+ const updatedMaxMana = computeMaxMana(
+ { prestigeUpgrades: ctx.prestige.prestigeUpgrades },
+ undefined,
+ updatedDisciplineEffects,
+ );
+ rawMana = Math.min(rawMana, updatedMaxMana);
+
for (const [targetElem, conv] of Object.entries(disciplineEffects.conversions)) {
const conversionAmount = conv.rate * HOURS_PER_TICK;
let canConvert = true;
diff --git a/src/lib/game/types/disciplines.ts b/src/lib/game/types/disciplines.ts
index df63471..a5b8414 100644
--- a/src/lib/game/types/disciplines.ts
+++ b/src/lib/game/types/disciplines.ts
@@ -67,6 +67,8 @@ export interface DisciplineState {
id: string;
xp: number;
paused: boolean;
+ /** True when the discipline was auto-paused due to insufficient mana */
+ autoPaused?: boolean;
}
// ─── Discipline Constants ─────────────────────────────────────────────────────