diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index eb89447..5042cb3 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,5 +1,5 @@ # Circular Dependencies -Generated: 2026-05-25T09:51:15.852Z +Generated: 2026-05-25T09:55:36.893Z Found: 6 circular chain(s) — these MUST be fixed before modifying involved files. 1. Processed 132 files (1.5s) (2 warnings) diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index 09c6b6c..1c0329b 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-05-25T09:51:14.159Z", + "generated": "2026-05-25T09:55:35.204Z", "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." }, diff --git a/docs/project-structure.txt b/docs/project-structure.txt index 2cdcd72..e9396d9 100644 --- a/docs/project-structure.txt +++ b/docs/project-structure.txt @@ -246,6 +246,8 @@ Mana-Loop/ │ │ ├── data/ │ │ │ ├── disciplines/ │ │ │ │ ├── base.ts +│ │ │ │ ├── elemental-regen-advanced.ts +│ │ │ │ ├── elemental-regen.ts │ │ │ │ ├── enchanter-special.ts │ │ │ │ ├── enchanter-spells.ts │ │ │ │ ├── enchanter-utility.ts diff --git a/src/components/game/tabs/DisciplinesTab.tsx b/src/components/game/tabs/DisciplinesTab.tsx index 119123e..8d0cdba 100644 --- a/src/components/game/tabs/DisciplinesTab.tsx +++ b/src/components/game/tabs/DisciplinesTab.tsx @@ -2,6 +2,8 @@ import React, { useEffect, useState, useCallback } from 'react'; import { useDisciplineStore } from '@/lib/game/stores/discipline-slice'; import type { DisciplineDefinition } from '@/lib/game/types/disciplines'; import { baseDisciplines } from '@/lib/game/data/disciplines/base'; +import { elementalRegenDisciplines } from '@/lib/game/data/disciplines/elemental-regen'; +import { elementalRegenAdvancedDisciplines } from '@/lib/game/data/disciplines/elemental-regen-advanced'; import { enchanterDisciplines } from '@/lib/game/data/disciplines/enchanter'; import { fabricatorDisciplines } from '@/lib/game/data/disciplines/fabricator'; import { invokerDisciplines } from '@/lib/game/data/disciplines/invoker'; @@ -18,6 +20,8 @@ interface AttunementTab { const ATTUNEMENT_TABS: AttunementTab[] = [ { key: 'base', label: 'Base', items: baseDisciplines }, + { key: 'elemental-regen', label: 'Elemental Regen', items: elementalRegenDisciplines }, + { key: 'elemental-regen-advanced', label: 'Advanced Regen', items: elementalRegenAdvancedDisciplines }, { key: 'enchanter', label: 'Enchanter', items: enchanterDisciplines }, { key: 'fabricator', label: 'Fabricator', items: fabricatorDisciplines }, { key: 'invoker', label: 'Invoker', items: invokerDisciplines }, diff --git a/src/lib/game/data/disciplines/elemental-regen-advanced.ts b/src/lib/game/data/disciplines/elemental-regen-advanced.ts new file mode 100644 index 0000000..b3d20cc --- /dev/null +++ b/src/lib/game/data/disciplines/elemental-regen-advanced.ts @@ -0,0 +1,185 @@ +// ─── Elemental Regen Disciplines (Composite + Exotic) ───────────────────────── +// Regen disciplines for composite and exotic mana types. +// All are BASE attunement so they are available to every role once the element is unlocked. + +import { DisciplinesAttunementType } from '../../types/disciplines'; +import type { DisciplineDefinition } from '../../types/disciplines'; + +export const elementalRegenAdvancedDisciplines: DisciplineDefinition[] = [ + // ── Composite Elements ───────────────────────────────────────────────────── + { + id: 'regen-metal', + name: 'Metal Mana Flow', + attunement: DisciplinesAttunementType.BASE, + manaType: 'metal', + baseCost: 12, + description: 'Attune your metal mana to regenerate passively over time.', + statBonus: { stat: 'regen_metal', baseValue: 0.35 }, + difficultyFactor: 160, + scalingFactor: 80, + drainBase: 2, + requires: ['metal'], + perks: [ + { + id: 'regen-metal-1', + type: 'once', + threshold: 150, + value: 0, + description: '+0.35 Metal Regen/tick', + }, + { + id: 'regen-metal-inf', + type: 'infinite', + threshold: 400, + value: 100, + description: 'Every 100 XP: +0.15 Metal Regen/tick', + }, + ], + }, + { + id: 'regen-sand', + name: 'Sand Mana Flow', + attunement: DisciplinesAttunementType.BASE, + manaType: 'sand', + baseCost: 12, + description: 'Attune your sand mana to regenerate passively over time.', + statBonus: { stat: 'regen_sand', baseValue: 0.35 }, + difficultyFactor: 160, + scalingFactor: 80, + drainBase: 2, + requires: ['sand'], + perks: [ + { + id: 'regen-sand-1', + type: 'once', + threshold: 150, + value: 0, + description: '+0.35 Sand Regen/tick', + }, + { + id: 'regen-sand-inf', + type: 'infinite', + threshold: 400, + value: 100, + description: 'Every 100 XP: +0.15 Sand Regen/tick', + }, + ], + }, + { + id: 'regen-lightning', + name: 'Lightning Mana Flow', + attunement: DisciplinesAttunementType.BASE, + manaType: 'lightning', + baseCost: 12, + description: 'Attune your lightning mana to regenerate passively over time.', + statBonus: { stat: 'regen_lightning', baseValue: 0.35 }, + difficultyFactor: 160, + scalingFactor: 80, + drainBase: 2, + requires: ['lightning'], + perks: [ + { + id: 'regen-lightning-1', + type: 'once', + threshold: 150, + value: 0, + description: '+0.35 Lightning Regen/tick', + }, + { + id: 'regen-lightning-inf', + type: 'infinite', + threshold: 400, + value: 100, + description: 'Every 100 XP: +0.15 Lightning Regen/tick', + }, + ], + }, + // ── Exotic Elements ──────────────────────────────────────────────────────── + { + id: 'regen-crystal', + name: 'Crystal Mana Flow', + attunement: DisciplinesAttunementType.BASE, + manaType: 'crystal', + baseCost: 18, + description: 'Attune your crystal mana to regenerate passively over time.', + statBonus: { stat: 'regen_crystal', baseValue: 0.25 }, + difficultyFactor: 220, + scalingFactor: 110, + drainBase: 3, + requires: ['crystal'], + perks: [ + { + id: 'regen-crystal-1', + type: 'once', + threshold: 200, + value: 0, + description: '+0.25 Crystal Regen/tick', + }, + { + id: 'regen-crystal-inf', + type: 'infinite', + threshold: 500, + value: 100, + description: 'Every 100 XP: +0.1 Crystal Regen/tick', + }, + ], + }, + { + id: 'regen-stellar', + name: 'Stellar Mana Flow', + attunement: DisciplinesAttunementType.BASE, + manaType: 'stellar', + baseCost: 18, + description: 'Attune your stellar mana to regenerate passively over time.', + statBonus: { stat: 'regen_stellar', baseValue: 0.25 }, + difficultyFactor: 220, + scalingFactor: 110, + drainBase: 3, + requires: ['stellar'], + perks: [ + { + id: 'regen-stellar-1', + type: 'once', + threshold: 200, + value: 0, + description: '+0.25 Stellar Regen/tick', + }, + { + id: 'regen-stellar-inf', + type: 'infinite', + threshold: 500, + value: 100, + description: 'Every 100 XP: +0.1 Stellar Regen/tick', + }, + ], + }, + { + id: 'regen-void', + name: 'Void Mana Flow', + attunement: DisciplinesAttunementType.BASE, + manaType: 'void', + baseCost: 18, + description: 'Attune your void mana to regenerate passively over time.', + statBonus: { stat: 'regen_void', baseValue: 0.25 }, + difficultyFactor: 220, + scalingFactor: 110, + drainBase: 3, + requires: ['void'], + perks: [ + { + id: 'regen-void-1', + type: 'once', + threshold: 200, + value: 0, + description: '+0.25 Void Regen/tick', + }, + { + id: 'regen-void-inf', + type: 'infinite', + threshold: 500, + value: 100, + description: 'Every 100 XP: +0.1 Void Regen/tick', + }, + ], + }, +]; diff --git a/src/lib/game/data/disciplines/elemental-regen.ts b/src/lib/game/data/disciplines/elemental-regen.ts new file mode 100644 index 0000000..1845278 --- /dev/null +++ b/src/lib/game/data/disciplines/elemental-regen.ts @@ -0,0 +1,86 @@ +// ─── Elemental Regen Disciplines (Base + Utility) ───────────────────────────── +// One discipline per mana type that provides passive regen for that element. +// All are BASE attunement so they are available to every role once the element is unlocked. + +import { DisciplinesAttunementType } from '../../types/disciplines'; +import type { DisciplineDefinition } from '../../types/disciplines'; + +const BASE_REGEN = 0.5; +const BASE_DRAIN = 1.5; +const BASE_DIFF = 120; +const BASE_SCALE = 60; + +function makeBaseRegen(id: string, name: string, manaType: string, cost: number): DisciplineDefinition { + const shortId = id.replace('regen-', ''); + return { + id, + name: `${name} Mana Flow`, + attunement: DisciplinesAttunementType.BASE, + manaType: manaType as DisciplineDefinition['manaType'], + baseCost: cost, + description: `Attune your ${name.toLowerCase()} mana to regenerate passively over time.`, + statBonus: { stat: `regen_${shortId}` as DisciplineDefinition['statBonus']['stat'], baseValue: BASE_REGEN }, + difficultyFactor: BASE_DIFF, + scalingFactor: BASE_SCALE, + drainBase: BASE_DRAIN, + requires: [manaType], + perks: [ + { + id: `${id}-1`, + type: 'once', + threshold: 100, + value: 0, + description: `+${BASE_REGEN} ${name} Regen/tick`, + }, + { + id: `${id}-inf`, + type: 'infinite', + threshold: 300, + value: 100, + description: `Every 100 XP: +0.25 ${name} Regen/tick`, + }, + ], + }; +} + +export const elementalRegenDisciplines: DisciplineDefinition[] = [ + // ── Base Elements ────────────────────────────────────────────────────────── + makeBaseRegen('regen-fire', 'Fire', 'fire', 8), + makeBaseRegen('regen-water', 'Water', 'water', 8), + makeBaseRegen('regen-air', 'Air', 'air', 8), + makeBaseRegen('regen-earth', 'Earth', 'earth', 8), + makeBaseRegen('regen-light', 'Light', 'light', 8), + makeBaseRegen('regen-dark', 'Dark', 'dark', 8), + makeBaseRegen('regen-death', 'Death', 'death', 8), + + // ── Utility Element ──────────────────────────────────────────────────────── + { + id: 'regen-transference', + name: 'Transference Mana Flow', + attunement: DisciplinesAttunementType.BASE, + manaType: 'transference', + baseCost: 6, + description: 'Attune your transference mana to regenerate passively over time.', + statBonus: { stat: 'regen_transference', baseValue: 0.4 }, + difficultyFactor: 100, + scalingFactor: 50, + drainBase: 1, + requires: ['transference'], + perks: [ + { + id: 'regen-transference-1', + type: 'once', + threshold: 100, + value: 0, + description: '+0.4 Transference Regen/tick', + }, + { + id: 'regen-transference-inf', + type: 'infinite', + threshold: 300, + value: 100, + description: 'Every 100 XP: +0.2 Transference Regen/tick', + }, + ], + }, +]; diff --git a/src/lib/game/data/disciplines/index.ts b/src/lib/game/data/disciplines/index.ts index 58645f8..3e18137 100644 --- a/src/lib/game/data/disciplines/index.ts +++ b/src/lib/game/data/disciplines/index.ts @@ -2,6 +2,8 @@ // Aggregates all discipline definitions into a single ALL_DISCIPLINES array import { baseDisciplines } from './base'; +import { elementalRegenDisciplines } from './elemental-regen'; +import { elementalRegenAdvancedDisciplines } from './elemental-regen-advanced'; import { enchanterDisciplines } from './enchanter'; import { enchanterUtilityDisciplines } from './enchanter-utility'; import { enchanterSpellDisciplines } from './enchanter-spells'; @@ -12,6 +14,8 @@ import type { DisciplineDefinition } from '../../types/disciplines'; export const ALL_DISCIPLINES: DisciplineDefinition[] = [ ...baseDisciplines, + ...elementalRegenDisciplines, + ...elementalRegenAdvancedDisciplines, ...enchanterDisciplines, ...enchanterUtilityDisciplines, ...enchanterSpellDisciplines, @@ -21,6 +25,8 @@ export const ALL_DISCIPLINES: DisciplineDefinition[] = [ ]; export { baseDisciplines } from './base'; +export { elementalRegenDisciplines } from './elemental-regen'; +export { elementalRegenAdvancedDisciplines } from './elemental-regen-advanced'; export { enchanterDisciplines } from './enchanter'; export { enchanterUtilityDisciplines } from './enchanter-utility'; export { enchanterSpellDisciplines } from './enchanter-spells'; diff --git a/src/lib/game/effects.ts b/src/lib/game/effects.ts index 3f86655..a105629 100755 --- a/src/lib/game/effects.ts +++ b/src/lib/game/effects.ts @@ -94,6 +94,15 @@ export function computeAllEffects( } } + // Merge per-element regen from discipline effects (regen_{element}) + const perElementRegenBonus: Record = { ...upgradeEffects.perElementRegenBonus }; + for (const [key, value] of Object.entries(disciplineEffects.bonuses)) { + if (key.startsWith('regen_') && key !== 'regenBonus') { + const element = key.replace('regen_', ''); + perElementRegenBonus[element] = (perElementRegenBonus[element] || 0) + value; + } + } + const merged: UnifiedEffects = { ...upgradeEffects, maxManaBonus: upgradeEffects.maxManaBonus + (equipmentEffects.bonuses.maxMana || 0) + (disciplineEffects.bonuses.maxManaBonus || 0), @@ -102,6 +111,7 @@ export function computeAllEffects( baseDamageBonus: upgradeEffects.baseDamageBonus + (equipmentEffects.bonuses.baseDamage || 0) + (disciplineEffects.bonuses.baseDamageBonus || 0), elementCapBonus: upgradeEffects.elementCapBonus + (equipmentEffects.bonuses.elementCap || 0) + (disciplineEffects.bonuses.elementCapBonus || 0), perElementCapBonus, + perElementRegenBonus, maxManaMultiplier: upgradeEffects.maxManaMultiplier * (equipmentEffects.multipliers.maxMana || 1), regenMultiplier: upgradeEffects.regenMultiplier * (equipmentEffects.multipliers.regen || 1), clickManaMultiplier: upgradeEffects.clickManaMultiplier * (equipmentEffects.multipliers.clickMana || 1), diff --git a/src/lib/game/effects/upgrade-effects.ts b/src/lib/game/effects/upgrade-effects.ts index ab8dc6f..71fea01 100755 --- a/src/lib/game/effects/upgrade-effects.ts +++ b/src/lib/game/effects/upgrade-effects.ts @@ -57,6 +57,7 @@ export function computeEffects( elementCapMultiplier: 1, elementCapBonus: 0, perElementCapBonus: {}, + perElementRegenBonus: {}, conversionCostMultiplier: 1, doubleCraftChance: 0, permanentRegenBonus: 0, diff --git a/src/lib/game/effects/upgrade-effects.types.ts b/src/lib/game/effects/upgrade-effects.types.ts index 86b076b..77a5589 100644 --- a/src/lib/game/effects/upgrade-effects.types.ts +++ b/src/lib/game/effects/upgrade-effects.types.ts @@ -41,6 +41,7 @@ export interface ComputedEffects { elementCapMultiplier: number; elementCapBonus: number; perElementCapBonus: Record; + perElementRegenBonus: Record; conversionCostMultiplier: number; doubleCraftChance: number; diff --git a/src/lib/game/stores/discipline-slice.ts b/src/lib/game/stores/discipline-slice.ts index 390646d..b45f5db 100644 --- a/src/lib/game/stores/discipline-slice.ts +++ b/src/lib/game/stores/discipline-slice.ts @@ -11,6 +11,8 @@ import { getUnlockedPerks, } from '../utils/discipline-math'; import { baseDisciplines } from '../data/disciplines/base'; +import { elementalRegenDisciplines } from '../data/disciplines/elemental-regen'; +import { elementalRegenAdvancedDisciplines } from '../data/disciplines/elemental-regen-advanced'; import { enchanterDisciplines } from '../data/disciplines/enchanter'; import { enchanterUtilityDisciplines } from '../data/disciplines/enchanter-utility'; import { enchanterSpellDisciplines } from '../data/disciplines/enchanter-spells'; @@ -21,6 +23,8 @@ import { MAX_CONCURRENT_DISCIPLINES } from '../types/disciplines'; const ALL_DISCIPLINES = [ ...baseDisciplines, + ...elementalRegenDisciplines, + ...elementalRegenAdvancedDisciplines, ...enchanterDisciplines, ...enchanterUtilityDisciplines, ...enchanterSpellDisciplines, diff --git a/src/lib/game/stores/gameStore.ts b/src/lib/game/stores/gameStore.ts index 688cbaf..0e71861 100755 --- a/src/lib/game/stores/gameStore.ts +++ b/src/lib/game/stores/gameStore.ts @@ -267,6 +267,22 @@ export const useGameStore = create()( rawMana = disciplineResult.rawMana; elements = disciplineResult.elements; + // Apply per-element regen from discipline effects (regen_{element}) + for (const [key, value] of Object.entries(disciplineEffects.bonuses)) { + if (key.startsWith('regen_') && key !== 'regenBonus') { + const element = key.replace('regen_', ''); + if (elements[element]) { + elements[element] = { + ...elements[element], + current: Math.min( + elements[element].max, + elements[element].current + value * HOURS_PER_TICK, + ), + }; + } + } + } + // Unlock enchantment effects from newly unlocked discipline perks if (disciplineResult.unlockedEffects.length > 0) { useCraftingStore.getState().unlockEffects(disciplineResult.unlockedEffects);