Major bug fixes and system cleanup
Some checks failed
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m16s

- Fix enchantment capacity validation to respect equipment capacity limits
- Remove combo system (no longer used)
- Remove AUDIT_REPORT.md and GAME_SYSTEMS_ANALYSIS.md files
- Add tests for capacity validation, attunement unlocking, and floor HP
- Remove combo-related achievements
- Fix AchievementsDisplay to not reference combo state
- Add capacity display showing current/max in enchantment design UI
- Prevent designs that exceed equipment capacity from being created
This commit is contained in:
Z User
2026-03-29 19:08:06 +00:00
parent 300e43f8be
commit 522079e011
11 changed files with 162 additions and 1019 deletions

View File

@@ -1,313 +0,0 @@
# Mana Loop - Codebase Audit Report
**Task ID:** 4
**Date:** Audit of unimplemented effects, upgrades, and missing functionality
---
## 1. Special Effects Status
### SPECIAL_EFFECTS Constant (upgrade-effects.ts)
The `SPECIAL_EFFECTS` constant defines 32 special effect IDs. Here's the implementation status:
| Effect ID | Name | Status | Notes |
|-----------|------|--------|-------|
| `MANA_CASCADE` | Mana Cascade | ⚠️ Partially Implemented | Defined in `computeDynamicRegen()` but that function is NOT called from store.ts |
| `STEADY_STREAM` | Steady Stream | ⚠️ Partially Implemented | Defined in `computeDynamicRegen()` but not called from tick |
| `MANA_TORRENT` | Mana Torrent | ⚠️ Partially Implemented | Defined in `computeDynamicRegen()` but not called |
| `FLOW_SURGE` | Flow Surge | ❌ Missing | Not implemented anywhere |
| `MANA_EQUILIBRIUM` | Mana Equilibrium | ❌ Missing | Not implemented |
| `DESPERATE_WELLS` | Desperate Wells | ⚠️ Partially Implemented | Defined in `computeDynamicRegen()` but not called |
| `MANA_ECHO` | Mana Echo | ❌ Missing | Not implemented in gatherMana() |
| `EMERGENCY_RESERVE` | Emergency Reserve | ❌ Missing | Not implemented in startNewLoop() |
| `BATTLE_FURY` | Battle Fury | ⚠️ Partially Implemented | In `computeDynamicDamage()` but function not called |
| `ARMOR_PIERCE` | Armor Pierce | ❌ Missing | Floor defense not implemented |
| `OVERPOWER` | Overpower | ✅ Implemented | store.ts line 627 |
| `BERSERKER` | Berserker | ✅ Implemented | store.ts line 632 |
| `COMBO_MASTER` | Combo Master | ❌ Missing | Not implemented |
| `ADRENALINE_RUSH` | Adrenaline Rush | ❌ Missing | Not implemented on enemy defeat |
| `PERFECT_MEMORY` | Perfect Memory | ❌ Missing | Not implemented in cancel study |
| `QUICK_MASTERY` | Quick Mastery | ❌ Missing | Not implemented |
| `PARALLEL_STUDY` | Parallel Study | ⚠️ Partially Implemented | State exists but logic incomplete |
| `STUDY_INSIGHT` | Study Insight | ❌ Missing | Not implemented |
| `STUDY_MOMENTUM` | Study Momentum | ❌ Missing | Not implemented |
| `KNOWLEDGE_ECHO` | Knowledge Echo | ❌ Missing | Not implemented |
| `KNOWLEDGE_TRANSFER` | Knowledge Transfer | ❌ Missing | Not implemented |
| `MENTAL_CLARITY` | Mental Clarity | ❌ Missing | Not implemented |
| `STUDY_REFUND` | Study Refund | ❌ Missing | Not implemented |
| `FREE_STUDY` | Free Study | ❌ Missing | Not implemented |
| `MIND_PALACE` | Mind Palace | ❌ Missing | Not implemented |
| `STUDY_RUSH` | Study Rush | ❌ Missing | Not implemented |
| `CHAIN_STUDY` | Chain Study | ❌ Missing | Not implemented |
| `ELEMENTAL_HARMONY` | Elemental Harmony | ❌ Missing | Not implemented |
| `DEEP_STORAGE` | Deep Storage | ❌ Missing | Not implemented |
| `DOUBLE_CRAFT` | Double Craft | ❌ Missing | Not implemented |
| `ELEMENTAL_RESONANCE` | Elemental Resonance | ❌ Missing | Not implemented |
| `PURE_ELEMENTS` | Pure Elements | ❌ Missing | Not implemented |
**Summary:** 2 fully implemented, 6 partially implemented (function exists but not called), 24 not implemented.
---
## 2. Enchantment Effects Status
### Equipment Enchantment Effects (enchantment-effects.ts)
The following effect types are defined:
| Effect Type | Status | Notes |
|-------------|--------|-------|
| **Spell Effects** (`type: 'spell'`) | ✅ Working | Spells granted via `getSpellsFromEquipment()` |
| **Bonus Effects** (`type: 'bonus'`) | ✅ Working | Applied in `computeEquipmentEffects()` |
| **Multiplier Effects** (`type: 'multiplier'`) | ✅ Working | Applied in `computeEquipmentEffects()` |
| **Special Effects** (`type: 'special'`) | ⚠️ Tracked Only | Added to `specials` Set but NOT applied in game logic |
### Special Enchantment Effects Not Applied:
| Effect ID | Description | Issue |
|-----------|-------------|-------|
| `spellEcho10` | 10% chance cast twice | Tracked but not implemented in combat |
| `lifesteal5` | 5% damage as mana | Tracked but not implemented in combat |
| `overpower` | +50% damage at 80% mana | Tracked but separate from skill upgrade version |
**Location of Issue:**
```typescript
// effects.ts line 58-60
} else if (effect.type === 'special' && effect.specialId) {
specials.add(effect.specialId);
}
// Effect is tracked but never used in combat/damage calculations
```
---
## 3. Skill Effects Status
### SKILLS_DEF Analysis (constants.ts)
Skills with direct effects that should apply per level:
| Skill | Effect | Status |
|-------|--------|--------|
| `manaWell` | +100 max mana per level | ✅ Implemented |
| `manaFlow` | +1 regen/hr per level | ✅ Implemented |
| `elemAttune` | +50 elem mana cap | ✅ Implemented |
| `manaOverflow` | +25% click mana | ✅ Implemented |
| `quickLearner` | +10% study speed | ✅ Implemented |
| `focusedMind` | -5% study cost | ✅ Implemented |
| `meditation` | 2.5x regen after 4hrs | ✅ Implemented |
| `knowledgeRetention` | +20% progress saved | ⚠️ Partially Implemented |
| `enchanting` | Unlocks designs | ✅ Implemented |
| `efficientEnchant` | -5% capacity cost | ⚠️ Not verified |
| `disenchanting` | 20% mana recovery | ⚠️ Not verified |
| `enchantSpeed` | -10% enchant time | ⚠️ Not verified |
| `scrollCrafting` | Create scrolls | ❌ Not implemented |
| `essenceRefining` | +10% effect power | ⚠️ Not verified |
| `effCrafting` | -10% craft time | ⚠️ Not verified |
| `fieldRepair` | +15% repair | ❌ Repair not implemented |
| `elemCrafting` | +25% craft output | ✅ Implemented |
| `manaTap` | +1 mana/click | ✅ Implemented |
| `manaSurge` | +3 mana/click | ✅ Implemented |
| `manaSpring` | +2 regen | ✅ Implemented |
| `deepTrance` | 3x after 6hrs | ✅ Implemented |
| `voidMeditation` | 5x after 8hrs | ✅ Implemented |
| `insightHarvest` | +10% insight | ✅ Implemented |
| `temporalMemory` | Keep spells | ✅ Implemented |
| `guardianBane` | +20% vs guardians | ⚠️ Tracked but not verified |
---
## 4. Missing Implementations
### 4.1 Dynamic Effect Functions Not Called
The following functions exist in `upgrade-effects.ts` but are NOT called from `store.ts`:
```typescript
// upgrade-effects.ts - EXISTS but NOT USED
export function computeDynamicRegen(
effects: ComputedEffects,
baseRegen: number,
maxMana: number,
currentMana: number,
incursionStrength: number
): number { ... }
export function computeDynamicDamage(
effects: ComputedEffects,
baseDamage: number,
floorHPPct: number,
currentMana: number,
maxMana: number,
consecutiveHits: number
): number { ... }
```
**Where it should be called:**
- `store.ts` tick() function around line 414 for regen
- `store.ts` tick() function around line 618 for damage
### 4.2 Missing Combat Special Effects
Location: `store.ts` tick() combat section (lines 510-760)
Missing implementations:
```typescript
// BATTLE_FURY - +10% damage per consecutive hit
if (hasSpecial(effects, SPECIAL_EFFECTS.BATTLE_FURY)) {
// Need to track consecutiveHits in state
}
// ARMOR_PIERCE - Ignore 10% floor defense
// Floor defense not implemented in game
// COMBO_MASTER - Every 5th attack deals 3x damage
if (hasSpecial(effects, SPECIAL_EFFECTS.COMBO_MASTER)) {
// Need to track hitCount in state
}
// ADRENALINE_RUSH - Restore 5% mana on kill
// Should be added after floorHP <= 0 check
```
### 4.3 Missing Study Special Effects
Location: `store.ts` tick() study section (lines 440-485)
Missing implementations:
```typescript
// MENTAL_CLARITY - +10% study speed when mana > 75%
// STUDY_RUSH - First hour is 2x speed
// STUDY_REFUND - 25% mana back on completion
// KNOWLEDGE_ECHO - 10% instant study chance
// STUDY_MOMENTUM - +5% speed per consecutive hour
```
### 4.4 Missing Loop/Click Effects
Location: `store.ts` gatherMana() and startNewLoop()
```typescript
// gatherMana() - MANA_ECHO
// 10% chance to gain double mana from clicks
if (hasSpecial(effects, SPECIAL_EFFECTS.MANA_ECHO) && Math.random() < 0.1) {
cm *= 2;
}
// startNewLoop() - EMERGENCY_RESERVE
// Keep 10% max mana when starting new loop
if (hasSpecial(effects, SPECIAL_EFFECTS.EMERGENCY_RESERVE)) {
newState.rawMana = maxMana * 0.1;
}
```
### 4.5 Parallel Study Incomplete
`parallelStudyTarget` exists in state but the logic is not fully implemented in tick():
- State field exists (line 203)
- No tick processing for parallel study
- UI may show it but actual progress not processed
---
## 5. Balance Concerns
### 5.1 Weak Upgrades
| Upgrade | Issue | Suggestion |
|---------|-------|------------|
| `manaThreshold` | +20% mana for -10% regen is a net negative early | Change to +30% mana for -5% regen |
| `manaOverflow` | +25% click mana at 5 levels is only +5%/level | Increase to +10% per level |
| `fieldRepair` | Repair system not implemented | Remove or implement repair |
| `scrollCrafting` | Scroll system not implemented | Remove or implement scrolls |
### 5.2 Tier Scaling Issues
From `skill-evolution.ts`, tier multipliers are 10x per tier:
- Tier 1: multiplier 1
- Tier 2: multiplier 10
- Tier 3: multiplier 100
- Tier 4: multiplier 1000
- Tier 5: multiplier 10000
This creates massive power jumps that may trivialize content when tiering up.
### 5.3 Special Effect Research Costs
Research skills for effects are expensive but effects may not be implemented:
- `researchSpecialEffects` costs 500 mana + 10 hours study
- Effects like `spellEcho10` are tracked but not applied
- Player invests resources for non-functional upgrades
---
## 6. Critical Issues
### 6.1 computeDynamicRegen Not Used
**File:** `computed-stats.ts` lines 210-225
The function exists but only applies incursion penalty. It should call the more comprehensive `computeDynamicRegen` from `upgrade-effects.ts` that handles:
- Mana Cascade
- Mana Torrent
- Desperate Wells
- Steady Stream
### 6.2 No Consecutive Hit Tracking
`BATTLE_FURY` and `COMBO_MASTER` require tracking consecutive hits, but this state doesn't exist. Need:
```typescript
// In GameState
consecutiveHits: number;
totalHitsThisLoop: number;
```
### 6.3 Enchantment Special Effects Not Applied
The `specials` Set is populated but never checked in combat for enchantment-specific effects like:
- `lifesteal5`
- `spellEcho10`
---
## 7. Recommendations
### Priority 1 - Core Effects
1. Call `computeDynamicRegen()` from tick() instead of inline calculation
2. Call `computeDynamicDamage()` from combat section
3. Implement MANA_ECHO in gatherMana()
4. Implement EMERGENCY_RESERVE in startNewLoop()
### Priority 2 - Combat Effects
1. Add `consecutiveHits` to GameState
2. Implement BATTLE_FURY damage scaling
3. Implement COMBO_MASTER every 5th hit
4. Implement ADRENALINE_RUSH on kill
### Priority 3 - Study Effects
1. Implement MENTAL_CLARITY conditional speed
2. Implement STUDY_RUSH first hour bonus
3. Implement STUDY_REFUND on completion
4. Implement KNOWLEDGE_ECHO instant chance
### Priority 4 - Missing Systems
1. Implement or remove `scrollCrafting` skill
2. Implement or remove `fieldRepair` skill
3. Complete parallel study tick processing
4. Implement floor defense for ARMOR_PIERCE
---
## 8. Files Affected
| File | Changes Needed |
|------|----------------|
| `src/lib/game/store.ts` | Call dynamic effect functions, implement specials |
| `src/lib/game/computed-stats.ts` | Integrate with upgrade-effects dynamic functions |
| `src/lib/game/types.ts` | Add consecutiveHits to GameState |
| `src/lib/game/skill-evolution.ts` | Consider removing unimplementable upgrades |
---
**End of Audit Report**

View File

@@ -1,510 +0,0 @@
# Mana Loop - Game Systems Analysis Report
**Generated:** Task ID 24
**Purpose:** Comprehensive review of all game systems, their completeness, and "feel"
---
## Executive Summary
Mana Loop is an incremental/idle game with a time-loop mechanic, spellcasting combat, equipment enchanting, and attunement-based progression. The game has solid core mechanics but several systems feel incomplete or disconnected from the main gameplay loop.
**Overall Assessment:** ⚠️ **Needs Polish** - Core systems work but lack depth and integration
---
## System-by-System Analysis
### 1. 🔮 Core Mana System
**Status:****Complete & Functional**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Mana Regeneration | ⭐⭐⭐⭐⭐ | Well-implemented with upgrades affecting it |
| Mana Cap | ⭐⭐⭐⭐⭐ | Clear scaling through skills |
| Click Gathering | ⭐⭐⭐⭐ | Works but feels less important late-game |
| Mana Types | ⭐⭐⭐⭐ | Good variety (18 types) |
| Compound Mana | ⭐⭐⭐⭐ | Auto-unlocks when components available |
**What Works Well:**
- Clear progression: raw mana → elemental mana → compound mana
- Attunements provide passive conversion
- Incursion mechanic adds urgency late-loop
**What Feels Lacking:**
- Limited use cases for many mana types
- Compound mana types unlock automatically but feel disconnected from gameplay
- No meaningful choices in which mana to generate/prioritize
- Exotic elements (void, stellar, crystal) are very difficult to unlock
**Suggestions:**
1. Add spells that specifically use compound/exotic elements
2. Allow players to choose which elements to generate from attunements
3. Add "mana conversion" buildings/upgrades that transform elements
---
### 2. ⚔️ Combat/Spire System
**Status:** ⚠️ **Partially Complete**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Floor Scaling | ⭐⭐⭐⭐⭐ | Good HP progression |
| Spell Casting | ⭐⭐⭐⭐ | Cast speed system works well |
| Elemental Weakness | ⭐⭐⭐⭐ | Opposing elements deal bonus damage |
| Guardian Fights | ⭐⭐⭐⭐ | Unique perks add flavor |
| Pact System | ⭐⭐⭐⭐⭐ | Excellent incentive to progress |
**What Works Well:**
- Guardian pacts provide permanent progression
- Each guardian has unique perks that feel impactful
- Descent mechanic prevents easy farming
- Barrier system on guardians adds tactical depth
**What Feels Lacking:**
- No active combat decisions - purely automatic
- Floor HP regeneration can feel frustrating without burst damage
- Limited spell selection (only from equipment)
- No enemy variety beyond floors/guardians
- Combo system exists in types but isn't actually used
**Critical Gap - Combo System:**
```typescript
// From types.ts - combo exists but isn't used
combo: {
count: number;
maxCombo: number;
multiplier: number;
elementChain: string[];
decayTimer: number;
}
```
The combo state is tracked but never affects gameplay. This is a dead system.
**Suggestions:**
1. Implement combo multiplier affecting damage
2. Add enemy types with different weaknesses
3. Allow manual spell selection mid-combat
4. Add tactical choices (focus fire, defensive casting, etc.)
---
### 3. ✨ Enchanting System (Enchanter Attunement)
**Status:****Complete & Well-Designed**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Design Stage | ⭐⭐⭐⭐⭐ | Clear, intuitive UI |
| Prepare Stage | ⭐⭐⭐⭐ | Good time investment |
| Apply Stage | ⭐⭐⭐⭐ | Mana sink feels appropriate |
| Effect Variety | ⭐⭐⭐⭐ | Good selection of effects |
| Spell Granting | ⭐⭐⭐⭐⭐ | Primary way to get spells |
**What Works Well:**
- 3-stage process (Design → Prepare → Apply) feels meaningful
- Effect research system provides clear progression
- Spells come from equipment - creates itemization
- Disenchanting recovers some mana
**What Feels Lacking:**
- Effect capacity limits can feel arbitrary
- No way to preview enchantment before committing
- No rare/special enchantments
- Enchantment effects feel same-y (mostly +stats)
**Suggestions:**
1. Add "rare" effect drops from guardians
2. Allow effect combining/stacking visually
3. Add visual flair to enchanted items
4. Create set bonuses for themed enchantments
---
### 4. 💜 Invoker/Pact System
**Status:** ⚠️ **Conceptually Complete, Implementation Lacking**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Pact Signing | ⭐⭐⭐⭐ | Time investment, meaningful choice |
| Guardian Perks | ⭐⭐⭐⭐⭐ | Unique and impactful |
| Pact Multipliers | ⭐⭐⭐⭐ | Clear progression |
| Invoker Skills | ⭐⭐⭐ | Skills exist but category is sparse |
**What Works Well:**
- 10 unique guardians with distinct perks
- Pact multiplier system rewards guardian hunting
- Each pact feels like a real achievement
**What Feels Lacking:**
- **No Invocation category spells/skills defined**
- Invoker attunement has no primary mana type
- Limited invoker-specific progression
- Once you sign a pact, interaction ends
**Critical Gap - Invocation Skills:**
```typescript
// From SKILL_CATEGORIES
{ id: 'invocation', name: 'Invocation', icon: '💜', attunement: 'invoker' },
{ id: 'pact', name: 'Pact Mastery', icon: '🤝', attunement: 'invoker' },
```
Looking at SKILLS_DEF, there are **NO skills** in the 'invocation' or 'pact' categories! The attunement promises these categories but delivers nothing.
**Suggestions:**
1. Add Invocation skills:
- Spirit Call (summon guardian echo)
- Elemental Channeling (boost pact element)
- Guardian's Boon (enhance perks)
2. Add Pact skills:
- Pact Binding (reduce signing time)
- Soul Link (gain mana from guardian defeats)
- Pact Synergy (combine perk effects)
3. Allow upgrading existing pacts
---
### 5. ⚒️ Fabricator/Golemancy System
**Status:****NOT IMPLEMENTED**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Golem Defs | ❌ | GOLEM_DEFS does not exist |
| Golem Summoning | ❌ | No summoning logic |
| Golem Combat | ❌ | Golems don't fight |
| Crafting Skills | ⚠️ | Only in constants, not evolved |
**Critical Gap:**
```typescript
// From types.ts - these exist
export interface GolemDef { ... }
export interface ActiveGolem { ... }
// In GameState
activeGolems: ActiveGolem[];
unlockedGolemTypes: string[];
golemSummoningProgress: Record<string, number>;
```
But GOLEM_DEFS is referenced nowhere. The entire golemancy system is **stub code**.
**What Should Exist:**
1. GOLEM_DEFS with 5-10 golem types
2. Golem summoning logic (earth mana cost)
3. Golem combat integration (they fight alongside player)
4. Golem variants (earth + fire = magma golem)
5. Golem equipment/crystals for customization
**Suggestions:**
1. Implement basic earth golem first
2. Add golem as "pet" that attacks automatically
3. Golems should have limited duration (HP-based)
4. Crystals can enhance golem stats
---
### 6. 📚 Skill System
**Status:** ⚠️ **Inconsistent**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Skill Categories | ⭐⭐⭐⭐ | Good organization |
| Study System | ⭐⭐⭐⭐⭐ | Clear time investment |
| Evolution Paths | ⭐⭐⭐⭐⭐ | 5 tiers with choices |
| Upgrade Choices | ⭐⭐⭐⭐ | Meaningful decisions |
**What Works Well:**
- 4 upgrade choices per milestone (2 selected max)
- Tier progression multiplies effects
- Study time creates opportunity cost
**What Feels Lacking:**
- Many skills have no evolution path
- 'craft' category is legacy/unclear
- 'effectResearch' is scattered
- Some skills do nothing (scrollCrafting, fieldRepair)
**Dead Skills:**
```typescript
// In SKILLS_DEF but not implemented
scrollCrafting: { ... desc: "Create scrolls..." }, // No scroll system
fieldRepair: { ... desc: "+15% repair efficiency" }, // No repair system
```
**Suggestions:**
1. Remove or implement scrollCrafting/fieldRepair
2. Add evolution paths to all skills
3. Consolidate effectResearch into clearer tree
4. Add skill synergies (combining skills = bonus)
---
### 7. 🎯 Attunement System
**Status:** ⚠️ **Good Concept, Incomplete Execution**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Concept | ⭐⭐⭐⭐⭐ | Class-like specialization |
| Enchanter | ⭐⭐⭐⭐⭐ | Fully implemented |
| Invoker | ⭐⭐ | Missing skills |
| Fabricator | ⭐ | Missing golemancy |
| Leveling | ⭐⭐⭐⭐ | Good XP scaling |
**What Works Well:**
- Enchanter attunement is complete and functional
- Attunement XP through gameplay feels natural
- Level-scaled conversion rates
**What Feels Lacking:**
- Invoker and Fabricator unlock conditions unclear
- Invoker has no Invocation/Pact skills
- Fabricator has no golemancy implementation
- Only 3 attunements, no late-game options
**Unlock Mystery:**
```typescript
// From attunements.ts
invoker: {
unlockCondition: 'Defeat your first guardian and choose the path of the Invoker',
// But no code checks for this condition
}
```
**Suggestions:**
1. Add clear unlock triggers in code
2. Implement missing skill categories
3. Add 4th attunement for late-game (Void Walker?)
4. Create attunement-specific achievements
---
### 8. 🏆 Achievement System
**Status:****Defined But Passive**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Definitions | ⭐⭐⭐⭐ | Good variety |
| Progress Tracking | ⭐⭐⭐ | State exists |
| Rewards | ⭐⭐ | Mostly insight |
**What Works Well:**
- Categories organized (mana, combat, progression)
- Progress tracked in state
**What Feels Lacking:**
- Achievements don't unlock anything unique
- No visual display of achievements
- Rewards are passive (insight)
- No hidden/challenge achievements
**Suggestions:**
1. Add achievement-locked cosmetics/titles
2. Create achievement showcase UI
3. Add challenge achievements (speedrun, no-upgrade, etc.)
4. Unlock effects through achievements
---
### 9. 📦 Equipment System
**Status:****Complete**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Equipment Types | ⭐⭐⭐⭐ | 8 slots, 40+ types |
| Capacity System | ⭐⭐⭐⭐⭐ | Clear limits |
| Rarity | ⭐⭐⭐ | Exists but cosmetic |
**What Works Well:**
- 8 equipment slots provide customization
- Capacity system limits power creep
- Equipment grants spells
**What Feels Lacking:**
- Equipment only comes from starting gear
- No way to craft equipment (except from blueprints)
- Rarity doesn't affect much
- No equipment drops from combat
**Critical Gap - Equipment Acquisition:**
Players start with:
- Basic Staff (Mana Bolt)
- Civilian Shirt
- Civilian Shoes
After that, the ONLY way to get equipment is:
1. Blueprint drops from floors (rare)
2. Craft from blueprint (expensive)
There's no consistent equipment progression!
**Suggestions:**
1. Add equipment drops from floors
2. Create more crafting recipes
3. Add equipment merchant/shop
4. Allow equipment upgrading
---
### 10. 🔁 Prestige/Loop System
**Status:****Complete**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Loop Reset | ⭐⭐⭐⭐⭐ | Clear, saves insight |
| Prestige Upgrades | ⭐⭐⭐⭐ | Good variety |
| Memory System | ⭐⭐⭐ | Keeps some progress |
| Victory Condition | ⭐⭐⭐⭐ | Defeat floor 100 guardian |
**What Works Well:**
- 30-day time limit creates urgency
- Insight economy for permanent upgrades
- Memory slots for keeping spells
- Clear victory condition
**What Feels Lacking:**
- No insight milestones/unlocks
- Memory system is shallow (just spell slots)
- No "loop challenges" or modifiers
- Limited replayability after first victory
**Suggestions:**
1. Add loop modifiers (harder floors, better rewards)
2. Insight milestones unlock attunements
3. Loop-specific achievements
4. New Game+ mode with modifiers
---
### 11. 🗓️ Time/Incursion System
**Status:****Complete**
| Aspect | Rating | Notes |
|--------|--------|-------|
| Day/Hour Cycle | ⭐⭐⭐⭐⭐ | Clear progression |
| Incursion Mechanic | ⭐⭐⭐⭐ | Adds late-game pressure |
| Time Actions | ⭐⭐⭐ | Study, craft, prepare |
**What Works Well:**
- 30 days = one loop
- Incursion starts day 20, scales to 95% penalty
- Actions have clear time costs
**What Feels Lacking:**
- No time manipulation (beyond debug)
- No day/night effects on gameplay
- Incursion is purely negative, no strategy around it
**Suggestions:**
1. Add time manipulation skills (slow incursion)
2. Night bonuses (different for guardians)
3. Incursion-specific rewards (void mana?)
---
## Missing Systems Summary
### High Priority (Break Promises)
| System | Promised By | Status |
|--------|-------------|--------|
| Golemancy | Fabricator attunement | ❌ Not implemented |
| Invocation Skills | Invoker attunement | ❌ No skills defined |
| Pact Skills | Invoker attunement | ❌ No skills defined |
| Combo System | ComboState in types | ❌ State exists, unused |
| Scroll Crafting | scrollCrafting skill | ❌ No scroll system |
### Medium Priority (Incomplete)
| System | Issue |
|--------|-------|
| Fabricator Unlocks | Unlock condition not coded |
| Invoker Unlocks | Unlock condition not coded |
| Equipment Progression | Only starting gear + rare blueprints |
| Evolution Paths | Not all skills have 5 tiers |
### Low Priority (Polish)
| System | Issue |
|--------|-------|
| Field Repair | Repair system doesn't exist |
| Guardian Variants | Not implemented |
| Achievement Rewards | Passive only |
| Enemy Variety | Only floors/guardians |
---
## "Feel" Analysis
### What Feels Good
1. **Guardian Pacts** - Defeating a guardian and signing a pact feels like a major achievement
2. **Enchanting Process** - 3-stage system feels involved and meaningful
3. **Cast Speed System** - Different spells feel different to use
4. **Skill Evolution** - Choosing upgrades at milestones gives agency
5. **Compound Mana** - Auto-unlocking elements through gameplay
### What Feels Bad
1. **Helplessness** - Combat is 100% automatic with no player input
2. **Dead Ends** - Attunements unlock with no skills to use
3. **Empty Promises** - Golemancy is mentioned everywhere but doesn't exist
4. **Grind Walls** - Exotic elements require absurd amounts of base elements
5. **Useless Skills** - scrollCrafting, fieldRepair do nothing
### What Feels Confusing
1. **Attunement Unlocks** - How do I get Invoker/Fabricator?
2. **Equipment Progression** - Where do I get better gear?
3. **Exotic Elements** - How do void/stellar/crystal work?
4. **Combo System** - UI mentions it but it does nothing
5. **Incursion** - Is there anything I can do about it?
---
## Recommended Priorities
### Phase 1: Fix Broken Promises (1-2 weeks)
1. Implement basic golemancy (1 golem type, auto-attacks)
2. Add Invocation/Pact skill categories with 3-4 skills each
3. Add attunement unlock conditions in code
4. Remove or implement scrollCrafting/fieldRepair
### Phase 2: Fill Content Gaps (2-3 weeks)
1. Add equipment drops from floors
2. Implement combo system for damage bonuses
3. Add more spells using compound/exotic elements
4. Create evolution paths for all skills
### Phase 3: Polish & Depth (2-3 weeks)
1. Add tactical combat options
2. Create achievement showcase
3. Add loop modifiers/challenges
4. Implement equipment upgrading
---
## Conclusion
Mana Loop has a strong foundation with unique mechanics (attunements, enchanting, pacts) that differentiate it from typical incremental games. However, several systems are incomplete or disconnected, creating confusion and limiting engagement.
**The biggest issues are:**
1. Golemancy is completely missing despite being promised
2. Invoker attunement has no skills
3. Combat has no player agency
4. Equipment progression is broken
**Focus on completing existing systems before adding new ones.**
---
*End of Analysis Report*

View File

@@ -232,7 +232,6 @@ export default function ManaLoopGame() {
totalSpellsCast: store.totalSpellsCast, totalSpellsCast: store.totalSpellsCast,
totalDamageDealt: store.totalDamageDealt, totalDamageDealt: store.totalDamageDealt,
totalCraftsCompleted: store.totalCraftsCompleted, totalCraftsCompleted: store.totalCraftsCompleted,
combo: store.combo,
}} }}
/> />
</DebugName> </DebugName>

View File

@@ -13,7 +13,7 @@ import { GameState } from '@/lib/game/types';
interface AchievementsProps { interface AchievementsProps {
achievements: AchievementState; achievements: AchievementState;
gameState: Pick<GameState, 'maxFloorReached' | 'totalManaGathered' | 'signedPacts' | 'totalSpellsCast' | 'totalDamageDealt' | 'totalCraftsCompleted' | 'combo'>; gameState: Pick<GameState, 'maxFloorReached' | 'totalManaGathered' | 'signedPacts' | 'totalSpellsCast' | 'totalDamageDealt' | 'totalCraftsCompleted'>;
} }
export function AchievementsDisplay({ achievements, gameState }: AchievementsProps) { export function AchievementsDisplay({ achievements, gameState }: AchievementsProps) {
@@ -39,8 +39,6 @@ export function AchievementsDisplay({ achievements, gameState }: AchievementsPro
: gameState.maxFloorReached; : gameState.maxFloorReached;
} }
return gameState.maxFloorReached; return gameState.maxFloorReached;
case 'combo':
return gameState.combo?.maxCombo || 0;
case 'spells': case 'spells':
return gameState.totalSpellsCast || 0; return gameState.totalSpellsCast || 0;
case 'damage': case 'damage':

View File

@@ -1,143 +0,0 @@
'use client';
import { Progress } from '@/components/ui/progress';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Zap, Flame, Sparkles } from 'lucide-react';
import type { ComboState } from '@/lib/game/types';
import { ELEMENTS } from '@/lib/game/constants';
interface ComboMeterProps {
combo: ComboState;
isClimbing: boolean;
}
export function ComboMeter({ combo, isClimbing }: ComboMeterProps) {
const comboPercent = Math.min(100, combo.count);
const multiplierPercent = Math.min(100, ((combo.multiplier - 1) / 2) * 100); // Max 300% = 200% bonus
// Combo tier names
const getComboTier = (count: number): { name: string; color: string } => {
if (count >= 100) return { name: 'LEGENDARY', color: 'text-amber-400' };
if (count >= 75) return { name: 'Master', color: 'text-purple-400' };
if (count >= 50) return { name: 'Expert', color: 'text-blue-400' };
if (count >= 25) return { name: 'Adept', color: 'text-green-400' };
if (count >= 10) return { name: 'Novice', color: 'text-cyan-400' };
return { name: 'Building...', color: 'text-gray-400' };
};
const tier = getComboTier(combo.count);
const hasElementChain = combo.elementChain.length === 3 && new Set(combo.elementChain).size === 3;
if (!isClimbing && combo.count === 0) {
return null;
}
return (
<Card className="bg-gray-900/80 border-gray-700">
<CardHeader className="pb-2">
<CardTitle className="text-amber-400 game-panel-title text-xs flex items-center gap-2">
<Zap className="w-4 h-4" />
Combo Meter
{combo.count >= 10 && (
<Badge className={`ml-auto ${tier.color} bg-gray-800`}>
{tier.name}
</Badge>
)}
</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
{/* Combo Count */}
<div className="space-y-1">
<div className="flex justify-between items-center text-sm">
<span className="text-gray-400">Hits</span>
<span className={`font-bold ${tier.color}`}>
{combo.count}
{combo.maxCombo > combo.count && (
<span className="text-gray-500 text-xs ml-2">max: {combo.maxCombo}</span>
)}
</span>
</div>
<Progress
value={comboPercent}
className="h-2 bg-gray-800"
/>
</div>
{/* Multiplier */}
<div className="space-y-1">
<div className="flex justify-between items-center text-sm">
<span className="text-gray-400">Multiplier</span>
<span className="font-bold text-amber-400">
{combo.multiplier.toFixed(2)}x
</span>
</div>
<div className="h-2 bg-gray-800 rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-300"
style={{
width: `${multiplierPercent}%`,
background: `linear-gradient(90deg, #F59E0B, #EF4444)`,
}}
/>
</div>
</div>
{/* Element Chain */}
{combo.elementChain.length > 0 && (
<div className="space-y-1">
<div className="flex justify-between items-center text-sm">
<span className="text-gray-400">Element Chain</span>
{hasElementChain && (
<span className="text-green-400 text-xs">+25% bonus!</span>
)}
</div>
<div className="flex gap-1">
{combo.elementChain.map((elem, i) => {
const elemDef = ELEMENTS[elem];
return (
<div
key={i}
className="w-8 h-8 rounded border flex items-center justify-center text-xs"
style={{
borderColor: elemDef?.color || '#60A5FA',
backgroundColor: `${elemDef?.color}20`,
color: elemDef?.color || '#60A5FA',
}}
>
{elemDef?.sym || '?'}
</div>
);
})}
{/* Empty slots */}
{Array.from({ length: 3 - combo.elementChain.length }).map((_, i) => (
<div
key={`empty-${i}`}
className="w-8 h-8 rounded border border-gray-700 bg-gray-800/50 flex items-center justify-center text-gray-600"
>
?
</div>
))}
</div>
</div>
)}
{/* Decay Warning */}
{isClimbing && combo.count > 0 && combo.decayTimer <= 3 && (
<div className="text-xs text-red-400 flex items-center gap-1">
<Flame className="w-3 h-3" />
Combo decaying soon!
</div>
)}
{/* Not climbing warning */}
{!isClimbing && combo.count > 0 && (
<div className="text-xs text-amber-400 flex items-center gap-1">
<Sparkles className="w-3 h-3" />
Resume climbing to maintain combo
</div>
)}
</CardContent>
</Card>
);
}

View File

@@ -89,6 +89,10 @@ export function CraftingTab({ store }: CraftingTabProps) {
0 0
); );
// Get capacity limit for selected equipment type
const selectedEquipmentCapacity = selectedEquipmentType ? EQUIPMENT_TYPES[selectedEquipmentType]?.baseCapacity || 0 : 0;
const isOverCapacity = selectedEquipmentType ? designCapacityCost > selectedEquipmentCapacity : false;
// Calculate design time // Calculate design time
const designTime = selectedEffects.reduce((total, eff) => total + 0.5 * eff.stacks, 1); const designTime = selectedEffects.reduce((total, eff) => total + 0.5 * eff.stacks, 1);
@@ -299,8 +303,8 @@ export function CraftingTab({ store }: CraftingTabProps) {
/> />
<div className="flex justify-between text-sm"> <div className="flex justify-between text-sm">
<span>Total Capacity:</span> <span>Total Capacity:</span>
<span className={designCapacityCost > 100 ? 'text-red-400' : 'text-green-400'}> <span className={isOverCapacity ? 'text-red-400' : 'text-green-400'}>
{designCapacityCost.toFixed(0)} {designCapacityCost.toFixed(0)} / {selectedEquipmentCapacity}
</span> </span>
</div> </div>
<div className="flex justify-between text-sm text-gray-400"> <div className="flex justify-between text-sm text-gray-400">
@@ -309,10 +313,10 @@ export function CraftingTab({ store }: CraftingTabProps) {
</div> </div>
<Button <Button
className="w-full" className="w-full"
disabled={!designName || selectedEffects.length === 0} disabled={!designName || selectedEffects.length === 0 || isOverCapacity}
onClick={handleCreateDesign} onClick={handleCreateDesign}
> >
Start Design ({designTime.toFixed(1)}h) {isOverCapacity ? 'Over Capacity!' : `Start Design (${designTime.toFixed(1)}h)`}
</Button> </Button>
</div> </div>
</> </>

View File

@@ -0,0 +1,133 @@
import { describe, it, expect } from 'vitest';
import { calculateEffectCapacityCost, ENCHANTMENT_EFFECTS } from '../data/enchantment-effects';
import { EQUIPMENT_TYPES } from '../data/equipment';
import { ATTUNEMENTS_DEF, getAttunementConversionRate } from '../data/attunements';
describe('Enchantment Capacity Validation', () => {
it('should calculate capacity cost for single stack effects', () => {
// Mana Bolt spell effect has base capacity cost of 50
const cost = calculateEffectCapacityCost('spell_manaBolt', 1, 0);
expect(cost).toBe(50);
});
it('should apply scaling for multiple stacks', () => {
// damage_5 has base cost 15, each additional stack costs 20% more
const cost1 = calculateEffectCapacityCost('damage_5', 1, 0);
const cost2 = calculateEffectCapacityCost('damage_5', 2, 0);
// First stack: 15
// Second stack: 15 * 1.2 = 18
// Total: 33
expect(cost1).toBe(15);
expect(cost2).toBe(Math.floor(15 + 15 * 1.2));
});
it('should apply efficiency bonus to reduce cost', () => {
const costWithoutEfficiency = calculateEffectCapacityCost('spell_manaBolt', 1, 0);
const costWithEfficiency = calculateEffectCapacityCost('spell_manaBolt', 1, 0.1); // 10% reduction
expect(costWithEfficiency).toBe(Math.floor(costWithoutEfficiency * 0.9));
});
it('should respect equipment base capacity', () => {
// Civilian Shirt has base capacity 30
const shirt = EQUIPMENT_TYPES['civilianShirt'];
expect(shirt.baseCapacity).toBe(30);
// Basic Staff has base capacity 50
const staff = EQUIPMENT_TYPES['basicStaff'];
expect(staff.baseCapacity).toBe(50);
});
it('should reject enchantment designs exceeding equipment capacity', () => {
// Mana Bolt spell effect costs 50 capacity
// Civilian Shirt only has 30 capacity
const manaBoltCost = calculateEffectCapacityCost('spell_manaBolt', 1, 0);
const shirtCapacity = EQUIPMENT_TYPES['civilianShirt'].baseCapacity;
expect(manaBoltCost).toBeGreaterThan(shirtCapacity);
});
});
describe('Attunement Mana Type Unlocking', () => {
it('should define primary mana types for attunements', () => {
// Enchanter should have transference as primary mana
const enchanter = ATTUNEMENTS_DEF['enchanter'];
expect(enchanter.primaryManaType).toBe('transference');
// Fabricator should have earth as primary mana
const fabricator = ATTUNEMENTS_DEF['fabricator'];
expect(fabricator.primaryManaType).toBe('earth');
});
it('should have conversion rates for attunements with primary mana', () => {
// Enchanter should have a conversion rate
const enchanter = ATTUNEMENTS_DEF['enchanter'];
expect(enchanter.conversionRate).toBeGreaterThan(0);
// Get scaled conversion rate at level 1
const level1Rate = getAttunementConversionRate('enchanter', 1);
expect(level1Rate).toBe(enchanter.conversionRate);
// Higher level should have higher rate
const level5Rate = getAttunementConversionRate('enchanter', 5);
expect(level5Rate).toBeGreaterThan(level1Rate);
});
it('should have raw mana regen for all attunements', () => {
Object.values(ATTUNEMENTS_DEF).forEach(attunement => {
expect(attunement.rawManaRegen).toBeGreaterThanOrEqual(0);
});
});
});
describe('Floor HP State', () => {
it('should have getFloorMaxHP function that returns positive values', async () => {
const { getFloorMaxHP } = await import('../computed-stats');
for (let floor = 1; floor <= 100; floor++) {
const hp = getFloorMaxHP(floor);
expect(hp).toBeGreaterThan(0);
}
});
it('should scale HP correctly with floor progression', async () => {
const { getFloorMaxHP } = await import('../computed-stats');
const hp1 = getFloorMaxHP(1);
const hp10 = getFloorMaxHP(10);
const hp50 = getFloorMaxHP(50);
const hp100 = getFloorMaxHP(100);
expect(hp10).toBeGreaterThan(hp1);
expect(hp50).toBeGreaterThan(hp10);
expect(hp100).toBeGreaterThan(hp50);
});
});
describe('Element State', () => {
it('should have utility elements defined', async () => {
const { ELEMENTS } = await import('../constants');
// Check that utility elements exist
expect(ELEMENTS['mental']).toBeDefined();
expect(ELEMENTS['transference']).toBeDefined();
expect(ELEMENTS['force']).toBeDefined();
// Check categories
expect(ELEMENTS['mental'].cat).toBe('utility');
expect(ELEMENTS['transference'].cat).toBe('utility');
});
it('should have composite elements with recipes', async () => {
const { ELEMENTS } = await import('../constants');
// Blood is life + water
expect(ELEMENTS['blood'].cat).toBe('composite');
expect(ELEMENTS['blood'].recipe).toEqual(['life', 'water']);
// Metal is fire + earth
expect(ELEMENTS['metal'].cat).toBe('composite');
expect(ELEMENTS['metal'].recipe).toEqual(['fire', 'earth']);
});
});

View File

@@ -274,8 +274,11 @@ export function createCraftingSlice(
const enchantingLevel = state.skills.enchanting || 0; const enchantingLevel = state.skills.enchanting || 0;
if (enchantingLevel < 1) return false; if (enchantingLevel < 1) return false;
// Validate effects for equipment category // Get equipment type and category
const category = getEquipmentCategory(equipmentTypeId); const equipType = EQUIPMENT_TYPES[equipmentTypeId];
if (!equipType) return false;
const category = equipType.category;
if (!category) return false; if (!category) return false;
for (const eff of effects) { for (const eff of effects) {
@@ -288,6 +291,11 @@ export function createCraftingSlice(
// Calculate capacity cost // Calculate capacity cost
const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05; const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05;
const totalCapacityCost = calculateDesignCapacityCost(effects, efficiencyBonus); const totalCapacityCost = calculateDesignCapacityCost(effects, efficiencyBonus);
// Validate capacity - design must fit within equipment capacity
if (totalCapacityCost > equipType.baseCapacity) {
return false; // Design exceeds equipment capacity
}
// Create design ID // Create design ID
const designId = `design_${Date.now()}`; const designId = `design_${Date.now()}`;

View File

@@ -53,32 +53,6 @@ export const ACHIEVEMENTS: Record<string, AchievementDef> = {
reward: { insight: 500, manaBonus: 200, damageBonus: 0.25, title: 'Apex Climber' }, reward: { insight: 500, manaBonus: 200, damageBonus: 0.25, title: 'Apex Climber' },
}, },
// ─── Combo Achievements ───
comboStarter: {
id: 'comboStarter',
name: 'Combo Starter',
desc: 'Reach a 10-hit combo',
category: 'combat',
requirement: { type: 'combo', value: 10 },
reward: { insight: 15 },
},
comboMaster: {
id: 'comboMaster',
name: 'Combo Master',
desc: 'Reach a 50-hit combo',
category: 'combat',
requirement: { type: 'combo', value: 50 },
reward: { insight: 50, damageBonus: 0.05 },
},
comboLegend: {
id: 'comboLegend',
name: 'Combo Legend',
desc: 'Reach a 100-hit combo',
category: 'combat',
requirement: { type: 'combo', value: 100 },
reward: { insight: 150, damageBonus: 0.1, title: 'Combo Legend' },
},
// ─── Damage Achievements ─── // ─── Damage Achievements ───
hundredDamage: { hundredDamage: {
id: 'hundredDamage', id: 'hundredDamage',

View File

@@ -466,13 +466,6 @@ function makeInitial(overrides: Partial<GameState> = {}): GameState {
activeSpell: 'manaBolt', activeSpell: 'manaBolt',
currentAction: 'meditate', currentAction: 'meditate',
castProgress: 0, castProgress: 0,
combo: {
count: 0,
maxCombo: 0,
multiplier: 1,
elementChain: [],
decayTimer: 0,
},
spells: startSpells, spells: startSpells,
skills: overrides.skills || {}, skills: overrides.skills || {},
@@ -1464,6 +1457,16 @@ export const useGameStore = create<GameStore>()(
if (eff.stacks > effectDef.maxStacks) return false; if (eff.stacks > effectDef.maxStacks) return false;
} }
// Validate capacity - design must fit within equipment capacity
const efficiencyBonus = (state.skills.efficientEnchant || 0) * 0.05;
const totalCapacityCost = effects.reduce(
(total, eff) => total + calculateEffectCapacityCost(eff.effectId, eff.stacks, efficiencyBonus),
0
);
if (totalCapacityCost > type.baseCapacity) {
return false; // Design exceeds equipment capacity
}
// Calculate design time // Calculate design time
let designTime = 1; let designTime = 1;
for (const eff of effects) { for (const eff of effects) {

View File

@@ -317,15 +317,6 @@ export interface StudyTarget {
required: number; // Total hours needed required: number; // Total hours needed
} }
// Combo state for combat
export interface ComboState {
count: number; // Current combo hits
maxCombo: number; // Highest combo this session
multiplier: number; // Current damage multiplier
elementChain: string[]; // Last 3 elements used
decayTimer: number; // Hours until decay starts
}
export interface GameState { export interface GameState {
// Time // Time
day: number; day: number;
@@ -355,7 +346,6 @@ export interface GameState {
activeSpell: string; activeSpell: string;
currentAction: GameAction; currentAction: GameAction;
castProgress: number; // Progress towards next spell cast (0-1) castProgress: number; // Progress towards next spell cast (0-1)
combo: ComboState; // Combat combo tracking
// Spells // Spells
spells: Record<string, SpellState>; spells: Record<string, SpellState>;