From b402b8f56ea1eda4c335f80d1ecb83dd4ce1f6c5 Mon Sep 17 00:00:00 2001 From: n8n-gitea Date: Tue, 26 May 2026 11:20:36 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20cleanup=20codebase=20=E2=80=94=20re?= =?UTF-8?q?move=20hydration=20guards,=20extract=20constants,=20fix=20bugs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 167 +++--- docs/circular-deps.txt | 4 +- docs/dependency-graph.json | 6 +- scorecard.png | Bin 85625 -> 37488 bytes src/components/game/AttunementStatus.tsx | 19 +- src/components/game/ManaDisplay.tsx | 2 +- src/components/game/tabs/AchievementsTab.tsx | 16 +- src/components/game/tabs/AttunementsTab.tsx | 16 +- src/components/game/tabs/DisciplinesTab.tsx | 16 +- src/components/game/tabs/EquipmentTab.tsx | 17 +- src/components/game/tabs/GolemancyTab.tsx | 16 +- src/components/game/tabs/GuardianPactsTab.tsx | 16 +- src/components/game/tabs/PrestigeTab.tsx | 17 +- .../game/tabs/SpireCombatPage/RoomDisplay.tsx | 2 +- .../tabs/SpireCombatPage/SpireCombatPage.tsx | 11 - src/components/game/tabs/SpireSummaryTab.tsx | 17 +- src/lib/game/crafting-apply.ts | 96 +--- src/lib/game/crafting-design.ts | 110 ++-- src/lib/game/crafting-equipment.ts | 143 ++--- src/lib/game/crafting-prep.ts | 81 +-- src/lib/game/effects/dynamic-compute.ts | 122 ++--- src/lib/game/stores/gameStore.ts | 501 +++++++++--------- src/lib/game/stores/manaStore.ts | 163 ++---- 23 files changed, 579 insertions(+), 979 deletions(-) diff --git a/README.md b/README.md index f6e0558..456a212 100755 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

Mana Loop Logo
- An incremental/idle game about climbing a magical spire, mastering skills, and uncovering ancient secrets. + An incremental/idle game about climbing a magical spire, mastering disciplines, and uncovering ancient secrets.

@@ -15,7 +15,7 @@

- Version + Version License Next.js TypeScript @@ -42,13 +42,13 @@ ## Overview -**Mana Loop** is a browser-based incremental/idle game where players gather mana, master skills, climb a mysterious 100-floor spire, craft enchanted equipment, and summon magical golems. The game features a unique time-loop prestige system (Insight) that provides permanent progression bonuses across playthroughs. +**Mana Loop** is a browser-based incremental/idle game where players gather mana, practice disciplines, climb a mysterious spire, craft enchanted equipment, and summon magical golems. The game features a unique time-loop prestige system (Insight) that provides permanent progression bonuses across playthroughs. ### Core Game Loop 1. **Gather Mana** - Click to collect mana or let it regenerate automatically (14 total mana types) -2. **Study Skills & Spells** - 20+ skills with 5-tier evolution system and milestone upgrades -3. **Climb the Spire** - Battle through 100 procedurally-generated floors, defeat guardians, sign pacts +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 @@ -62,18 +62,19 @@ - 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) -### 📜 Skill & Spell System -- 20+ skills across multiple categories (mana, study, enchanting, golemancy) -- 5-tier evolution system for each skill -- Milestone upgrades at levels 5 and 10 per tier -- Unique special effects unlocked through skill upgrades +### 📜 Discipline System +- 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) +- Concurrent discipline slots unlock as total XP grows (max 4) ### ⚔️ Combat & Spire -- Cast-speed based combat system +- Cast-speed based combat system with elemental effectiveness - Multi-spell support from equipped weapons -- 100-floor spire with elemental themes -- Floor guardians with unique mechanics and pacts +- Every 10th floor is a guardian: base elements (10–80), compound (90–110), exotic (120–140), then procedural combination bosses (150+) - Golem allies that deal automatic damage each tick +- Enemy modifiers: Armored, Agile, Mage, Shield, Swarm ### 🛡️ Equipment & Enchanting - 3-stage enchantment process: Design → Prepare → Apply @@ -86,20 +87,19 @@ - Summon magical constructs (Earth, 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 -- Golem maintenance costs and stat upgrades via skills ### 🔄 Prestige (Insight) - Reset progress for permanent Insight currency - Insight upgrades across multiple categories - Signed pacts and attunements persist through prestige -- Three attunement classes: Enchanter (Transference), Invoker (Spells), Fabricator (Golems/Equipment) +- Three attunement classes: Enchanter (Transference), Invoker (Spells/Pacts), Fabricator (Golems/Equipment) --- ## 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 | @@ -176,50 +176,53 @@ Mana-Loop/ ├── src/ # Application source code │ ├── app/ # Next.js App Router │ │ ├── layout.tsx # Root layout (metadata, fonts, providers) -│ │ ├── page.tsx # Main game UI (~583 lines) +│ │ ├── page.tsx # Main game UI │ │ ├── globals.css # Global styles │ │ └── api/ # API routes (minimal) │ ├── components/ # React components │ │ ├── ui/ # shadcn/ui components (20+ components) │ │ └── game/ # Game-specific components -│ │ ├── tabs/ # Tab components (SpireTab, SkillsTab, etc.) +│ │ ├── tabs/ # Tab components (SpireTab, DisciplinesTab, etc.) │ │ ├── ManaDisplay.tsx, ActionButtons.tsx, TimeDisplay.tsx │ │ └── crafting/, debug/, shared/, stats/ subdirectories │ ├── hooks/ # Custom React hooks (use-mobile, use-toast) -│ ├── lib/ # Utility libraries -│ │ ├── game/ # Core game logic -│ │ │ ├── store.ts # Main Zustand store (~2862 lines) -│ │ │ ├── crafting-slice.ts, study-slice.ts, navigation-slice.ts -│ │ │ ├── effects.ts, upgrade-effects.ts -│ │ │ ├── skill-evolution.ts (~3400 lines) -│ │ │ ├── constants/ # Game definitions (elements, spells, skills) -│ │ │ ├── data/ # Game data (equipment, golems, recipes) -│ │ │ └── __tests__/ # Test files for game logic -│ │ └── db.ts, utils.ts -│ └── test/ # Test setup +│ └── 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 +│ ├── crafting-actions/ # Modular crafting stage handlers +│ ├── constants/ # Elements, spells, rooms, prestige +│ ├── data/ # Game data +│ │ ├── disciplines/ # Per-attunement discipline definitions +│ │ ├── 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 +│ ├── 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 -│ └── schema.prisma # SQLite schema -├── public/ # Static assets (logo.svg, robots.txt) +├── public/ # Static assets ├── docs/ # Project documentation -│ ├── AGENTS.md # Comprehensive architecture guide -│ ├── GAME_BRIEFING.md # Game design document -│ └── task/ # Task tracking documentation -├── .next/ # Next.js build output (generated) -├── node_modules/ # Dependencies (generated) -├── Configuration Files: -│ ├── package.json # Project metadata and scripts -│ ├── tsconfig.json # TypeScript configuration -│ ├── next.config.ts # Next.js config (standalone output) -│ ├── vitest.config.ts # Vitest test configuration -│ ├── eslint.config.mjs # ESLint configuration -│ ├── Dockerfile # Docker multi-stage build -│ ├── docker-compose.yml # Docker Compose setup -│ ├── Caddyfile # Reverse proxy configuration -│ └── .gitea/workflows/ # Gitea Actions CI/CD pipeline -└── README.md # This file +│ ├── AGENTS.md # Architecture guide for AI agents +│ └── GAME_BRIEFING.md # Comprehensive game design document +└── Configuration Files: + ├── package.json, tsconfig.json, next.config.ts + ├── vitest.config.ts, eslint.config.mjs + ├── Dockerfile, docker-compose.yml, Caddyfile + └── .gitea/workflows/ # Gitea Actions CI/CD pipeline ``` -For detailed architecture patterns and coding guidelines, see [AGENTS.md](./docs/AGENTS.md). +For detailed architecture patterns and coding guidelines, see [AGENTS.md](./AGENTS.md). --- @@ -232,36 +235,50 @@ The core resource of the game with 14 distinct types organized in a hierarchy: - **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) -**Key Files**: `src/lib/game/store.ts`, `src/lib/game/constants/elements.ts` +**Key Files**: `src/lib/game/stores/manaStore.ts`, `src/lib/game/constants/elements.ts` -### Skill Evolution System -Each skill progresses through 5 tiers with upgrades at levels 5 and 10 per tier: -- **Tier 1**: Basic functionality -- **Tier 2-5**: Unlock new mechanics and bonuses -- **Evolution Paths**: Defined in `src/lib/game/skill-evolution.ts` (~3400 lines) +### 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. + +- **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)` +- **Perks** unlock at XP thresholds (`once`, `capped`, or `infinite`) +- **Concurrent slots** start at 1 and unlock as total XP grows (max 4) + +**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: + +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. + +**Key Files**: `src/lib/game/data/guardian-data.ts`, `src/lib/game/data/guardian-encounters.ts` ### Combat System -- Cast-speed based spell casting with DPS calculations -- Elemental damage bonuses and effectiveness -- Multi-spell support from equipped weapons +- Cast-speed based spell casting with elemental effectiveness multipliers +- Enemy modifiers: Armored, Agile, Mage (barrier), Shielded, Swarm - Golem allies deal automatic damage each tick +- Discipline bonuses feed into damage via `getUnifiedEffects()` -**Key Files**: `src/lib/game/store.ts` (combat tick logic), `src/lib/game/constants/spells.ts` +**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**: Prepare equipment (ONLY way to disenchant existing enchantments) +2. **Prepare**: Ready equipment (ONLY stage where disenchanting is possible) 3. **Apply**: Apply designed enchantments (cannot re-enchant already enchanted gear) -**Key Files**: `src/lib/game/crafting-slice.ts`, `src/lib/game/data/enchantment-effects.ts` +**Key Files**: `src/lib/game/crafting-actions/`, `src/lib/game/data/enchantments/` ### Golemancy System - **Base Golems**: Earth (Fabricator 2), 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.ts`, `src/lib/game/store.ts` +**Key Files**: `src/lib/game/data/golems/`, `src/lib/game/stores/gameStore.ts` ### Prestige (Insight) Reset progress to gain Insight currency for permanent upgrades: @@ -274,7 +291,6 @@ Reset progress to gain Insight currency for permanent upgrades: ## Deployment ### Docker Deployment -The project includes Docker configuration for containerized deployment: ```bash # Build and run with Docker Compose @@ -286,7 +302,7 @@ 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` branches +- **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" @@ -316,26 +332,24 @@ We welcome contributions! Please follow these guidelines: ### Code Style - TypeScript throughout with strict typing - Use existing shadcn/ui components over custom implementations -- Follow the slice pattern for Zustand store actions -- Keep components focused (extract to separate files when >50 lines) +- Follow the modular store pattern (`src/lib/game/stores/`) +- Keep files under 400 lines (enforced by pre-commit hook) - Use path aliases: `@/*` maps to `./src/*` ### Adding New Features -For detailed patterns on adding new effects, skills, spells, or systems, see the comprehensive [AGENTS.md](./docs/AGENTS.md) guide, which includes: -- Architecture overview -- Coding patterns -- Git workflow (mandatory pull before work, commit & push after) -- Credentials for automation (if applicable) +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. --- ## Banned Content -The following content has been removed from the game and should not be re-added: +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 player) +- **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) @@ -345,14 +359,13 @@ The following content has been removed from the game and should not be re-added: - **Force** - Removed ### Banned Systems -- **Familiar System** - Removed in favor of Golemancy and Pact 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 --- ## License -This project is licensed under the MIT License - see the LICENSE section below for details. - ``` MIT License @@ -377,8 +390,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` -**Note**: A `LICENSE` file is not currently present in the project root. It is recommended to create one with the above MIT License text. - --- ## Acknowledgments @@ -393,4 +404,4 @@ SOFTWARE.

Climb the spire. Master the mana. Uncover the loop. -

+

\ No newline at end of file diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index 6f04272..48e7bd7 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,8 +1,8 @@ # Circular Dependencies -Generated: 2026-05-25T18:44:15.053Z +Generated: 2026-05-26T08:53:53.586Z Found: 6 circular chain(s) — these MUST be fixed before modifying involved files. -1. Processed 135 files (1.7s) (2 warnings) +1. Processed 135 files (1.5s) (2 warnings) 2. 1) utils/floor-utils.ts > utils/room-utils.ts > utils/enemy-utils.ts 3. 2) utils/floor-utils.ts > utils/room-utils.ts 4. 3) stores/gameStore.ts > stores/gameActions.ts diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index 5a8bc97..bdd3c02 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-05-25T18:44:13.132Z", + "generated": "2026-05-26T08:53:51.901Z", "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." }, @@ -123,6 +123,7 @@ "stores/craftingStore.types.ts" ], "crafting-apply.ts": [ + "constants.ts", "crafting-utils.ts", "data/attunements.ts", "data/enchantment-effects.ts", @@ -135,6 +136,7 @@ "types.ts" ], "crafting-design.ts": [ + "constants.ts", "data/attunements.ts", "data/enchantment-effects.ts", "data/equipment/index.ts", @@ -143,6 +145,7 @@ "types.ts" ], "crafting-equipment.ts": [ + "constants.ts", "data/crafting-recipes.ts", "data/equipment/index.ts", "types.ts", @@ -153,6 +156,7 @@ "types.ts" ], "crafting-prep.ts": [ + "constants.ts", "crafting-utils.ts", "types.ts" ], diff --git a/scorecard.png b/scorecard.png index e9377de5eccd323f035dfd0e3e5109d7ed2112bd..efe38c76b7a7cd5e0186bd3a9ec835cf18681827 100644 GIT binary patch literal 37488 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 L^>bP0l+XkK&~2EB literal 85625 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 = { export function AttunementStatus() { const attunements = useAttunementStore((s) => s.attunements); - const activeAttunements = Object.entries(attunements) - .filter(([, state]) => state.active) - .sort(([, a], [, b]) => { - const orderA = Object.values(ATTUNEMENTS_DEF).findIndex(d => d.id === a.id); - const orderB = Object.values(ATTUNEMENTS_DEF).findIndex(d => d.id === b.id); - return orderA - orderB; - }); + const attunementOrder = useMemo(() => { + const map = new Map(); + Object.values(ATTUNEMENTS_DEF).forEach((d, i) => map.set(d.id, i)); + return map; + }, []); + + const activeAttunements = useMemo(() => { + return Object.entries(attunements) + .filter(([, state]) => state.active) + .sort(([, a], [, b]) => (attunementOrder.get(a.id) ?? 0) - (attunementOrder.get(b.id) ?? 0)); + }, [attunements, attunementOrder]); const xpForNext = (level: number) => { if (level <= 1) return 0; diff --git a/src/components/game/ManaDisplay.tsx b/src/components/game/ManaDisplay.tsx index cbbb598..47669ef 100755 --- a/src/components/game/ManaDisplay.tsx +++ b/src/components/game/ManaDisplay.tsx @@ -67,7 +67,7 @@ export function ManaDisplay({ style={{ background: 'var(--mana-raw)', border: '1px solid var(--border-accent)', - color: '#0C1020', + color: 'var(--bg-gather-btn)', fontWeight: 600, }} onMouseDown={onGatherStart} diff --git a/src/components/game/tabs/AchievementsTab.tsx b/src/components/game/tabs/AchievementsTab.tsx index 7956ce6..0083de7 100644 --- a/src/components/game/tabs/AchievementsTab.tsx +++ b/src/components/game/tabs/AchievementsTab.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useMemo, useEffect } from 'react'; +import { useState, useMemo } from 'react'; import { useCombatStore } from '@/lib/game/stores'; import { ACHIEVEMENTS, @@ -164,14 +164,8 @@ function CategorySection({ export function AchievementsTab() { const achievements = useCombatStore((s) => s.achievements); - const [mounted, setMounted] = useState(false); const [collapsedCategories, setCollapsedCategories] = useState>({}); - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); - const byCategory = useMemo(() => getAchievementsByCategory(), []); const categories = useMemo( () => Object.keys(byCategory).sort(), @@ -188,14 +182,6 @@ export function AchievementsTab() { })); }; - if (!mounted) { - return ( -
- Loading achievements… -
- ); - } - return (
diff --git a/src/components/game/tabs/AttunementsTab.tsx b/src/components/game/tabs/AttunementsTab.tsx index ca2ab23..34fc545 100644 --- a/src/components/game/tabs/AttunementsTab.tsx +++ b/src/components/game/tabs/AttunementsTab.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect } from 'react'; +import { useState } from 'react'; import { useAttunementStore } from '@/lib/game/stores'; import { ATTUNEMENTS_DEF, ATTUNEMENT_SLOT_NAMES, getAttunementXPForLevel, MAX_ATTUNEMENT_LEVEL } from '@/lib/game/data/attunements'; import type { AttunementDef, AttunementState } from '@/lib/game/types'; @@ -157,24 +157,10 @@ function AttunementCard({ def, state }: AttunementCardProps) { export function AttunementsTab() { const attunements = useAttunementStore((s) => s.attunements); - const [mounted, setMounted] = useState(false); - - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); const allDefs = Object.values(ATTUNEMENTS_DEF); const unlockedCount = allDefs.filter((d) => isAttunementUnlocked(d.id, attunements)).length; - if (!mounted) { - return ( -
- Loading attunements… -
- ); - } - return (
diff --git a/src/components/game/tabs/DisciplinesTab.tsx b/src/components/game/tabs/DisciplinesTab.tsx index 4fd3b96..c924fa1 100644 --- a/src/components/game/tabs/DisciplinesTab.tsx +++ b/src/components/game/tabs/DisciplinesTab.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useState, useCallback } from 'react'; import { useDisciplineStore } from '@/lib/game/stores/discipline-slice'; import type { DisciplineDefinition } from '@/lib/game/types/disciplines'; import type { ManaType } from '@/lib/game/types/elements'; @@ -201,14 +201,8 @@ export const DisciplinesTab: React.FC = () => { const activate = useDisciplineStore((s) => s.activate); const deactivate = useDisciplineStore((s) => s.deactivate); - const [mounted, setMounted] = useState(false); const [activeAttunement, setActiveAttunement] = useState('base'); - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); - const handleToggle = useCallback((id: string, paused: boolean) => { if (paused) { activate(id); @@ -217,14 +211,6 @@ export const DisciplinesTab: React.FC = () => { } }, [activate, deactivate]); - if (!mounted) { - return ( -
- Loading disciplines… -
- ); - } - const activeTab = ATTUNEMENT_TABS.find((t) => t.key === activeAttunement); return ( diff --git a/src/components/game/tabs/EquipmentTab.tsx b/src/components/game/tabs/EquipmentTab.tsx index bb70dba..54225a5 100644 --- a/src/components/game/tabs/EquipmentTab.tsx +++ b/src/components/game/tabs/EquipmentTab.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect, useCallback, useMemo } from 'react'; +import { useState, useCallback, useMemo } from 'react'; import { useCraftingStore } from '@/lib/game/stores/craftingStore'; import type { EquipmentSlot } from '@/lib/game/types'; import { DebugName } from '@/components/game/debug/debug-context'; @@ -9,19 +9,12 @@ import { InventoryList } from './EquipmentTab/InventoryList'; import { EquipmentEffectsSummary } from './EquipmentTab/EquipmentEffectsSummary'; export function EquipmentTab() { - const [mounted, setMounted] = useState(false); - const equippedInstances = useCraftingStore((s) => s.equippedInstances); const equipmentInstances = useCraftingStore((s) => s.equipmentInstances); const storeEquipItem = useCraftingStore((s) => s.equipItem); const storeUnequipItem = useCraftingStore((s) => s.unequipItem); const storeDeleteEquipment = useCraftingStore((s) => s.deleteEquipmentInstance); - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); - const handleEquip = useCallback( (instanceId: string, slot: EquipmentSlot): boolean => { return storeEquipItem(instanceId, slot); @@ -51,14 +44,6 @@ export function EquipmentTab() { [equipmentInstances, equippedInstances] ); - if (!mounted) { - return ( -
- Loading equipment… -
- ); - } - return (
diff --git a/src/components/game/tabs/GolemancyTab.tsx b/src/components/game/tabs/GolemancyTab.tsx index 3365ee4..613e536 100644 --- a/src/components/game/tabs/GolemancyTab.tsx +++ b/src/components/game/tabs/GolemancyTab.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useState, useEffect, useCallback, useMemo } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { useCombatStore } from '@/lib/game/stores/combatStore'; import { useAttunementStore } from '@/lib/game/stores/attunementStore'; @@ -197,7 +197,6 @@ GolemCard.displayName = 'GolemCard'; // ─── Main Tab ──────────────────────────────────────────────────────────────── export const GolemancyTab: React.FC = () => { - const [mounted, setMounted] = useState(false); const [activeTier, setActiveTier] = useState('base'); const { golemancy, toggleGolem } = useCombatStore(useShallow(s => ({ @@ -210,11 +209,6 @@ export const GolemancyTab: React.FC = () => { elements: s.elements, }))); - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); - // Build attunement lookup for isGolemUnlocked const attunementLookup = useMemo(() => { const lookup: Record = {}; @@ -254,14 +248,6 @@ export const GolemancyTab: React.FC = () => { const golemSlots = getGolemSlots(fabricatorLevel); const enabledCount = golemancy.enabledGolems.length; - if (!mounted) { - return ( -
- Loading golemancy… -
- ); - } - const activeTierGolems = golemsByTier[activeTier] ?? []; return ( diff --git a/src/components/game/tabs/GuardianPactsTab.tsx b/src/components/game/tabs/GuardianPactsTab.tsx index 2115aab..cb50d40 100644 --- a/src/components/game/tabs/GuardianPactsTab.tsx +++ b/src/components/game/tabs/GuardianPactsTab.tsx @@ -1,6 +1,6 @@ 'use client'; -import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import React, { useState, useMemo, useCallback } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { usePrestigeStore } from '@/lib/game/stores/prestigeStore'; import { useManaStore } from '@/lib/game/stores/manaStore'; @@ -53,7 +53,6 @@ function groupFloorsByTier(floors: number[]): FloorTier[] { // ─── Main Tab ──────────────────────────────────────────────────────────────── export const GuardianPactsTab: React.FC = () => { - const [mounted, setMounted] = useState(false); const [activeTier, setActiveTier] = useState('all'); const { @@ -75,11 +74,6 @@ export const GuardianPactsTab: React.FC = () => { const rawMana = useManaStore(s => s.rawMana); const addLog = useUIStore(s => s.addLog); - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); - const guardianFloors = useMemo( () => getAllGuardianFloors(), [], @@ -126,14 +120,6 @@ export const GuardianPactsTab: React.FC = () => { return boonMap; }, [signedPacts]); - if (!mounted) { - return ( -
- Loading guardian pacts… -
- ); - } - return (
diff --git a/src/components/game/tabs/PrestigeTab.tsx b/src/components/game/tabs/PrestigeTab.tsx index 60c7626..a7e10da 100644 --- a/src/components/game/tabs/PrestigeTab.tsx +++ b/src/components/game/tabs/PrestigeTab.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect, useCallback } from 'react'; +import { useState, useCallback } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { usePrestigeStore, useGameStore } from '@/lib/game/stores'; import { PRESTIGE_DEF } from '@/lib/game/constants/prestige'; @@ -186,8 +186,6 @@ function ResetLoopSection({ loopInsight, onReset }: { loopInsight: number; onRes // ─── Main Component ─────────────────────────────────────────────────────────── export function PrestigeTab() { - const [mounted, setMounted] = useState(false); - const { insight, totalInsight, @@ -212,11 +210,6 @@ export function PrestigeTab() { const startNewLoop = useGameStore((s) => s.startNewLoop); - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); - const handlePurchase = useCallback((id: string) => { doPrestige(id); }, [doPrestige]); @@ -225,14 +218,6 @@ export function PrestigeTab() { startNewLoop(); }, [startNewLoop]); - if (!mounted) { - return ( -
- Loading prestige… -
- ); - } - const upgradeEntries = Object.entries(PRESTIGE_DEF); return ( diff --git a/src/components/game/tabs/SpireCombatPage/RoomDisplay.tsx b/src/components/game/tabs/SpireCombatPage/RoomDisplay.tsx index 104af78..b583392 100644 --- a/src/components/game/tabs/SpireCombatPage/RoomDisplay.tsx +++ b/src/components/game/tabs/SpireCombatPage/RoomDisplay.tsx @@ -71,7 +71,7 @@ function EnemyRow({ enemy }: { enemy: EnemyState }) { ); } -export function RoomDisplay({ floorState, _floor }: RoomDisplayProps) { +export function RoomDisplay({ floorState }: RoomDisplayProps) { // Guard against null/undefined/stale floorState if (!floorState || !floorState.roomType) { return ( diff --git a/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx b/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx index b66dd58..186ccf7 100644 --- a/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx +++ b/src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx @@ -47,7 +47,6 @@ function useSpireStats(prestigeUpgrades: Record, equippedInstanc // ─── Main Component ─────────────────────────────────────────────────────────── export function SpireCombatPage() { - const [mounted, setMounted] = useState(false); const [roomsCleared, setRoomsCleared] = useState(0); const { @@ -104,8 +103,6 @@ export function SpireCombatPage() { const totalRooms = useMemo(() => getRoomsForFloor(currentFloor), [currentFloor]); useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); setRoomsCleared(0); const newRoom = generateSpireFloorState(currentFloor, 0, totalRooms); setCurrentRoom(newRoom); @@ -166,14 +163,6 @@ export function SpireCombatPage() { addActivityLog('floor_transition', '🚪 Exited the Spire.'); }; - if (!mounted) { - return ( -
- Loading spire... -
- ); - } - return (
diff --git a/src/components/game/tabs/SpireSummaryTab.tsx b/src/components/game/tabs/SpireSummaryTab.tsx index fec86a4..a35ec2f 100644 --- a/src/components/game/tabs/SpireSummaryTab.tsx +++ b/src/components/game/tabs/SpireSummaryTab.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState, useEffect, useMemo } from 'react'; +import { useState, useMemo } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { useCombatStore, usePrestigeStore, fmt } from '@/lib/game/stores'; import { ELEMENT_OPPOSITES, FLOOR_ELEM_CYCLE } from '@/lib/game/constants'; @@ -311,8 +311,6 @@ function GuardianRosterItem({ floor, guardian, isDefeated }: { floor: number; gu // ─── Main Component ─────────────────────────────────────────────────────────── export function SpireSummaryTab() { - const [mounted, setMounted] = useState(false); - const { maxFloorReached, clearedFloors, @@ -327,11 +325,6 @@ export function SpireSummaryTab() { insight: s.insight, }))); - useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setMounted(true); - }, []); - const defeatedGuardians = useMemo(() => { return GUARDIAN_FLOORS.filter((floor) => clearedFloors[floor]); }, [clearedFloors]); @@ -346,14 +339,6 @@ export function SpireSummaryTab() { return Object.values(clearedFloors).filter(Boolean).length; }, [clearedFloors]); - if (!mounted) { - return ( -
- Loading spire data… -
- ); - } - return (
diff --git a/src/lib/game/crafting-apply.ts b/src/lib/game/crafting-apply.ts index d93011d..4d1ad5c 100644 --- a/src/lib/game/crafting-apply.ts +++ b/src/lib/game/crafting-apply.ts @@ -3,6 +3,7 @@ import type { EquipmentInstance, AppliedEnchantment, EnchantmentDesign, ApplicationProgress } from './types'; import { calculateApplicationTime, calculateApplicationManaPerHour } from './crafting-utils'; +import { HOURS_PER_TICK } from './constants'; import { hasSpecial, SPECIAL_EFFECTS } from './effects/special-effects'; import type { ComputedEffects } from './effects/upgrade-effects.types'; import type { AttunementState } from './types'; @@ -11,32 +12,16 @@ import { ENCHANTMENT_EFFECTS } from './data/enchantment-effects'; // ─── Application Validation ───────────────────────────────────────────────── -// Check if enchantment application can start export function canApplyEnchantment( instance: EquipmentInstance | undefined, design: EnchantmentDesign | undefined, currentAction: string ): { canApply: boolean; reason?: string } { - if (!instance) { - return { canApply: false, reason: 'Equipment instance not found' }; - } - - if (!design) { - return { canApply: false, reason: 'Enchantment design not found' }; - } - - if (currentAction !== 'meditate') { - return { canApply: false, reason: 'Must be in meditate state' }; - } - - if (!instance.tags?.includes('Ready for Enchantment')) { - return { canApply: false, reason: 'Equipment must be prepared for enchanting' }; - } - - if (instance.usedCapacity + design.totalCapacityUsed > instance.totalCapacity) { - return { canApply: false, reason: 'Not enough capacity on equipment' }; - } - + if (!instance) return { canApply: false, reason: 'Equipment instance not found' }; + if (!design) return { canApply: false, reason: 'Enchantment design not found' }; + if (currentAction !== 'meditate') return { canApply: false, reason: 'Must be in meditate state' }; + if (!instance.tags?.includes('Ready for Enchantment')) return { canApply: false, reason: 'Equipment must be prepared for enchanting' }; + if (instance.usedCapacity + design.totalCapacityUsed > instance.totalCapacity) return { canApply: false, reason: 'Not enough capacity on equipment' }; return { canApply: true }; } @@ -51,21 +36,18 @@ export interface ApplicationCosts { export function calculateApplicationCosts(design: EnchantmentDesign): ApplicationCosts { const time = calculateApplicationTime(design); const manaPerHour = calculateApplicationManaPerHour(design); - const manaPerTick = manaPerHour * 0.04; // HOURS_PER_TICK - + const manaPerTick = manaPerHour * HOURS_PER_TICK; return { time, manaPerHour, manaPerTick }; } // ─── Application Progress ─────────────────────────────────────────────────── -// Initialize application progress export function initializeApplicationProgress( equipmentInstanceId: string, designId: string, design: EnchantmentDesign ): ApplicationProgress { const costs = calculateApplicationCosts(design); - return { equipmentInstanceId, designId, @@ -77,7 +59,13 @@ export function initializeApplicationProgress( }; } -// Calculate application progress after a tick +// Free enchant chance per special effect +const FREE_ENCHANT_CHANCES: Record = { + [SPECIAL_EFFECTS.ENCHANT_PRESERVATION]: 0.25, + [SPECIAL_EFFECTS.THRIFTY_ENCHANTER]: 0.10, + [SPECIAL_EFFECTS.OPTIMIZED_ENCHANTING]: 0.25, +}; + export interface ApplicationTickResult { progress: number; manaSpent: number; @@ -93,20 +81,14 @@ export function calculateApplicationTick( manaPerTick: number, computedEffects: ComputedEffects ): ApplicationTickResult { - let progress = currentProgress + 0.04; + let progress = currentProgress + HOURS_PER_TICK; let manaSpent = currentManaSpent + manaPerTick; let manaConsumed = manaPerTick; let triggeredFreeEnchant = false; let freeEnchantChance = 0; - if (hasSpecial(computedEffects, SPECIAL_EFFECTS.ENCHANT_PRESERVATION)) { - freeEnchantChance += 0.25; - } - if (hasSpecial(computedEffects, SPECIAL_EFFECTS.THRIFTY_ENCHANTER)) { - freeEnchantChance += 0.10; - } - if (hasSpecial(computedEffects, SPECIAL_EFFECTS.OPTIMIZED_ENCHANTING)) { - freeEnchantChance += 0.25; + for (const [special, chance] of Object.entries(FREE_ENCHANT_CHANCES)) { + if (hasSpecial(computedEffects, special)) freeEnchantChance += chance; } if (freeEnchantChance > 0 && Math.random() < freeEnchantChance) { @@ -116,47 +98,32 @@ export function calculateApplicationTick( triggeredFreeEnchant = true; } - return { - progress, - manaSpent, - manaConsumed, - isComplete: progress >= required, - triggeredFreeEnchant, - }; + return { progress, manaSpent, manaConsumed, isComplete: progress >= required, triggeredFreeEnchant }; } // ─── Enchantment Application ──────────────────────────────────────────────── -// Apply enchantments to equipment instance +const PURE_ESSENCE_STACK_BONUS = 1.25; +const PURE_ESSENCE_COST_CAP = 100; + export function applyEnchantments( instance: EquipmentInstance, design: EnchantmentDesign, computedEffects: ComputedEffects -): { - updatedInstance: EquipmentInstance; - xpGained: number; - logMessage: string; -} { +): { updatedInstance: EquipmentInstance; xpGained: number; logMessage: string } { const isPureEssenceActive = hasSpecial(computedEffects, SPECIAL_EFFECTS.PURE_ESSENCE); const newEnchantments: AppliedEnchantment[] = design.effects.map(eff => { - let stacks = eff.stacks; - let actualCost = eff.capacityCost; - const effectDef = ENCHANTMENT_EFFECTS[eff.effectId]; - if (isPureEssenceActive && effectDef && effectDef.baseCapacityCost < 100) { - stacks = Math.ceil(stacks * 1.25); - } - + const bonusStacks = isPureEssenceActive && effectDef && effectDef.baseCapacityCost < PURE_ESSENCE_COST_CAP; return { effectId: eff.effectId, - stacks, - actualCost, + stacks: bonusStacks ? Math.ceil(eff.stacks * PURE_ESSENCE_STACK_BONUS) : eff.stacks, + actualCost: eff.capacityCost, }; }); const xpGained = calculateEnchantingXP(design.totalCapacityUsed); - const updatedInstance: EquipmentInstance = { ...instance, enchantments: [...instance.enchantments, ...newEnchantments], @@ -176,15 +143,12 @@ export function updateEnchanterAttunement( attunements: Record, xpGained: number ): Record { - if (!attunements?.enchanter?.active || xpGained <= 0) { - return attunements; - } + if (!attunements?.enchanter?.active || xpGained <= 0) return attunements; const enchanterState = attunements.enchanter; let newXP = enchanterState.experience + xpGained; let newLevel = enchanterState.level; - while (newLevel < MAX_ATTUNEMENT_LEVEL) { const xpNeeded = getAttunementXPForLevel(newLevel + 1); if (newXP >= xpNeeded) { @@ -197,11 +161,7 @@ export function updateEnchanterAttunement( return { ...attunements, - enchanter: { - ...enchanterState, - level: newLevel, - experience: newXP, - }, + enchanter: { ...enchanterState, level: newLevel, experience: newXP }, }; } @@ -222,7 +182,7 @@ export function resumeApplication() { // ─── Progress Calculations ────────────────────────────────────────────────── export function getApplicationManaCostForTick(manaPerHour: number): number { - return manaPerHour * 0.04; + return manaPerHour * HOURS_PER_TICK; } export function getApplicationRemainingTime(currentProgress: number, required: number): number { diff --git a/src/lib/game/crafting-design.ts b/src/lib/game/crafting-design.ts index d3dfe15..a507c1c 100644 --- a/src/lib/game/crafting-design.ts +++ b/src/lib/game/crafting-design.ts @@ -6,67 +6,55 @@ import type { ComputedEffects } from './effects/upgrade-effects.types'; import { calculateEnchantingXP } from './data/attunements'; import { ENCHANTMENT_EFFECTS, calculateEffectCapacityCost } from './data/enchantment-effects'; import { hasSpecial, SPECIAL_EFFECTS } from './effects/special-effects'; +import { HOURS_PER_TICK } from './constants'; import { EQUIPMENT_TYPES } from './data/equipment'; +// Progress per tick expressed as a fraction of HOURS_PER_TICK +const DESIGN_PROGRESS_PER_TICK = HOURS_PER_TICK; +const HASTY_ENCHANTER_BONUS_MULTIPLIER = 0.25; + // ─── Design Creation & Calculation ────────────────────────────────────────── -// Validate effects for a design against equipment category export function validateDesignEffects( effects: DesignEffect[], equipmentTypeId: string, enchantingLevel: number ): { valid: boolean; reason?: string } { - if (enchantingLevel < 1) { - return { valid: false, reason: 'Requires enchanting skill level 1' }; - } + if (enchantingLevel < 1) return { valid: false, reason: 'Requires enchanting skill level 1' }; const equipType = EQUIPMENT_TYPES[equipmentTypeId]; - if (!equipType) { - return { valid: false, reason: 'Invalid equipment type' }; - } - const category = equipType.category; - if (!category) { - return { valid: false, reason: 'Invalid equipment category' }; - } + if (!equipType || !equipType.category) return { valid: false, reason: 'Invalid equipment type or category' }; for (const eff of effects) { const effectDef = ENCHANTMENT_EFFECTS[eff.effectId]; - if (!effectDef) { - return { valid: false, reason: `Unknown effect: ${eff.effectId}` }; - } - if (!effectDef.allowedEquipmentCategories.includes(category)) { - return { valid: false, reason: `Effect ${eff.effectId} not allowed on ${category}` }; - } - if (eff.stacks > effectDef.maxStacks) { - return { valid: false, reason: `Stacks exceed maximum for ${eff.effectId}` }; + if (!effectDef) return { valid: false, reason: `Unknown effect: ${eff.effectId}` }; + if (!effectDef.allowedEquipmentCategories.includes(equipType.category)) { + return { valid: false, reason: `Effect ${eff.effectId} not allowed on ${equipType.category}` }; } + if (eff.stacks > effectDef.maxStacks) return { valid: false, reason: `Stacks exceed maximum for ${eff.effectId}` }; } return { valid: true }; } -// Create an enchantment design from validated inputs export function createEnchantmentDesign( name: string, equipmentType: string, effects: DesignEffect[], efficiencyBonus: number = 0 ): EnchantmentDesign { - const totalCapacityUsed = calculateDesignCapacityCost(effects, efficiencyBonus); - const designTime = calculateDesignTime(effects); - return { id: `design_${Date.now()}`, name, equipmentType, effects, - totalCapacityUsed, - designTime, + totalCapacityUsed: calculateDesignCapacityCost(effects, efficiencyBonus), + designTime: calculateDesignTime(effects), created: Date.now(), }; } -// ─── Capacity Cost Calculation ────────────────────────────────────────────── +// ─── Capacity & Time Calculations ─────────────────────────────────────────── export function calculateDesignCapacityCost(effects: DesignEffect[], efficiencyBonus: number = 0): number { return effects.reduce((total, eff) => total + calculateEffectCapacityCost(eff.effectId, eff.stacks, efficiencyBonus), 0); @@ -76,6 +64,25 @@ export function calculateTotalCapacityCost(design: EnchantmentDesign): number { return design.totalCapacityUsed; } +export function calculateDesignTime(effects: DesignEffect[]): number { + let time = 1; + for (const eff of effects) { + if (ENCHANTMENT_EFFECTS[eff.effectId]) time += 0.5 * eff.stacks; + } + return time; +} + +export function getDesignTimeWithHaste( + effects: DesignEffect[], + isRepeatDesign: boolean, + computedEffects: ComputedEffects +): number { + const time = calculateDesignTime(effects); + return isRepeatDesign && hasSpecial(computedEffects, SPECIAL_EFFECTS.HASTY_ENCHANTER) + ? time * 0.75 + : time; +} + // ─── XP & Progression ─────────────────────────────────────────────────────── export function calculateEnchantingXpFromDesign(design: EnchantmentDesign): number { @@ -88,37 +95,11 @@ export function calculateXpFromInstanceEnchantments( let totalXp = 0; for (const ench of instance.enchantments) { const effectDef = ENCHANTMENT_EFFECTS[ench.effectId]; - const baseCost = effectDef?.baseCapacityCost || 0; - totalXp += calculateEnchantingXP(baseCost * ench.stacks); + totalXp += calculateEnchantingXP((effectDef?.baseCapacityCost || 0) * ench.stacks); } return totalXp; } -// ─── Design Time Calculations ────────────────────────────────────────────── - -export function calculateDesignTime(effects: DesignEffect[]): number { - let time = 1; - for (const eff of effects) { - const effectDef = ENCHANTMENT_EFFECTS[eff.effectId]; - if (effectDef) { - time += 0.5 * eff.stacks; - } - } - return time; -} - -export function getDesignTimeWithHaste( - effects: DesignEffect[], - isRepeatDesign: boolean, - computedEffects: ComputedEffects -): number { - let time = calculateDesignTime(effects); - if (isRepeatDesign && hasSpecial(computedEffects, SPECIAL_EFFECTS.HASTY_ENCHANTER)) { - time *= 0.75; - } - return time; -} - // ─── Progress Calculations ────────────────────────────────────────────────── export interface DesignProgressUpdate { @@ -128,21 +109,23 @@ export interface DesignProgressUpdate { timeBonus: number; } +const INSTANT_DESIGN_CHANCE = 0.10; + export function calculateDesignProgress( currentProgress: number, required: number, computedEffects: ComputedEffects, isRepeatDesign: boolean ): DesignProgressUpdate { - let progress = currentProgress + 0.04; + let progress = currentProgress + DESIGN_PROGRESS_PER_TICK; let timeBonus = 0; if (isRepeatDesign && hasSpecial(computedEffects, SPECIAL_EFFECTS.HASTY_ENCHANTER)) { - timeBonus = 0.04 * 0.25; + timeBonus = DESIGN_PROGRESS_PER_TICK * HASTY_ENCHANTER_BONUS_MULTIPLIER; progress += timeBonus; } - if (hasSpecial(computedEffects, SPECIAL_EFFECTS.INSTANT_DESIGNS) && Math.random() < 0.10) { + if (hasSpecial(computedEffects, SPECIAL_EFFECTS.INSTANT_DESIGNS) && Math.random() < INSTANT_DESIGN_CHANCE) { progress = required; } @@ -165,8 +148,7 @@ export function isSecondDesignSlotAvailable( ): boolean { if (!designProgress && !designProgress2) return true; if (!designProgress && designProgress2) return false; - if (designProgress && !designProgress2 && hasEnchantMastery) return true; - return false; + return !!(designProgress && !designProgress2 && hasEnchantMastery); } // ─── Auto-save Completed Design ──────────────────────────────────────────── @@ -181,13 +163,12 @@ export function createCompletedDesignFromProgress( }, efficiencyBonus: number = 0 ): EnchantmentDesign { - const totalCapacityCost = calculateDesignCapacityCost(progressData.effects, efficiencyBonus); return { id: progressData.designId, name: progressData.name, equipmentType: progressData.equipmentType, effects: progressData.effects, - totalCapacityUsed: totalCapacityCost, + totalCapacityUsed: calculateDesignCapacityCost(progressData.effects, efficiencyBonus), designTime: progressData.required, created: Date.now(), }; @@ -206,13 +187,10 @@ export function filterDesignsByEquipment( equipment: { instanceId: string; totalCapacity: number; usedCapacity: number } | null ): DesignWithCapacityInfo[] { if (!equipment) return []; + const availableCapacity = equipment.totalCapacity - equipment.usedCapacity; return designs.map(design => ({ design, - fitsInEquipment: designFitsInEquipment(design, equipment), - availableCapacity: equipment.totalCapacity - equipment.usedCapacity, + fitsInEquipment: (equipment.usedCapacity || 0) + design.totalCapacityUsed <= equipment.totalCapacity, + availableCapacity, })); } - -function designFitsInEquipment(design: EnchantmentDesign, instance: { usedCapacity: number; totalCapacity: number }): boolean { - return (instance.usedCapacity || 0) + design.totalCapacityUsed <= instance.totalCapacity; -} diff --git a/src/lib/game/crafting-equipment.ts b/src/lib/game/crafting-equipment.ts index 00a16b7..860e6f5 100644 --- a/src/lib/game/crafting-equipment.ts +++ b/src/lib/game/crafting-equipment.ts @@ -6,10 +6,12 @@ import { CRAFTING_RECIPES, canCraftRecipe, type CraftingRecipe } from './data/cr import { EQUIPMENT_TYPES } from './data/equipment'; import { ok, fail, ErrorCode } from './utils/result'; import type { Result } from './utils/result'; +import { HOURS_PER_TICK } from './constants'; + +const MANA_REFUND_RATE = 0.5; // ─── Equipment Crafting Validation ────────────────────────────────────────── -// Check if equipment crafting can start export function canStartEquipmentCrafting( blueprintId: string, hasBlueprint: boolean, @@ -17,38 +19,27 @@ export function canStartEquipmentCrafting( currentMana: number, currentAction: string ): { canCraft: boolean; reason?: string; recipe?: CraftingRecipe; missingMaterials?: Record; missingMana?: number } { - if (currentAction !== 'meditate') { - return { canCraft: false, reason: 'Must be in meditate state' }; - } + if (currentAction !== 'meditate') return { canCraft: false, reason: 'Must be in meditate state' }; const recipe = CRAFTING_RECIPES[blueprintId]; - if (!recipe) { - return { canCraft: false, reason: 'Invalid blueprint' }; - } - - if (!hasBlueprint) { - return { canCraft: false, reason: 'Blueprint not acquired' }; - } + if (!recipe) return { canCraft: false, reason: 'Invalid blueprint' }; + if (!hasBlueprint) return { canCraft: false, reason: 'Blueprint not acquired' }; const { canCraft, missingMaterials } = canCraftRecipe(recipe, materials, currentMana); + if (canCraft) return { canCraft: true, recipe }; - if (!canCraft) { - const missingMana = Math.max(0, recipe.manaCost - currentMana); - return { - canCraft: false, - reason: missingMana > 0 ? 'Insufficient mana' : 'Missing materials', - recipe, - missingMaterials, - missingMana: missingMana > 0 ? missingMana : undefined, - }; - } - - return { canCraft: true, recipe }; + const missingManaAmount = Math.max(0, recipe.manaCost - currentMana); + return { + canCraft: false, + reason: missingManaAmount > 0 ? 'Insufficient mana' : 'Missing materials', + recipe, + missingMaterials, + missingMana: missingManaAmount > 0 ? missingManaAmount : undefined, + }; } // ─── Equipment Crafting Execution ─────────────────────────────────────────── -// Deduct crafting costs and initialize progress export interface CraftingInitResult { recipe: CraftingRecipe; newMaterials: Record; @@ -63,108 +54,80 @@ export function initializeEquipmentCrafting( ): CraftingInitResult { const recipe = CRAFTING_RECIPES[blueprintId]; - // Deduct materials const newMaterials = { ...materials }; for (const [matId, amount] of Object.entries(recipe.materials)) { newMaterials[matId] = (newMaterials[matId] || 0) - amount; - if (newMaterials[matId] <= 0) { - delete newMaterials[matId]; - } + if (newMaterials[matId] <= 0) delete newMaterials[matId]; } - // Create progress - const progress: EquipmentCraftingProgress = { - blueprintId, - equipmentTypeId: recipe.equipmentTypeId, - progress: 0, - required: recipe.craftTime, - manaSpent: recipe.manaCost, - }; - return { recipe, newMaterials, manaCost: recipe.manaCost, - progress, + progress: { + blueprintId, + equipmentTypeId: recipe.equipmentTypeId, + progress: 0, + required: recipe.craftTime, + manaSpent: recipe.manaCost, + }, }; } // ─── Crafting Progress ────────────────────────────────────────────────────── -// Calculate crafting progress after a tick export interface CraftingTickResult { progress: number; isComplete: boolean; } export function calculateCraftingTick(currentProgress: number, required: number): CraftingTickResult { - const progress = currentProgress + 0.04; // HOURS_PER_TICK - return { - progress, - isComplete: progress >= required, - }; + const progress = currentProgress + HOURS_PER_TICK; + return { progress, isComplete: progress >= required }; } // ─── Crafting Completion ─────────────────────────────────────────────────── -// Create equipment instance from completed crafting +const BASE_EQUIPMENT_QUALITY = 100; + export function completeEquipmentCrafting( blueprintId: string, recipe: CraftingRecipe -): Result<{ - instanceId: string; - instance: EquipmentInstance; - logMessage: string; -}> { +): Result<{ instanceId: string; instance: EquipmentInstance; logMessage: string }> { const equipType = EQUIPMENT_TYPES[recipe.equipmentTypeId]; - if (!equipType) { - return fail(ErrorCode.INVALID_EQUIPMENT_TYPE, `Invalid equipment type: ${recipe.equipmentTypeId}`); - } + if (!equipType) return fail(ErrorCode.INVALID_EQUIPMENT_TYPE, `Invalid equipment type: ${recipe.equipmentTypeId}`); const instanceId = `equip_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - const newInstance: EquipmentInstance = { - instanceId, - typeId: recipe.equipmentTypeId, - name: recipe.name, - enchantments: [], - usedCapacity: 0, - totalCapacity: equipType.baseCapacity, - rarity: recipe.rarity, - quality: 100, - tags: [], - }; - return ok({ instanceId, - instance: newInstance, + instance: { + instanceId, + typeId: recipe.equipmentTypeId, + name: recipe.name, + enchantments: [], + usedCapacity: 0, + totalCapacity: equipType.baseCapacity, + rarity: recipe.rarity, + quality: BASE_EQUIPMENT_QUALITY, + tags: [], + }, logMessage: `🔨 Crafted ${recipe.name}!`, }); } // ─── Crafting Cancellation ────────────────────────────────────────────────── -// Cancel active crafting and refund partial resources export interface CraftingCancelResult { manaRefund: number; logMessage: string; } -export function cancelEquipmentCrafting(_blueprintId: string, manaSpent: number): CraftingCancelResult { +export function cancelEquipmentCrafting(blueprintId: string, manaSpent: number): CraftingCancelResult { const recipe = CRAFTING_RECIPES[blueprintId]; - if (!recipe) { - return { - manaRefund: 0, - logMessage: 'Invalid crafting recipe.', - }; - } + if (!recipe) return { manaRefund: 0, logMessage: 'Invalid crafting recipe.' }; - // Refund 50% of mana - const manaRefund = Math.floor(manaSpent * 0.5); - - return { - manaRefund, - logMessage: `🚫 Equipment crafting cancelled. Refunded ${manaRefund} mana.`, - }; + const manaRefund = Math.floor(manaSpent * MANA_REFUND_RATE); + return { manaRefund, logMessage: `🚫 Equipment crafting cancelled. Refunded ${manaRefund} mana.` }; } // ─── Recipe Information ───────────────────────────────────────────────────── @@ -179,23 +142,16 @@ export function getCraftableRecipes( currentMana: number ): CraftingRecipe[] { const craftable: CraftingRecipe[] = []; - for (const blueprintId of blueprints) { const recipe = CRAFTING_RECIPES[blueprintId]; if (!recipe) continue; - - const { canCraft } = canCraftRecipe(recipe, materials, currentMana); - if (canCraft) { - craftable.push(recipe); - } + if (canCraftRecipe(recipe, materials, currentMana).canCraft) craftable.push(recipe); } - return craftable; } // ─── Material Management ──────────────────────────────────────────────────── -// Delete materials from inventory export function deleteMaterials(materialId: string, amount: number, materials: Record): { newMaterials: Record; deleted: number; @@ -204,25 +160,18 @@ export function deleteMaterials(materialId: string, amount: number, materials: R const deleted = Math.min(amount, currentAmount); const remaining = Math.max(0, currentAmount - amount); const newMaterials = { ...materials }; - if (remaining <= 0) { delete newMaterials[materialId]; } else { newMaterials[materialId] = remaining; } - - return { - newMaterials, - deleted, - }; + return { newMaterials, deleted }; } -// Get total material count export function getMaterialCount(materials: Record, materialId: string): number { return materials[materialId] || 0; } -// Add materials to inventory export function addMaterials(materials: Record, materialId: string, amount: number): Record { const newMaterials = { ...materials }; newMaterials[materialId] = (newMaterials[materialId] || 0) + amount; diff --git a/src/lib/game/crafting-prep.ts b/src/lib/game/crafting-prep.ts index cb447ee..b69f2ce 100644 --- a/src/lib/game/crafting-prep.ts +++ b/src/lib/game/crafting-prep.ts @@ -3,26 +3,21 @@ import type { EquipmentInstance, PreparationProgress } from './types'; import { calculatePrepTime, calculatePrepManaCost, calculateManaPerHourForPrep } from './crafting-utils'; +import { HOURS_PER_TICK } from './constants'; // ─── Preparation Validation ───────────────────────────────────────────────── -// Check if an equipment instance can be prepared export function canPrepareEquipment( instance: EquipmentInstance | undefined, currentTags: string[] ): { canPrepare: boolean; reason?: string } { - if (!instance) { - return { canPrepare: false, reason: 'Equipment instance not found' }; - } - - if (currentTags.includes('Ready for Enchantment')) { - return { canPrepare: false, reason: 'Equipment is already prepared for enchanting' }; - } - + if (!instance) return { canPrepare: false, reason: 'Equipment instance not found' }; + if (currentTags.includes('Ready for Enchantment')) return { canPrepare: false, reason: 'Equipment is already prepared for enchanting' }; return { canPrepare: true }; } -// Calculate preparation resource costs +// ─── Preparation Costs ────────────────────────────────────────────────────── + export interface PreparationCosts { time: number; manaTotal: number; @@ -34,30 +29,20 @@ export function calculatePreparationCosts(totalCapacity: number): PreparationCos const time = calculatePrepTime(totalCapacity); const manaTotal = calculatePrepManaCost(totalCapacity); const manaPerHour = calculateManaPerHourForPrep(totalCapacity, time); - const manaPerTick = manaPerHour * 0.04; // HOURS_PER_TICK - - return { time, manaTotal, manaPerHour, manaPerTick }; + return { time, manaTotal, manaPerHour, manaPerTick: manaPerHour * HOURS_PER_TICK }; } -// ─── Preparation Progress ─────────────────────────────────────────────────── - -// Initialize preparation progress export function initializePreparationProgress( equipmentInstanceId: string, totalCapacity: number, manaCostPaid: number = 0 ): PreparationProgress { const costs = calculatePreparationCosts(totalCapacity); - - return { - equipmentInstanceId, - progress: 0, - required: costs.time, - manaCostPaid, - }; + return { equipmentInstanceId, progress: 0, required: costs.time, manaCostPaid }; } -// Calculate updated preparation progress after a tick +// ─── Preparation Tick ─────────────────────────────────────────────────────── + export interface PreparationTickResult { progress: number; manaCostPaid: number; @@ -68,15 +53,14 @@ export interface PreparationTickResult { export function calculatePreparationTick( currentProgress: number, required: number, + currentManaCostPaid: number, manaPerTick: number ): PreparationTickResult { - const progress = currentProgress + 0.04; // HOURS_PER_TICK + const progress = currentProgress + HOURS_PER_TICK; const manaConsumed = manaPerTick; - const manaCostPaid = manaPerTick; // Accumulated - return { progress, - manaCostPaid, + manaCostPaid: currentManaCostPaid + manaConsumed, manaConsumed, isComplete: progress >= required, }; @@ -84,51 +68,40 @@ export function calculatePreparationTick( // ─── Preparation Completion ───────────────────────────────────────────────── -// Apply preparation completion to equipment instance +const BASE_DISENCHANT_RECOVERY_RATE = 0.1; +const DISENCHANT_RECOVERY_PER_LEVEL = 0.2; + export function completePreparation( instance: EquipmentInstance, - _manaSpent: number -): { - updatedInstance: EquipmentInstance; - manaRecovered: number; - logMessage: string; -} { - // Calculate mana recovery from disenchanting (disenchanting skill removed - Bug 13) - const disenchantLevel = 0; - const recoveryRate = 0.1 + disenchantLevel * 0.2; // 10% base + 20% per level - + disenchantLevel: number = 0 +): { updatedInstance: EquipmentInstance; manaRecovered: number; logMessage: string } { + const recoveryRate = BASE_DISENCHANT_RECOVERY_RATE + disenchantLevel * DISENCHANT_RECOVERY_PER_LEVEL; let totalRecovered = 0; for (const ench of instance.enchantments) { totalRecovered += Math.floor(ench.actualCost * recoveryRate); } - const updatedInstance: EquipmentInstance = { - ...instance, - enchantments: [], - usedCapacity: 0, - rarity: 'common', - tags: [...(instance.tags || []), 'Ready for Enchantment'], - }; - return { - updatedInstance, + updatedInstance: { + ...instance, + enchantments: [], + usedCapacity: 0, + rarity: 'common', + tags: [...(instance.tags || []), 'Ready for Enchantment'], + }, manaRecovered: totalRecovered, logMessage: `✅ Equipment prepared for enchanting! Recovered ${totalRecovered} mana.`, }; } -// Cancel preparation (no resource recovery for preparation itself) export function cancelPreparation() { - return { - logMessage: 'Preparation cancelled.', - }; + return { logMessage: 'Preparation cancelled.' }; } // ─── Preparation State Calculations ───────────────────────────────────────── export function getPreparationManaCostForTick(instance: EquipmentInstance): number { - const costs = calculatePreparationCosts(instance.totalCapacity); - return costs.manaPerTick; + return calculatePreparationCosts(instance.totalCapacity).manaPerTick; } export function getPreparationRemainingTime(currentProgress: number, required: number): number { diff --git a/src/lib/game/effects/dynamic-compute.ts b/src/lib/game/effects/dynamic-compute.ts index cf73b22..ec9eeb3 100644 --- a/src/lib/game/effects/dynamic-compute.ts +++ b/src/lib/game/effects/dynamic-compute.ts @@ -2,8 +2,22 @@ // Dynamic computation functions that depend on special effects import type { ComputedEffects } from './upgrade-effects.types'; + import { SPECIAL_EFFECTS, hasSpecial } from './special-effects'; +// Threshold ratios for mana-dependent effects (currentMana / maxMana) +const MANA_HIGH_THRESHOLD = 0.75; +const MANA_OVERPOWER_THRESHOLD = 0.8; +const MANA_BERSERKER_THRESHOLD = 0.5; +const MANA_LOW_THRESHOLD = 0.25; +const MANA_CRITICAL_THRESHOLD = 0.1; + +// Regen multipliers for mana-dependent thresholds +const MANA_TORRENT_MULTIPLIER = 1.5; +const MANA_CRISIS_MULTIPLIER = 1.5; // Desperate / Despair Wells +const PANIC_RESERVE_MULTIPLIER = 2.0; +const REGEN_BOOST_PER_100_MANA = 0.1; + /** * Compute regen with special effects that depend on dynamic values */ @@ -15,60 +29,51 @@ export function computeDynamicRegen( incursionStrength: number ): number { let regen = baseRegen; - - // Mana Cascade: +0.1 regen per 100 max mana + + // Per-100-max-mana regen bonuses + const manaHundreds = Math.floor(maxMana / 100); if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_CASCADE)) { - regen += Math.floor(maxMana / 100) * 0.1; + regen += manaHundreds * REGEN_BOOST_PER_100_MANA; } - - // Mana Waterfall: +0.25 regen per 100 max mana (upgraded cascade) if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_WATERFALL)) { - regen += Math.floor(maxMana / 100) * 0.25; + regen += manaHundreds * 0.25; } - - // Mana Torrent: +50% regen when above 75% mana - if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_TORRENT) && currentMana > maxMana * 0.75) { - regen *= 1.5; - } - - // Desperate Wells / Despair Wells: +50% regen when below 25% mana - if ((hasSpecial(effects, SPECIAL_EFFECTS.DESPERATE_WELLS) || hasSpecial(effects, SPECIAL_EFFECTS.DESPAIR_WELLS)) && currentMana < maxMana * 0.25) { - regen *= 1.5; - } - - // Panic Reserve: +100% regen when below 10% mana - if (hasSpecial(effects, SPECIAL_EFFECTS.PANIC_RESERVE) && currentMana < maxMana * 0.1) { - regen *= 2.0; - } - - // Deep Reserve: +0.5 regen per 100 max mana if (hasSpecial(effects, SPECIAL_EFFECTS.DEEP_RESERVE)) { - regen += Math.floor(maxMana / 100) * 0.5; + regen += manaHundreds * 0.5; } - - // Mana Core: 0.5% of max mana added as regen + + // Fractional max-mana regen if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_CORE)) { regen += maxMana * 0.005; } - - // Mana Tide: Regen pulses ±50% (sinusoidal based on time) + + // Mana Tide: sinusoidal pulse if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_TIDE)) { regen *= (1.0 + 0.5 * Math.sin(Date.now() / 10000)); } - - // Eternal Flow: Regen immune to ALL penalties + + // Mana-ratio-dependent multipliers + const manaRatio = currentMana / maxMana; + if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_TORRENT) && manaRatio > MANA_HIGH_THRESHOLD) { + regen *= MANA_TORRENT_MULTIPLIER; + } + if ((hasSpecial(effects, SPECIAL_EFFECTS.DESPERATE_WELLS) || hasSpecial(effects, SPECIAL_EFFECTS.DESPAIR_WELLS)) && manaRatio < MANA_LOW_THRESHOLD) { + regen *= MANA_CRISIS_MULTIPLIER; + } + if (hasSpecial(effects, SPECIAL_EFFECTS.PANIC_RESERVE) && manaRatio < MANA_CRITICAL_THRESHOLD) { + regen *= PANIC_RESERVE_MULTIPLIER; + } + + // Eternal Flow: skip incursion + multiplier below if (hasSpecial(effects, SPECIAL_EFFECTS.ETERNAL_FLOW)) { return regen * effects.regenMultiplier; } - - // Steady Stream: Regen immune to incursion (skip incursion penalty only) - if (hasSpecial(effects, SPECIAL_EFFECTS.STEADY_STREAM)) { - // incursion penalty is skipped, but regenMultiplier still applies below - } else { - // Apply incursion penalty + + // Steady Stream: skip incursion only + if (!hasSpecial(effects, SPECIAL_EFFECTS.STEADY_STREAM)) { regen *= (1 - incursionStrength); } - + return regen * effects.regenMultiplier; } @@ -79,46 +84,7 @@ export function computeDynamicClickMana( effects: ComputedEffects, baseClickMana: number ): number { - let clickMana = baseClickMana; - - // Mana Echo: 10% chance to gain double mana from clicks - // Note: The chance is handled in the click handler, this just returns the base - // The click handler should check hasSpecial and apply the 10% chance - - // Mana Genesis: Generate 1% of max mana per hour passively - // This is handled in the game loop (store.ts), not here - - // Mana Heart: +10% max mana per loop (permanent) - // This is applied during loop reset in store.ts - - return Math.floor((clickMana + effects.clickManaBonus) * effects.clickManaMultiplier); + return Math.floor((baseClickMana + effects.clickManaBonus) * effects.clickManaMultiplier); } -/** - * Compute damage with special effects - */ -export function computeDynamicDamage( - effects: ComputedEffects, - baseDamage: number, - _floorHPPct: number, - currentMana: number, - maxMana: number -): number { - let damage = baseDamage * effects.baseDamageMultiplier; - - // Overpower: +50% damage when mana above 80% - if (hasSpecial(effects, SPECIAL_EFFECTS.OVERPOWER) && currentMana >= maxMana * 0.8) { - damage *= 1.5; - } - - // Berserker: +50% damage when below 50% mana - if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && currentMana < maxMana * 0.5) { - damage *= 1.5; - } - - // Combo Master: Every 5th attack deals 3x damage - // Note: The hit counter is tracked in game state, this just returns the multiplier - // The combat handler should check hasSpecial and the hit count - - return damage + effects.baseDamageBonus; -} + diff --git a/src/lib/game/stores/gameStore.ts b/src/lib/game/stores/gameStore.ts index 0c6c965..9677ec6 100644 --- a/src/lib/game/stores/gameStore.ts +++ b/src/lib/game/stores/gameStore.ts @@ -82,277 +82,258 @@ export const useGameStore = create()( if (ctx.ui.gameOver || ctx.ui.paused) return; - // ── Phase 2: Compute — derive all updates ─────────────────────────── - const writes: TickWrites = { logs: [] }; - const addLog = (msg: string) => writes.logs.push(msg); - - // Compute equipment and discipline effects - const equipmentEffects = computeEquipmentEffects( - ctx.crafting.equipmentInstances || {}, - ctx.crafting.equippedInstances || {} - ); - const disciplineEffects = computeDisciplineEffects(); - const allSpecials = new Set([ - ...equipmentEffects.specials, - ...disciplineEffects.specials, - ]); - const effects = { specials: allSpecials } as ComputedEffects; - - const maxMana = computeMaxMana( - { skills: {}, prestigeUpgrades: ctx.prestige.prestigeUpgrades, skillUpgrades: {}, skillTiers: {} }, - undefined, - disciplineEffects, - ); - const baseRegen = computeRegen( - { skills: {}, prestigeUpgrades: ctx.prestige.prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, attunements: {} }, - undefined, - disciplineEffects, - ); - - // Time progression - let hour = ctx.game.hour + HOURS_PER_TICK; - let day = ctx.game.day; - if (hour >= 24) { - hour -= 24; - day += 1; - } - - // Check for loop end - if (day > MAX_DAY) { - const insightGained = calcInsight({ - maxFloorReached: ctx.combat.maxFloorReached, - totalManaGathered: ctx.mana.totalManaGathered, - signedPacts: ctx.prestige.signedPacts, - prestigeUpgrades: ctx.prestige.prestigeUpgrades, - skills: {}, - }, disciplineEffects); - - addLog(`⏰ The loop ends. Gained ${insightGained} Insight.`); - writes.ui = { ...(writes.ui || {}), gameOver: true, victory: false }; - writes.prestige = { ...(writes.prestige || {}), loopInsight: insightGained }; - writes.game = { day, hour }; - applyTickWrites(writes, { + // Shared setters object — used by every applyTickWrites call below + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const storeSetters = { setGame: set, - setUI: (w) => useUIStore.setState(w), - setPrestige: (w) => usePrestigeStore.setState(w), - setMana: (w) => useManaStore.setState(w), - setCombat: (w) => useCombatStore.setState(w), - setCrafting: (w) => useCraftingStore.setState(w), - setAttunement: (w) => useAttunementStore.setState(w), - setDiscipline: (w) => useDisciplineStore.setState(w), - addLogs: (msgs) => msgs.forEach((m) => useUIStore.getState().addLog(m)), - }); - return; - } + setUI: (w: any) => useUIStore.setState(w), + setPrestige: (w: any) => usePrestigeStore.setState(w), + setMana: (w: any) => useManaStore.setState(w), + setCombat: (w: any) => useCombatStore.setState(w), + setCrafting: (w: any) => useCraftingStore.setState(w), + setAttunement: (w: any) => useAttunementStore.setState(w), + setDiscipline: (w: any) => useDisciplineStore.setState(w), + addLogs: (msgs: string[]) => msgs.forEach((m) => useUIStore.getState().addLog(m)), + }; - // Check for victory - if (ctx.combat.maxFloorReached >= 100 && ctx.prestige.signedPacts.includes(100)) { - const insightGained = calcInsight({ - maxFloorReached: ctx.combat.maxFloorReached, - totalManaGathered: ctx.mana.totalManaGathered, - signedPacts: ctx.prestige.signedPacts, - prestigeUpgrades: ctx.prestige.prestigeUpgrades, - skills: {}, - }, disciplineEffects) * 3; + // ── Phase 2: Compute — derive all updates ─────────────────────────── + const writes: TickWrites = { logs: [] }; + const addLog = (msg: string) => writes.logs.push(msg); - addLog(`🏆 VICTORY! The Awakened One falls! Gained ${insightGained} Insight!`); - writes.ui = { ...(writes.ui || {}), gameOver: true, victory: true }; - writes.prestige = { ...(writes.prestige || {}), loopInsight: insightGained }; - applyTickWrites(writes, { - setGame: set, - setUI: (w) => useUIStore.setState(w), - setPrestige: (w) => usePrestigeStore.setState(w), - setMana: (w) => useManaStore.setState(w), - setCombat: (w) => useCombatStore.setState(w), - setCrafting: (w) => useCraftingStore.setState(w), - setAttunement: (w) => useAttunementStore.setState(w), - setDiscipline: (w) => useDisciplineStore.setState(w), - addLogs: (msgs) => msgs.forEach((m) => useUIStore.getState().addLog(m)), - }); - return; - } + // Compute equipment and discipline effects + const equipmentEffects = computeEquipmentEffects( + ctx.crafting.equipmentInstances || {}, + ctx.crafting.equippedInstances || {} + ); + const disciplineEffects = computeDisciplineEffects(); + const allSpecials = new Set([ + ...equipmentEffects.specials, + ...disciplineEffects.specials, + ]); + const effects = { specials: allSpecials } as ComputedEffects; - // Incursion - const incursionStrength = getIncursionStrength(day, hour); - - // Meditation bonus tracking - let meditateTicks = ctx.mana.meditateTicks; - let meditationMultiplier = 1; - - if (ctx.combat.currentAction === 'meditate') { - meditateTicks++; - meditationMultiplier = getMeditationBonus(meditateTicks, {}, 1); - } else { - meditateTicks = 0; - } - - // Calculate total attunement conversion per tick - let totalConversionPerTick = 0; - Object.entries(ctx.attunement.attunements).forEach(([id, state]) => { - if (!state.active) return; - const def = ATTUNEMENTS_DEF[id]; - if (!def || def.conversionRate <= 0 || !def.primaryManaType) return; - const scaledRate = getAttunementConversionRate(id, state.level || 1); - totalConversionPerTick += scaledRate * HOURS_PER_TICK; - }); - - // Calculate effective regen - const effectiveRegen = Math.max(0, baseRegen * (1 - incursionStrength) * meditationMultiplier - totalConversionPerTick); - - // Mana regeneration - let rawMana = Math.min(ctx.mana.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana); - let elements = { ...ctx.mana.elements }; - - // Apply attunement conversion - Object.entries(ctx.attunement.attunements).forEach(([id, state]) => { - if (!state.active) return; - const def = ATTUNEMENTS_DEF[id]; - if (!def || def.conversionRate <= 0 || !def.primaryManaType) return; - const scaledRate = getAttunementConversionRate(id, state.level || 1); - const conversionThisTick = scaledRate * HOURS_PER_TICK; - if (elements[def.primaryManaType]) { - elements[def.primaryManaType].current = Math.min( - elements[def.primaryManaType].max, - elements[def.primaryManaType].current + conversionThisTick - ); - } - }); - let totalManaGathered = ctx.mana.totalManaGathered; - - // Convert action — delegate to manaStore - if (ctx.combat.currentAction === 'convert') { - const convertResult = useManaStore.getState().processConvertAction(rawMana); - if (convertResult) { - rawMana = convertResult.rawMana; - elements = convertResult.elements; - } - } - - // Pact ritual progress - if (ctx.prestige.pactRitualFloor !== null) { - const guardian = getGuardianForFloor(ctx.prestige.pactRitualFloor); - if (guardian) { - const pactAffinityBonus = 1 - (ctx.prestige.prestigeUpgrades.pactAffinity || 0) * 0.1; - const requiredTime = guardian.pactTime * pactAffinityBonus; - const newProgress = ctx.prestige.pactRitualProgress + HOURS_PER_TICK; - - if (newProgress >= requiredTime) { - addLog(`📜 Pact signed with ${guardian.name}! You have gained their boons.`); - writes.prestige = { - ...(writes.prestige || {}), - signedPacts: [...ctx.prestige.signedPacts, ctx.prestige.pactRitualFloor], - defeatedGuardians: ctx.prestige.defeatedGuardians.filter(f => f !== ctx.prestige.pactRitualFloor), - pactRitualFloor: null, - pactRitualProgress: 0, - }; - } else { - writes.prestige = { - ...(writes.prestige || {}), - pactRitualProgress: newProgress, - }; - } - } - } - - // Discipline tick — process active disciplines (XP accrual + mana drain) - const disciplineResult = useDisciplineStore.getState().processTick({ - rawMana, - elements, - }); - rawMana = disciplineResult.rawMana; - elements = disciplineResult.elements; - - // Apply per-element regen from discipline effects (regen_{element}) - for (const [key, value] of Object.entries(disciplineEffects.bonuses)) { - if (key.startsWith('regen_') && key !== 'regenBonus') { - const element = key.replace('regen_', ''); - if (elements[element]) { - elements[element] = { - ...elements[element], - current: Math.min( - elements[element].max, - elements[element].current + value * HOURS_PER_TICK, - ), - }; - } - } - } - - // Unlock enchantment effects from newly unlocked discipline perks - if (disciplineResult.unlockedEffects.length > 0) { - useCraftingStore.getState().unlockEffects(disciplineResult.unlockedEffects); - for (const effectId of disciplineResult.unlockedEffects) { - addLog(`✨ Discipline insight unlocked: ${effectId}`); - } - } - - // Combat — delegate to combatStore - if (ctx.combat.currentAction === 'climb') { - const combatResult = useCombatStore.getState().processCombatTick( - rawMana, - elements, - maxMana, - 1, - (floor, wasGuardian) => { - if (wasGuardian) { - const defeatedGuardian = getGuardianForFloor(floor); - addLog(`\u2694\ufe0f ${defeatedGuardian?.name || 'Guardian'} defeated! Visit the Grimoire to sign a pact.`); - } else if (floor % 5 === 0) { - addLog(`🏰 Floor ${floor} cleared!`); - } - }, - (damage) => { - let dmg = damage; - if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && ctx.combat.floorHP / ctx.combat.floorMaxHP < 0.25) { - dmg *= 2; - } - if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) { - dmg *= 1.5; - } - return { rawMana, elements, modifiedDamage: dmg }; - }, - ctx.prestige.signedPacts, + const maxMana = computeMaxMana( + { skills: {}, prestigeUpgrades: ctx.prestige.prestigeUpgrades, skillUpgrades: {}, skillTiers: {} }, + undefined, + disciplineEffects, + ); + const baseRegen = computeRegen( + { skills: {}, prestigeUpgrades: ctx.prestige.prestigeUpgrades, skillUpgrades: {}, skillTiers: {}, attunements: {} }, + undefined, + disciplineEffects, ); - rawMana = combatResult.rawMana; - elements = combatResult.elements; - totalManaGathered += combatResult.totalManaGathered || 0; - - if (combatResult.logMessages) { - combatResult.logMessages.forEach(msg => addLog(msg)); + // Time progression + let hour = ctx.game.hour + HOURS_PER_TICK; + let day = ctx.game.day; + if (hour >= 24) { + hour -= 24; + day += 1; } - writes.combat = { - ...(writes.combat || {}), - currentFloor: combatResult.currentFloor, - floorHP: combatResult.floorHP, - floorMaxHP: combatResult.floorMaxHP, - maxFloorReached: combatResult.maxFloorReached, - castProgress: combatResult.castProgress, - equipmentSpellStates: combatResult.equipmentSpellStates, + // Shared insight params — reused for both loop-end and victory + const insightParams = { + maxFloorReached: ctx.combat.maxFloorReached, + totalManaGathered: ctx.mana.totalManaGathered, + signedPacts: ctx.prestige.signedPacts, + prestigeUpgrades: ctx.prestige.prestigeUpgrades, + skills: {} as Record, }; - } - // ── Phase 3: Write — batch all state updates ───────────────────────── - writes.game = { day, hour, incursionStrength }; - writes.mana = { - rawMana, - meditateTicks, - totalManaGathered, - elements, - }; + // Check for loop end + if (day > MAX_DAY) { + const insightGained = calcInsight(insightParams, disciplineEffects); - applyTickWrites(writes, { - setGame: set, - setUI: (w) => useUIStore.setState(w), - setPrestige: (w) => usePrestigeStore.setState(w), - setMana: (w) => useManaStore.setState(w), - setCombat: (w) => useCombatStore.setState(w), - setCrafting: (w) => useCraftingStore.setState(w), - setAttunement: (w) => useAttunementStore.setState(w), - setDiscipline: (w) => useDisciplineStore.setState(w), - addLogs: (msgs) => msgs.forEach((m) => useUIStore.getState().addLog(m)), - }); + addLog(`⏰ The loop ends. Gained ${insightGained} Insight.`); + writes.ui = { ...(writes.ui || {}), gameOver: true, victory: false }; + writes.prestige = { ...(writes.prestige || {}), loopInsight: insightGained }; + writes.game = { day, hour }; + applyTickWrites(writes, storeSetters); + return; + } + + // Check for victory (3× insight multiplier) + if (ctx.combat.maxFloorReached >= 100 && ctx.prestige.signedPacts.includes(100)) { + const insightGained = calcInsight(insightParams, disciplineEffects) * 3; + + addLog(`🏆 VICTORY! The Awakened One falls! Gained ${insightGained} Insight!`); + writes.ui = { ...(writes.ui || {}), gameOver: true, victory: true }; + writes.prestige = { ...(writes.prestige || {}), loopInsight: insightGained }; + applyTickWrites(writes, storeSetters); + return; + } + + // Incursion + const incursionStrength = getIncursionStrength(day, hour); + + // Meditation bonus tracking + let meditateTicks = ctx.mana.meditateTicks; + let meditationMultiplier = 1; + + if (ctx.combat.currentAction === 'meditate') { + meditateTicks++; + meditationMultiplier = getMeditationBonus(meditateTicks, {}, 1); + } else { + meditateTicks = 0; + } + + // Calculate total attunement conversion per tick + let totalConversionPerTick = 0; + Object.entries(ctx.attunement.attunements).forEach(([id, state]) => { + if (!state.active) return; + const def = ATTUNEMENTS_DEF[id]; + if (!def || def.conversionRate <= 0 || !def.primaryManaType) return; + const scaledRate = getAttunementConversionRate(id, state.level || 1); + totalConversionPerTick += scaledRate * HOURS_PER_TICK; + }); + + // Calculate effective regen + const effectiveRegen = Math.max(0, baseRegen * (1 - incursionStrength) * meditationMultiplier - totalConversionPerTick); + + // Mana regeneration + let rawMana = Math.min(ctx.mana.rawMana + effectiveRegen * HOURS_PER_TICK, maxMana); + let elements = { ...ctx.mana.elements }; + + // Apply attunement conversion + Object.entries(ctx.attunement.attunements).forEach(([id, state]) => { + if (!state.active) return; + const def = ATTUNEMENTS_DEF[id]; + if (!def || def.conversionRate <= 0 || !def.primaryManaType) return; + const scaledRate = getAttunementConversionRate(id, state.level || 1); + const conversionThisTick = scaledRate * HOURS_PER_TICK; + if (elements[def.primaryManaType]) { + elements[def.primaryManaType].current = Math.min( + elements[def.primaryManaType].max, + elements[def.primaryManaType].current + conversionThisTick + ); + } + }); + let totalManaGathered = ctx.mana.totalManaGathered; + + // Convert action — delegate to manaStore + if (ctx.combat.currentAction === 'convert') { + const convertResult = useManaStore.getState().processConvertAction(rawMana); + if (convertResult) { + rawMana = convertResult.rawMana; + elements = convertResult.elements; + } + } + + // Pact ritual progress + if (ctx.prestige.pactRitualFloor !== null) { + const guardian = getGuardianForFloor(ctx.prestige.pactRitualFloor); + if (guardian) { + const pactAffinityBonus = 1 - (ctx.prestige.prestigeUpgrades.pactAffinity || 0) * 0.1; + const requiredTime = guardian.pactTime * pactAffinityBonus; + const newProgress = ctx.prestige.pactRitualProgress + HOURS_PER_TICK; + + if (newProgress >= requiredTime) { + addLog(`📜 Pact signed with ${guardian.name}! You have gained their boons.`); + writes.prestige = { + ...(writes.prestige || {}), + signedPacts: [...ctx.prestige.signedPacts, ctx.prestige.pactRitualFloor], + defeatedGuardians: ctx.prestige.defeatedGuardians.filter(f => f !== ctx.prestige.pactRitualFloor), + pactRitualFloor: null, + pactRitualProgress: 0, + }; + } else { + writes.prestige = { + ...(writes.prestige || {}), + pactRitualProgress: newProgress, + }; + } + } + } + + // Discipline tick — process active disciplines (XP accrual + mana drain) + const disciplineResult = useDisciplineStore.getState().processTick({ + rawMana, + elements, + }); + rawMana = disciplineResult.rawMana; + elements = disciplineResult.elements; + + // Apply per-element regen from discipline effects (regen_{element}) + for (const [key, value] of Object.entries(disciplineEffects.bonuses)) { + if (key.startsWith('regen_') && key !== 'regenBonus') { + const element = key.replace('regen_', ''); + if (elements[element]) { + elements[element] = { + ...elements[element], + current: Math.min( + elements[element].max, + elements[element].current + value * HOURS_PER_TICK, + ), + }; + } + } + } + + // Unlock enchantment effects from newly unlocked discipline perks + if (disciplineResult.unlockedEffects.length > 0) { + useCraftingStore.getState().unlockEffects(disciplineResult.unlockedEffects); + for (const effectId of disciplineResult.unlockedEffects) { + addLog(`✨ Discipline insight unlocked: ${effectId}`); + } + } + + // Combat — delegate to combatStore + if (ctx.combat.currentAction === 'climb') { + const combatResult = useCombatStore.getState().processCombatTick( + rawMana, + elements, + maxMana, + 1, + (floor, wasGuardian) => { + if (wasGuardian) { + const defeatedGuardian = getGuardianForFloor(floor); + addLog(`\u2694\ufe0f ${defeatedGuardian?.name || 'Guardian'} defeated! Visit the Grimoire to sign a pact.`); + } else if (floor % 5 === 0) { + addLog(`🏰 Floor ${floor} cleared!`); + } + }, + (damage) => { + let dmg = damage; + if (hasSpecial(effects, SPECIAL_EFFECTS.EXECUTIONER) && ctx.combat.floorHP / ctx.combat.floorMaxHP < 0.25) { + dmg *= 2; + } + if (hasSpecial(effects, SPECIAL_EFFECTS.BERSERKER) && rawMana < maxMana * 0.5) { + dmg *= 1.5; + } + return { rawMana, elements, modifiedDamage: dmg }; + }, + ctx.prestige.signedPacts, + ); + + rawMana = combatResult.rawMana; + elements = combatResult.elements; + totalManaGathered += combatResult.totalManaGathered || 0; + + if (combatResult.logMessages) { + combatResult.logMessages.forEach(msg => addLog(msg)); + } + + writes.combat = { + ...(writes.combat || {}), + currentFloor: combatResult.currentFloor, + floorHP: combatResult.floorHP, + floorMaxHP: combatResult.floorMaxHP, + maxFloorReached: combatResult.maxFloorReached, + castProgress: combatResult.castProgress, + equipmentSpellStates: combatResult.equipmentSpellStates, + }; + } + + // ── Phase 3: Write — batch all state updates ───────────────────────── + writes.game = { day, hour, incursionStrength }; + writes.mana = { + rawMana, + meditateTicks, + totalManaGathered, + elements, + }; + + applyTickWrites(writes, storeSetters); } catch (error: unknown) { // Log error to UI store if available, otherwise console error try { diff --git a/src/lib/game/stores/manaStore.ts b/src/lib/game/stores/manaStore.ts index 4911b34..c5ecca6 100755 --- a/src/lib/game/stores/manaStore.ts +++ b/src/lib/game/stores/manaStore.ts @@ -86,7 +86,6 @@ export const useManaStore = create()( spendRawMana: (amount: number) => { const state = get(); if (state.rawMana < amount) return false; - set({ rawMana: state.rawMana - amount }); return true; }, @@ -98,71 +97,35 @@ export const useManaStore = create()( })); }, - setMeditateTicks: (ticks: number) => { - set({ meditateTicks: ticks }); - }, - - incrementMeditateTicks: () => { - set((state) => ({ meditateTicks: state.meditateTicks + 1 })); - }, - - resetMeditateTicks: () => { - set({ meditateTicks: 0 }); - }, + setMeditateTicks: (ticks: number) => set({ meditateTicks: ticks }), + incrementMeditateTicks: () => set((s) => ({ meditateTicks: s.meditateTicks + 1 })), + resetMeditateTicks: () => set({ meditateTicks: 0 }), convertMana: (element: string, amount: number) => { const state = get(); const elem = state.elements[element]; - if (!elem?.unlocked) { - return fail(ErrorCode.ELEMENT_NOT_UNLOCKED, `Element ${element} is not unlocked`); - } + if (!elem?.unlocked) return fail(ErrorCode.ELEMENT_NOT_UNLOCKED, `Element ${element} is not unlocked`); const cost = MANA_PER_ELEMENT * amount; - if (state.rawMana < cost) { - return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`); - } - if (elem.current >= elem.max) { - return fail(ErrorCode.ELEMENT_MAX_CAPACITY, `Element ${element} is at max capacity`); - } + if (state.rawMana < cost) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`); + if (elem.current >= elem.max) return fail(ErrorCode.ELEMENT_MAX_CAPACITY, `Element ${element} is at max capacity`); - const canConvert = Math.min( - amount, - Math.floor(state.rawMana / MANA_PER_ELEMENT), - elem.max - elem.current - ); - - if (canConvert <= 0) { - return fail(ErrorCode.INVALID_INPUT, 'Cannot convert 0 or negative amount'); - } + const canConvert = Math.min(amount, Math.floor(state.rawMana / MANA_PER_ELEMENT), elem.max - elem.current); + if (canConvert <= 0) return fail(ErrorCode.INVALID_INPUT, 'Cannot convert 0 or negative amount'); set({ rawMana: state.rawMana - canConvert * MANA_PER_ELEMENT, - elements: { - ...state.elements, - [element]: { ...elem, current: elem.current + canConvert }, - }, + elements: { ...state.elements, [element]: { ...elem, current: elem.current + canConvert } }, }); - return ok({ converted: canConvert }); }, unlockElement: (element: string, cost: number) => { const state = get(); - if (state.elements[element]?.unlocked) { - return fail(ErrorCode.INVALID_INPUT, `Element ${element} is already unlocked`); - } - if (state.rawMana < cost) { - return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`); - } - - set({ - rawMana: state.rawMana - cost, - elements: { - ...state.elements, - [element]: { ...state.elements[element], unlocked: true }, - }, - }); + if (state.elements[element]?.unlocked) return fail(ErrorCode.INVALID_INPUT, `Element ${element} is already unlocked`); + if (state.rawMana < cost) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${cost} raw mana, have ${state.rawMana}`); + set({ rawMana: state.rawMana - cost, elements: { ...state.elements, [element]: { ...state.elements[element], unlocked: true } } }); return okVoid(); }, @@ -170,15 +133,8 @@ export const useManaStore = create()( set((state) => { const elem = state.elements[element]; if (!elem) return state; - return { - elements: { - ...state.elements, - [element]: { - ...elem, - current: Math.min(elem.current + amount, max), - }, - }, + elements: { ...state.elements, [element]: { ...elem, current: Math.min(elem.current + amount, max) } }, }; }); }, @@ -186,64 +142,35 @@ export const useManaStore = create()( spendElementMana: (element: string, amount: number) => { const state = get(); const elem = state.elements[element]; - if (!elem) { - return fail(ErrorCode.INVALID_ELEMENT, `Element ${element} does not exist`); - } - if (elem.current < amount) { - return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amount} ${element} mana, have ${elem.current}`); - } - - set({ - elements: { - ...state.elements, - [element]: { ...elem, current: elem.current - amount }, - }, - }); + if (!elem) return fail(ErrorCode.INVALID_ELEMENT, `Element ${element} does not exist`); + if (elem.current < amount) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amount} ${element} mana, have ${elem.current}`); + set({ elements: { ...state.elements, [element]: { ...elem, current: elem.current - amount } } }); return okVoid(); }, setElementMax: (max: number) => { set((state) => ({ - elements: Object.fromEntries( - Object.entries(state.elements).map(([k, v]) => [k, { ...v, max }]) - ) as Record, + elements: Object.fromEntries(Object.entries(state.elements).map(([k, v]) => [k, { ...v, max }])) as Record, })); }, craftComposite: (target: string, recipe: string[]) => { const state = get(); - - // Count required ingredients const costs: Record = {}; - recipe.forEach(r => { - costs[r] = (costs[r] || 0) + 1; - }); + recipe.forEach(r => { costs[r] = (costs[r] || 0) + 1; }); - // Check if we have all ingredients for (const [r, amt] of Object.entries(costs)) { - if ((state.elements[r]?.current || 0) < amt) { - return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amt} ${r} mana, have ${state.elements[r]?.current || 0}`); - } + if ((state.elements[r]?.current || 0) < amt) return fail(ErrorCode.INSUFFICIENT_MANA, `Need ${amt} ${r} mana, have ${state.elements[r]?.current || 0}`); } - // Deduct ingredients const newElems = { ...state.elements }; for (const [r, amt] of Object.entries(costs)) { - newElems[r] = { - ...newElems[r], - current: newElems[r].current - amt, - }; + newElems[r] = { ...newElems[r], current: newElems[r].current - amt }; } - // Add crafted element const targetElem = newElems[target]; - newElems[target] = { - ...(targetElem || { current: 0, max: 10, unlocked: false }), - current: (targetElem?.current || 0) + 1, - unlocked: true, - }; - + newElems[target] = { ...(targetElem || { current: 0, max: 10, unlocked: false }), current: (targetElem?.current || 0) + 1, unlocked: true }; set({ elements: newElems }); return okVoid(); }, @@ -252,27 +179,16 @@ export const useManaStore = create()( const state = get(); const elements = { ...state.elements }; - const unlockedElements = Object.entries(elements) - .filter(([, e]) => e.unlocked && e.current < e.max); - - if (unlockedElements.length === 0 || rawMana < 100) return null; + const unlockedElements = Object.entries(elements).filter(([, e]) => e.unlocked && e.current < e.max); + if (unlockedElements.length === 0 || rawMana < MANA_PER_ELEMENT) return null; unlockedElements.sort((a, b) => (b[1].max - b[1].current) - (a[1].max - a[1].current)); const [targetId, targetState] = unlockedElements[0]; - const canConvert = Math.min( - Math.floor(rawMana / 100), - targetState.max - targetState.current - ); - + const canConvert = Math.min(Math.floor(rawMana / MANA_PER_ELEMENT), targetState.max - targetState.current); if (canConvert <= 0) return null; - rawMana -= canConvert * 100; - const updatedElements = { - ...elements, - [targetId]: { ...targetState, current: targetState.current + canConvert } - }; - - return { rawMana, elements: updatedElements }; + rawMana -= canConvert * MANA_PER_ELEMENT; + return { rawMana, elements: { ...elements, [targetId]: { ...targetState, current: targetState.current + canConvert } } }; }, resetMana: ( @@ -283,24 +199,13 @@ export const useManaStore = create()( ) => { const elementMax = 10 + (prestigeUpgrades.elemMax || 0) * 5; const startingMana = 10 + (prestigeUpgrades.manaStart || 0) * 10; - const elements = makeInitialElements(elementMax, prestigeUpgrades); - - set({ - rawMana: startingMana, - meditateTicks: 0, - totalManaGathered: 0, - elements, - }); + set({ rawMana: startingMana, meditateTicks: 0, totalManaGathered: 0, elements: makeInitialElements(elementMax, prestigeUpgrades) }); }, }), { storage: createSafeStorage(), name: 'mana-loop-mana', - partialize: (state) => ({ - rawMana: state.rawMana, - totalManaGathered: state.totalManaGathered, - elements: state.elements, - }), + partialize: (state) => ({ rawMana: state.rawMana, totalManaGathered: state.totalManaGathered, elements: state.elements }), } ) ); @@ -311,16 +216,10 @@ export function makeInitialElements( prestigeUpgrades: Record = {} ): Record { const elemStart = (prestigeUpgrades.elemStart || 0) * 5; - const elements: Record = {}; - Object.keys(ELEMENTS).forEach(k => { + for (const k of Object.keys(ELEMENTS)) { const isUnlocked = BASE_UNLOCKED_ELEMENTS.includes(k); - elements[k] = { - current: isUnlocked ? elemStart : 0, - max: elementMax, - unlocked: isUnlocked, - }; - }); - + elements[k] = { current: isUnlocked ? elemStart : 0, max: elementMax, unlocked: isUnlocked }; + } return elements; }