Major bug fixes and system cleanup
Some checks failed
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m16s
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:
313
AUDIT_REPORT.md
313
AUDIT_REPORT.md
@@ -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**
|
|
||||||
@@ -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*
|
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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':
|
||||||
|
|||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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>
|
||||||
</>
|
</>
|
||||||
|
|||||||
133
src/lib/game/__tests__/bug-fixes.test.ts
Normal file
133
src/lib/game/__tests__/bug-fixes.test.ts
Normal 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']);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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()}`;
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
Reference in New Issue
Block a user