feat: add mana conversion system spec and ticket
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m2s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m2s
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Circular Dependencies
|
||||
Generated: 2026-06-04T09:16:19.999Z
|
||||
Generated: 2026-06-04T09:37:34.683Z
|
||||
|
||||
No circular dependencies found. ✅
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"_meta": {
|
||||
"generated": "2026-06-04T09:16:18.073Z",
|
||||
"generated": "2026-06-04T09:37:32.833Z",
|
||||
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
||||
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
||||
},
|
||||
|
||||
@@ -27,6 +27,7 @@ Mana-Loop/
|
||||
│ │ │ │ │ └── pact-system-spec.md
|
||||
│ │ │ │ └── invoker-spec.md
|
||||
│ │ │ └── attunement-system-spec.md
|
||||
│ │ ├── mana-conversion-spec.md
|
||||
│ │ ├── spire-climbing-spec.md
|
||||
│ │ └── spire-combat-spec.md
|
||||
│ ├── GAME_BRIEFING.md
|
||||
|
||||
@@ -0,0 +1,427 @@
|
||||
# Mana Conversion System — Specification
|
||||
|
||||
## Overview
|
||||
|
||||
This spec defines a unified mana conversion system that replaces the current fragmented approach (attunement conversions, discipline conversions, manual conversion, and guardian pact conversions). All conversion types use the same core mechanics: consuming source mana types to produce a destination mana type, with costs deducted from **regen** (not from the mana pool directly).
|
||||
|
||||
---
|
||||
|
||||
## 1. Element Distance from Raw Mana
|
||||
|
||||
Every mana type has a **distance** from raw mana. This value is used in two places:
|
||||
1. Calculating conversion cost ratios
|
||||
2. Calculating meditation multiplier strength for that element's conversion
|
||||
|
||||
### Distance Table
|
||||
|
||||
| Element | Category | Distance |
|
||||
|---------|----------|----------|
|
||||
| Raw | — | 0 |
|
||||
| Fire, Water, Air, Earth, Light, Dark, Death | Base | 1 |
|
||||
| Transference | Utility | 1 |
|
||||
| Metal, Sand, Lightning, Frost, BlackFlame, RadiantFlames, Miasma, ShadowGlass | Composite | 2 |
|
||||
| Crystal, Stellar, Void, Soul, Plasma | Exotic (tier 1) | 3 |
|
||||
| Time | Exotic (tier 2) | 4 |
|
||||
|
||||
### Reusable Function
|
||||
|
||||
```typescript
|
||||
// src/lib/game/utils/element-distance.ts
|
||||
export function getElementDistance(elementId: string): number
|
||||
```
|
||||
|
||||
Returns the distance for any element. If a composite element's recipe contains components at different distances, the element's distance = max(component distances) + 1.
|
||||
|
||||
---
|
||||
|
||||
## 2. Conversion Cost Ratios
|
||||
|
||||
All conversions produce **1 unit** of destination mana. The cost depends on the destination's distance from raw.
|
||||
|
||||
### Cost Formula
|
||||
|
||||
For a destination element at distance `d`:
|
||||
|
||||
- **Raw mana cost** = `10^(d+1)`
|
||||
- Distance 1 (base): `10^2 = 100` raw per 1 element
|
||||
- Distance 2 (composite): `10^3 = 1,000` raw per 1 element
|
||||
- Distance 3 (exotic): `10^4 = 10,000` raw per 1 element
|
||||
- Distance 4 (time): `10^5 = 100,000` raw per 1 element
|
||||
|
||||
- **Each component mana cost** = `10 * (d + 1)` per 1 destination element
|
||||
- Distance 1: `10 * 2 = 20` of that element per 1 destination
|
||||
- Distance 2: `10 * 3 = 30` of that element per 1 destination
|
||||
- Distance 3: `10 * 4 = 40` of that element per 1 destination
|
||||
- Distance 4: `10 * 5 = 50` of that element per 1 destination
|
||||
|
||||
### Cost Table (per 1 unit of destination mana)
|
||||
|
||||
| Destination | Distance | Raw Cost | Each Component Cost | Components |
|
||||
|-------------|----------|----------|---------------------|------------|
|
||||
| Fire (base) | 1 | 100 | — | — |
|
||||
| Transference | 1 | 100 | — | — |
|
||||
| Metal | 2 | 1,000 | 30 fire + 30 earth | fire, earth |
|
||||
| Sand | 2 | 1,000 | 30 earth + 30 water | earth, water |
|
||||
| Lightning | 2 | 1,000 | 30 fire + 30 air | fire, air |
|
||||
| Frost | 2 | 1,000 | 30 air + 30 water | air, water |
|
||||
| BlackFlame | 2 | 1,000 | 30 dark + 30 fire | dark, fire |
|
||||
| Radiant Flames | 2 | 1,000 | 30 light + 30 fire | light, fire |
|
||||
| Miasma | 2 | 1,000 | 30 air + 30 death | air, death |
|
||||
| Shadow Glass | 2 | 1,000 | 30 earth + 30 dark | earth, dark |
|
||||
| Crystal | 3 | 10,000 | 40 sand + 40 light | sand, light |
|
||||
| Stellar | 3 | 10,000 | 40 plasma + 40 light | plasma, light |
|
||||
| Void | 3 | 10,000 | 40 dark + 40 death | dark, death |
|
||||
| Soul | 3 | 10,000 | 40 light + 40 dark + 40 transference | light, dark, transference |
|
||||
| Plasma | 3 | 10,000 | 40 lightning + 40 fire + 40 transference | lightning, fire, transference |
|
||||
| Time | 4 | 100,000 | 50 soul + 50 sand + 50 transference | soul, sand, transference |
|
||||
|
||||
### Key Constraint
|
||||
|
||||
Raw mana cost is always **greater** than any individual component cost. This is inherent in the formula: `10^(d+1)` for raw vs `10*(d+1)` for each component.
|
||||
|
||||
---
|
||||
|
||||
## 3. Conversion Rate — Unified Formula
|
||||
|
||||
All three sources (disciplines, attunements, guardian pacts) contribute to a single **base conversion rate** for each element. This rate is then exponentially boosted by attunement levels and pact bonuses.
|
||||
|
||||
### Formula
|
||||
|
||||
```
|
||||
finalRate = (disciplineRate + attunementBaseRate + pactBaseRate) ^ (1 + attunementLevelBonus + pactLevelBonus)
|
||||
```
|
||||
|
||||
Where:
|
||||
- `disciplineRate` = sum of conversion rates from active disciplines for this element (see §4)
|
||||
- `attunementBaseRate` = sum of base conversion rates from attunements for this element (see §5)
|
||||
- `pactBaseRate` = sum of base conversion rates from guardian pacts for this element (see §6)
|
||||
- `attunementLevelBonus` = sum of relevant attunement levels (e.g., Enchanter level for transference, Fabricator level for earth)
|
||||
- `pactLevelBonus` = count of pacts with guardians that have this element as primary × Invoker attunement level
|
||||
|
||||
### Example
|
||||
|
||||
A player with:
|
||||
- Fire Conversion discipline active (rate = 0.5)
|
||||
- Enchanter attunement level 3 (no fire base rate, but level contributes to exponent if fire is the attunement's primary)
|
||||
- Fabricator attunement level 2 (earth primary, so contributes to earth conversions)
|
||||
- 2 fire-type guardian pacts, Invoker level 3
|
||||
|
||||
For **fire mana** conversion:
|
||||
```
|
||||
baseRate = 0.5 (discipline) + 0 (no attunement base for fire) + 0 (no pact base for fire)
|
||||
exponent = 1 + 0 (no attunement has fire as primary) + 0 (no fire-type pact bonus)
|
||||
finalRate = 0.5^1 = 0.5/hr
|
||||
```
|
||||
|
||||
For **metal mana** conversion (fire + earth):
|
||||
```
|
||||
baseRate = 0.35 (metal discipline) + 0 (no attunement base) + 0 (no pact base)
|
||||
exponent = 1 + 2 (Fabricator level 2, earth is a component of metal) + 0
|
||||
finalRate = 0.35^3 = 0.0429/hr
|
||||
```
|
||||
|
||||
Wait — this produces *lower* rates at higher levels, which is wrong. The exponent should be a **multiplier**, not an exponent on the rate. Let me restate:
|
||||
|
||||
### Corrected Formula
|
||||
|
||||
```
|
||||
finalRate = (disciplineRate + attunementBaseRate + pactBaseRate) × (1 + attunementLevelBonus + pactLevelBonus)
|
||||
```
|
||||
|
||||
Where the multiplier is additive:
|
||||
- `attunementLevelBonus` = sum of relevant attunement levels × 0.5 (each level adds +50% to rate)
|
||||
- `pactLevelBonus` = count of pacts with this element × Invoker level × 0.25
|
||||
|
||||
So:
|
||||
```
|
||||
finalRate = baseRate × (1 + Σ(attunementLevel_i × 0.5) + Σ(pactCount_element × invokerLevel × 0.25))
|
||||
```
|
||||
|
||||
### Revised Example
|
||||
|
||||
For **metal mana** with Metal Conversion discipline (0.35/hr), Fabricator level 2:
|
||||
```
|
||||
baseRate = 0.35
|
||||
multiplier = 1 + (2 × 0.5) = 2.0
|
||||
finalRate = 0.35 × 2.0 = 0.70/hr
|
||||
```
|
||||
|
||||
For **transference mana** with Transference Conversion discipline (0.4/hr), Enchanter level 3:
|
||||
```
|
||||
baseRate = 0.4
|
||||
multiplier = 1 + (3 × 0.5) = 2.5
|
||||
finalRate = 0.4 × 2.5 = 1.0/hr
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Discipline Contributions
|
||||
|
||||
Each conversion discipline provides a **base rate** that scales with XP.
|
||||
|
||||
### Base Rates (per hour)
|
||||
|
||||
| Element | Base Rate | Difficulty Factor | Scaling Factor |
|
||||
|---------|-----------|-------------------|----------------|
|
||||
| Fire, Water, Air, Earth, Light, Dark, Death | 0.5 | 120 | 60 |
|
||||
| Transference | 0.4 | 100 | 50 |
|
||||
| Metal, Sand, Lightning, Frost | 0.35 | 160 | 80 |
|
||||
| BlackFlame, RadiantFlames, Miasma, ShadowGlass | 0.30 | 170 | 85 |
|
||||
| Crystal, Void | 0.25 | 220 | 110 |
|
||||
| Stellar, Soul, Plasma | 0.20 | 240 | 120 |
|
||||
| Time | 0.15 | 260 | 130 |
|
||||
|
||||
### XP Scaling
|
||||
|
||||
The discipline's effective rate bonus follows the standard stat bonus formula:
|
||||
```
|
||||
statBonus = baseValue × (XP / scalingFactor)^0.65
|
||||
```
|
||||
|
||||
The discipline's total contribution to the base rate is:
|
||||
```
|
||||
disciplineRate = baseRate + statBonus
|
||||
```
|
||||
|
||||
### Perks
|
||||
|
||||
Each discipline has perks that add flat bonuses to the rate:
|
||||
- **`once` perk**: grants `+baseRate` to the conversion rate at threshold XP
|
||||
- **`infinite` perk**: every N XP grants `+baseRate × 0.5` to the conversion rate
|
||||
|
||||
---
|
||||
|
||||
## 5. Attunement Contributions
|
||||
|
||||
Attunements provide a **base conversion rate** for their primary mana type, plus a **level-based multiplier** to all conversions involving their element.
|
||||
|
||||
### Attunement Base Rates
|
||||
|
||||
| Attunement | Primary Mana | Base Rate (per hour) |
|
||||
|------------|--------------|---------------------|
|
||||
| Enchanter | Transference | 0.2 |
|
||||
| Fabricator | Earth | 0.25 |
|
||||
| Invoker | None | 0 |
|
||||
|
||||
### Attunement Level Multiplier
|
||||
|
||||
Each attunement level adds +0.5 to the multiplier for conversions where the attunement's primary element is either:
|
||||
- The destination element, OR
|
||||
- A component element of the destination
|
||||
|
||||
Example: Fabricator (earth) level 3 boosts:
|
||||
- Earth conversions (earth is destination)
|
||||
- Metal conversions (earth is component)
|
||||
- Sand conversions (earth is component)
|
||||
- Shadow Glass conversions (earth is component)
|
||||
|
||||
But NOT fire conversions (earth is not involved).
|
||||
|
||||
---
|
||||
|
||||
## 6. Guardian Pact Contributions
|
||||
|
||||
Guardian pacts provide:
|
||||
1. A **base conversion rate** for the guardian's element
|
||||
2. A **level bonus** to the multiplier, scaled by Invoker attunement level
|
||||
|
||||
### Pact Base Rate
|
||||
|
||||
Each signed pact grants `+0.15/hr` base rate for the guardian's primary element.
|
||||
|
||||
### Pact Level Bonus
|
||||
|
||||
For each signed pact whose guardian has element E as primary:
|
||||
```
|
||||
pactLevelBonus_E += invokerLevel × 0.25
|
||||
```
|
||||
|
||||
So an Invoker at level 4 with 2 fire-type pacts grants:
|
||||
```
|
||||
pactLevelBonus_fire = 2 × 4 × 0.25 = 2.0
|
||||
```
|
||||
|
||||
This adds to the multiplier for fire conversions and any composite that uses fire.
|
||||
|
||||
---
|
||||
|
||||
## 7. Meditation Multiplier
|
||||
|
||||
Meditation boosts conversion rates, but the boost is reduced for elements further from raw.
|
||||
|
||||
### Formula
|
||||
|
||||
```
|
||||
meditationBoost = 1 + (meditationMultiplier - 1) / distance
|
||||
```
|
||||
|
||||
Where `distance` is the destination element's distance from raw mana.
|
||||
|
||||
| Element Distance | Meditation Strength |
|
||||
|-----------------|-------------------|
|
||||
| 1 (base) | Full: `meditationMultiplier` |
|
||||
| 2 (composite) | Half: `1 + (med - 1) / 2` |
|
||||
| 3 (exotic) | Third: `1 + (med - 1) / 3` |
|
||||
| 4 (time) | Quarter: `1 + (med - 1) / 4` |
|
||||
|
||||
For elements with components at different distances, use the **highest** distance value (i.e., the weakest meditation boost).
|
||||
|
||||
---
|
||||
|
||||
## 8. Regen Deduction Model
|
||||
|
||||
All conversion costs are deducted from **mana regen**, not from the mana pool directly. This means:
|
||||
|
||||
1. Each element has a **gross regen** (from attunements, upgrades, etc.)
|
||||
2. Conversions that consume this element as a source **reduce** the effective regen
|
||||
3. The remaining regen is the **net regen** that actually adds to the pool
|
||||
|
||||
### Raw Mana
|
||||
|
||||
```
|
||||
rawNetRegen = rawGrossRegen
|
||||
- Σ (conversionRate_destination × rawCost_destination) for all active conversions
|
||||
```
|
||||
|
||||
### Element Mana (e.g., fire)
|
||||
|
||||
```
|
||||
fireNetRegen = fireGrossRegen
|
||||
+ fireProducedRate (from raw→fire conversion)
|
||||
- Σ (conversionRate_destination × fireCost_destination) for all conversions using fire as component
|
||||
```
|
||||
|
||||
### Display Format
|
||||
|
||||
Each element's regen display shows:
|
||||
```
|
||||
Fire Mana Regen:
|
||||
+0.50/hr converted from raw mana (Fire Conversion discipline, rate × attunement multiplier × meditation)
|
||||
-0.15/hr being converted into Metal mana (30 per unit × 0.005 units/hr)
|
||||
-0.10/hr being converted into Lightning mana (30 per unit × 0.0033 units/hr)
|
||||
─────────────────
|
||||
+0.25/hr net fire mana regen
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. Insufficient Regen — Auto-Pause
|
||||
|
||||
If a conversion's source cost exceeds the **gross regen** of that source type, the conversion is **completely disabled** (not partially throttled).
|
||||
|
||||
### Conditions
|
||||
|
||||
A conversion for element E is paused if:
|
||||
```
|
||||
conversionRate_E × sourceCost_source > sourceGrossRegen
|
||||
```
|
||||
|
||||
for **any** source type (raw or component element) in the conversion.
|
||||
|
||||
### UI Warning
|
||||
|
||||
When a conversion is paused due to insufficient regen:
|
||||
- The conversion's entry in the stats tab shows a **red warning**: "⚠️ PAUSED: Insufficient [source] regen (need X/hr, have Y/hr)"
|
||||
- The mana display for the source element shows a warning icon next to the draining conversion
|
||||
|
||||
### Auto-Resume
|
||||
|
||||
When regen increases (e.g., attunement levels up, new discipline XP gained, meditation active), paused conversions automatically resume if the regen now covers the cost.
|
||||
|
||||
---
|
||||
|
||||
## 10. No Manual Conversion
|
||||
|
||||
The existing `convertMana` action and `processConvertAction` are **removed**. All mana conversion happens passively through the unified system. The "convert" player action is removed from the action buttons.
|
||||
|
||||
---
|
||||
|
||||
## 11. Stats Tab Display
|
||||
|
||||
The Stats tab includes a new **Conversion Stats** section showing:
|
||||
|
||||
### Per-Element Conversion Table
|
||||
|
||||
For each element with active conversions:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 🔥 FIRE MANA CONVERSION │
|
||||
│ │
|
||||
│ Base Rate: 0.50/hr (Fire Conversion discipline) │
|
||||
│ Attunement Bonus: ×1.00 (no attunement for fire) │
|
||||
│ Pact Bonus: ×1.00 (0 fire-type pacts) │
|
||||
│ Meditation: ×1.00 (not meditating) │
|
||||
│ ───────────────────────────────────────── │
|
||||
│ Effective Rate: 0.50/hr → produces 0.50 fire/hr │
|
||||
│ │
|
||||
│ Costs (deducted from raw regen): │
|
||||
│ Raw: 100 × 0.50 = 50.0 raw/hr │
|
||||
│ │
|
||||
│ Drained by downstream conversions: │
|
||||
│ → Metal: 30 × 0.005 = 0.15 fire/hr │
|
||||
│ → Lightning: 30 × 0.003 = 0.10 fire/hr │
|
||||
│ │
|
||||
│ Net Fire Regen: +0.50 - 0.15 - 0.10 = +0.25 fire/hr │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Formula Summary
|
||||
|
||||
A collapsible formula reference is shown at the top:
|
||||
|
||||
```
|
||||
Conversion Rate Formula:
|
||||
finalRate = (disciplineRate + attunementBase + pactBase) × attunementMult × pactMult × meditationMult
|
||||
|
||||
Where:
|
||||
attunementMult = 1 + Σ(relevantAttunementLevel × 0.5)
|
||||
pactMult = 1 + Σ(pactCount_element × invokerLevel × 0.25)
|
||||
meditationMult = 1 + (meditationMultiplier - 1) / elementDistance
|
||||
|
||||
Cost per 1 unit of destination:
|
||||
rawCost = 10^(distance+1)
|
||||
componentCost = 10 × (distance+1) per component
|
||||
|
||||
All costs deducted from source regen (not from mana pool).
|
||||
Conversions pause if source regen < conversion cost.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Implementation Notes
|
||||
|
||||
### New Files
|
||||
|
||||
- `src/lib/game/utils/element-distance.ts` — `getElementDistance()` function
|
||||
- `src/lib/game/utils/conversion-rates.ts` — Unified conversion rate calculator
|
||||
- `src/lib/game/data/conversion-costs.ts` — Cost ratio table per element
|
||||
|
||||
### Modified Files
|
||||
|
||||
- `src/lib/game/data/disciplines/elemental-regen.ts` — Update base rates, remove drain model
|
||||
- `src/lib/game/data/disciplines/elemental-regen-advanced.ts` — Update base rates, remove drain model
|
||||
- `src/lib/game/data/attunements.ts` — Update conversion rates to match new system
|
||||
- `src/lib/game/effects/discipline-effects.ts` — Update conversion computation
|
||||
- `src/lib/game/stores/gameStore.ts` — Replace tick conversion logic with unified system
|
||||
- `src/lib/game/stores/manaStore.ts` — Remove `convertMana`, `processConvertAction`, `craftComposite`
|
||||
- `src/lib/game/stores/prestigeStore.ts` — Add pact conversion rate data
|
||||
- `src/components/game/tabs/StatsTab/ElementStatsSection.tsx` — Add conversion display
|
||||
- `src/components/game/ManaDisplay.tsx` — Add per-element regen breakdown
|
||||
|
||||
### Removed
|
||||
|
||||
- Manual conversion (`convertMana`, `processConvertAction`)
|
||||
- Composite crafting via `craftComposite` (replaced by passive conversion)
|
||||
- The "convert" action from player actions
|
||||
- Per-tick mana pool deduction for conversions (replaced by regen deduction)
|
||||
|
||||
---
|
||||
|
||||
## 13. Migration Notes
|
||||
|
||||
Existing save data will need migration:
|
||||
- Active discipline conversion rates are preserved (the XP and discipline IDs stay the same)
|
||||
- Attunement conversion rates are recalculated from the new base rates
|
||||
- Any manually-converted element mana in pools is preserved
|
||||
- The `convertMana` and `craftComposite` store actions are kept as no-ops for save compatibility but have no UI
|
||||
Reference in New Issue
Block a user