diff --git a/AGENTS.md b/AGENTS.md index f549351..16ef7f6 100755 --- a/AGENTS.md +++ b/AGENTS.md @@ -153,7 +153,7 @@ ManaDrainPerTick = drainBase × (1 + (XP / difficultyFactor)^0.4) ## Banned -Lifesteal/healing, scroll crafting, ascension skills, LabTab, pause mechanics, familiar system, mana types: `life`, `blood`, `wood`, `mental`, `force` +Lifesteal/healing, scroll crafting, ascension skills, LabTab, pause mechanics, familiar system, shields, mana types: `life`, `blood`, `wood`, `mental`, `force` ## File Limit diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index 8063280..1816160 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,4 +1,4 @@ # Circular Dependencies -Generated: 2026-05-27T17:14:06.661Z +Generated: 2026-05-27T19:08:46.353Z No circular dependencies found. ✅ diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index 8d063f5..ed0a817 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_meta": { - "generated": "2026-05-27T17:14:04.806Z", + "generated": "2026-05-27T19:08:44.608Z", "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." }, @@ -372,7 +372,8 @@ "data/fabricator-recipe-types.ts" ], "data/fabricator-recipe-types.ts": [ - "data/equipment/types.ts" + "data/equipment/types.ts", + "types/equipment.ts" ], "data/fabricator-recipes.ts": [ "data/fabricator-material-recipes.ts", @@ -482,6 +483,15 @@ "utils/room-utils.ts", "utils/safe-persist.ts" ], + "stores/crafting-equipment-tick.ts": [ + "constants.ts", + "crafting-equipment.ts", + "data/crafting-recipes.ts", + "data/fabricator-recipes.ts", + "stores/combatStore.ts", + "stores/craftingStore.types.ts", + "types/equipment.ts" + ], "stores/crafting-initial-state.ts": [ "crafting-utils.ts", "types.ts" @@ -496,6 +506,7 @@ "crafting-fabricator.ts", "crafting-utils.ts", "stores/combatStore.ts", + "stores/crafting-equipment-tick.ts", "stores/crafting-initial-state.ts", "stores/craftingStore.types.ts", "stores/manaStore.ts", diff --git a/src/app/components/GrimoireTab.tsx b/src/app/components/GrimoireTab.tsx index 2565439..b6f009c 100644 --- a/src/app/components/GrimoireTab.tsx +++ b/src/app/components/GrimoireTab.tsx @@ -9,7 +9,6 @@ import type { SpellDef } from '@/lib/game/types'; export function GrimoireTab() { const [grimoireSpells, setGrimoireSpells] = useState<[string, SpellDef][]>([]); - const [loaded, _setLoaded] = useState(false); useEffect(() => { if (typeof window !== 'undefined' && SPELLS_DEF) { @@ -20,10 +19,6 @@ export function GrimoireTab() { } }, []); - if (!loaded) { - return
Loading grimoire...
; - } - if (grimoireSpells.length === 0) { return (
diff --git a/src/components/game/LootInventory/icons.ts b/src/components/game/LootInventory/icons.ts index 9ed7235..a4ee0c2 100644 --- a/src/components/game/LootInventory/icons.ts +++ b/src/components/game/LootInventory/icons.ts @@ -3,7 +3,6 @@ import { Sparkles, Package, Sword, - Shield, Shirt, Crown, Wrench @@ -11,7 +10,6 @@ import { export const CATEGORY_ICONS: Record = { caster: Sword, - shield: Shield, catalyst: Sparkles, head: Crown, body: Shirt, diff --git a/src/components/game/tabs/CraftingTab/FabricatorSubTab.tsx b/src/components/game/tabs/CraftingTab/FabricatorSubTab.tsx index 757cf04..b728fd6 100644 --- a/src/components/game/tabs/CraftingTab/FabricatorSubTab.tsx +++ b/src/components/game/tabs/CraftingTab/FabricatorSubTab.tsx @@ -24,7 +24,7 @@ const BRANCH_RECIPE_IDS = new Set([ 'oakStaff', 'arcanistStaff', 'battlestaff', 'arcanistCirclet', 'arcanistRobe', 'voidCatalyst', 'arcanistPendant', 'crystalBlade', 'arcanistBlade', 'voidBlade', 'battleHelm', 'battleRobe', - 'battleBoots', 'combatGauntlets', 'runicShield', 'manaShield', + 'battleBoots', 'combatGauntlets', ]); function isWizardBranch(recipe: FabricatorRecipe): boolean { @@ -34,8 +34,7 @@ function isWizardBranch(recipe: FabricatorRecipe): boolean { function isPhysicalBranch(recipe: FabricatorRecipe): boolean { return ['crystalBlade', 'arcanistBlade', 'voidBlade', 'battleHelm', - 'battleRobe', 'battleBoots', 'combatGauntlets', 'runicShield', - 'manaShield'].includes(recipe.id); + 'battleRobe', 'battleBoots', 'combatGauntlets'].includes(recipe.id); } function RecipeCard({ diff --git a/src/components/game/tabs/DisciplinesTab.tsx b/src/components/game/tabs/DisciplinesTab.tsx index fd69238..d43c1bf 100644 --- a/src/components/game/tabs/DisciplinesTab.tsx +++ b/src/components/game/tabs/DisciplinesTab.tsx @@ -257,7 +257,7 @@ export const DisciplinesTab: React.FC = () => {
{activeTab?.items.map((disc) => { const discState = disciplines[disc.id] ?? { xp: 0, paused: true }; - const prereqCheck = checkDisciplinePrerequisites(disc, disciplines, ALL_DISCIPLINES); + const prereqCheck = checkDisciplinePrerequisites(disc, disciplines, ALL_DISCIPLINES, elements); return (
+
+ Meditation Cap: + {fmtDec(stats.meditationCap, 1)}x +
Incursion Strength: {Math.round(incursionStrength * 100)}% diff --git a/src/lib/game/__tests__/discipline-prerequisites.test.ts b/src/lib/game/__tests__/discipline-prerequisites.test.ts index 5f1fbcc..86599f1 100644 --- a/src/lib/game/__tests__/discipline-prerequisites.test.ts +++ b/src/lib/game/__tests__/discipline-prerequisites.test.ts @@ -136,4 +136,54 @@ describe('checkDisciplinePrerequisites', () => { expect(result.canProceed).toBe(true); expect(result.missingPrereqs).toEqual([]); }); + + it('should return canProceed true when mana type requirement is unlocked', () => { + const disciplines: Record = {}; + const prereqDisc: DisciplineDefinition = { + ...rawMastery, + id: 'regen-transference', + requires: ['transference'], + }; + const elements = { transference: { unlocked: true } }; + const result = checkDisciplinePrerequisites(prereqDisc, disciplines, [rawMastery], elements); + expect(result.canProceed).toBe(true); + expect(result.missingPrereqs).toEqual([]); + }); + + it('should return canProceed false when mana type requirement is not unlocked', () => { + const disciplines: Record = {}; + const prereqDisc: DisciplineDefinition = { + ...rawMastery, + id: 'regen-transference', + requires: ['transference'], + }; + const elements = { transference: { unlocked: false } }; + const result = checkDisciplinePrerequisites(prereqDisc, disciplines, [rawMastery], elements); + expect(result.canProceed).toBe(false); + expect(result.missingPrereqs).toContain('transference mana'); + }); + + it('should return canProceed false when mana type is missing from elements', () => { + const disciplines: Record = {}; + const prereqDisc: DisciplineDefinition = { + ...rawMastery, + id: 'regen-fire', + requires: ['fire'], + }; + const result = checkDisciplinePrerequisites(prereqDisc, disciplines, [rawMastery], {}); + expect(result.canProceed).toBe(false); + expect(result.missingPrereqs).toContain('fire mana'); + }); + + it('should not require elements param for backwards compatibility', () => { + const disciplines: Record = {}; + const prereqDisc: DisciplineDefinition = { + ...rawMastery, + id: 'unknown-req', + requires: ['unknown-thing'], + }; + const result = checkDisciplinePrerequisites(prereqDisc, disciplines, [rawMastery]); + expect(result.canProceed).toBe(false); + expect(result.missingPrereqs).toContain('unknown-thing'); + }); }); diff --git a/src/lib/game/data/crafting-recipes.ts b/src/lib/game/data/crafting-recipes.ts index 4bcd312..3c18631 100644 --- a/src/lib/game/data/crafting-recipes.ts +++ b/src/lib/game/data/crafting-recipes.ts @@ -120,23 +120,7 @@ export const CRAFTING_RECIPES: Record = { unlocked: false, }, - shieldBlueprint: { - id: 'shieldBlueprint', - equipmentTypeId: 'runicShield', - name: 'Runic Shield', - description: 'A shield engraved with protective runes.', - rarity: 'rare', - materials: { - manaCrystalDust: 10, - arcaneShard: 6, - elementalCore: 2, - }, - manaCost: 450, - craftTime: 5, - minFloor: 28, - unlocked: false, - }, - + hatBlueprint: { id: 'hatBlueprint', equipmentTypeId: 'wizardHat', diff --git a/src/lib/game/data/disciplines/base.ts b/src/lib/game/data/disciplines/base.ts index 653059d..d7be132 100644 --- a/src/lib/game/data/disciplines/base.ts +++ b/src/lib/game/data/disciplines/base.ts @@ -61,10 +61,41 @@ export const baseDisciplines: DisciplineDefinition[] = [ type: 'capped', threshold: 100, value: 100, - description: 'Every 100 XP: +0.5 max meditation multiplier (7 tiers, up to +3.5)', + description: 'Every 100 XP: +0.5 max meditation cap (7 tiers, up to +3.5). Raises Void Meditation ceiling.', bonus: { stat: 'meditationCapBonus', amount: 0.5 }, maxTier: 7, }, ], }, + { + id: 'meditative-mastery', + name: 'Meditative Mastery', + attunement: DisciplinesAttunementType.BASE, + manaType: 'raw', + baseCost: 5, + description: + 'Deepen your meditation practice to accelerate discipline XP gain. The more you master yourself, the faster all disciplines grow.', + statBonus: { stat: 'disciplineXpBonus', baseValue: 0.5, label: 'Discipline XP Bonus/tick' }, + difficultyFactor: 120, + scalingFactor: 60, + drainBase: 1, + perks: [ + { + id: 'meditative-mastery-1', + type: 'once', + threshold: 100, + value: 0, + description: '+0.5 Discipline XP per tick', + bonus: { stat: 'disciplineXpBonus', amount: 0.5 }, + }, + { + id: 'meditative-mastery-2', + type: 'infinite', + threshold: 200, + value: 100, + description: 'Every 100 XP: +0.25 Discipline XP per tick', + bonus: { stat: 'disciplineXpBonus', amount: 0.25 }, + }, + ], + }, ]; \ No newline at end of file diff --git a/src/lib/game/data/disciplines/enchanter-utility.ts b/src/lib/game/data/disciplines/enchanter-utility.ts index 35b5852..b367d56 100644 --- a/src/lib/game/data/disciplines/enchanter-utility.ts +++ b/src/lib/game/data/disciplines/enchanter-utility.ts @@ -50,7 +50,7 @@ export const enchanterUtilityDisciplines: DisciplineDefinition[] = [ manaType: 'transference', baseCost: 15, description: 'Increase your max mana, unlocking mana enchantments for equipment.', - statBonus: { stat: 'maxMana', baseValue: 10, label: 'Max Mana' }, + statBonus: { stat: 'maxManaBonus', baseValue: 10, label: 'Max Mana' }, difficultyFactor: 150, scalingFactor: 100, drainBase: 3, diff --git a/src/lib/game/data/disciplines/enchanter.ts b/src/lib/game/data/disciplines/enchanter.ts index f150436..70d377c 100644 --- a/src/lib/game/data/disciplines/enchanter.ts +++ b/src/lib/game/data/disciplines/enchanter.ts @@ -21,15 +21,18 @@ export const enchanterDisciplines: DisciplineDefinition[] = [ id: 'enchant-1', type: 'infinite', threshold: 150, - value: 5, - description: '+5 Enchantment Power (stacks with skill tiers)', + value: 50, + description: '+5 Enchantment Power per tier', + bonus: { stat: 'enchantPower', amount: 5 }, }, { id: 'enchant-2', type: 'capped', threshold: 300, - value: 20, - description: 'Double enchantment duration at 300 XP', + value: 200, + maxTier: 3, + description: '+10 Enchantment Power per tier (max 3)', + bonus: { stat: 'enchantPower', amount: 10 }, }, ], }, @@ -50,7 +53,8 @@ export const enchanterDisciplines: DisciplineDefinition[] = [ type: 'once', threshold: 250, value: 0, - description: 'Unlock lightning mana boosting', + description: '+15 Lightning Mana Capacity', + bonus: { stat: 'elementCap_lightning', amount: 15 }, }, ], }, diff --git a/src/lib/game/data/disciplines/fabricator.ts b/src/lib/game/data/disciplines/fabricator.ts index dd3fd60..c819007 100644 --- a/src/lib/game/data/disciplines/fabricator.ts +++ b/src/lib/game/data/disciplines/fabricator.ts @@ -28,8 +28,10 @@ export const fabricatorDisciplines: DisciplineDefinition[] = [ id: 'golem-2', type: 'capped', threshold: 500, - value: 5, - description: 'Double golem capacity at 500 XP', + value: 250, + maxTier: 2, + description: '+1 Golem Capacity per tier (max 2)', + bonus: { stat: 'golemCapacity', amount: 1 }, }, ], }, @@ -50,7 +52,8 @@ export const fabricatorDisciplines: DisciplineDefinition[] = [ type: 'once', threshold: 300, value: 0, - description: 'Unlock reduced crafting costs', + description: '+10% Crafting Cost Reduction', + bonus: { stat: 'craftingCostReduction', amount: 10 }, }, ], }, diff --git a/src/lib/game/data/equipment/equipment-types-data.ts b/src/lib/game/data/equipment/equipment-types-data.ts index fce06d9..0ce00bb 100644 --- a/src/lib/game/data/equipment/equipment-types-data.ts +++ b/src/lib/game/data/equipment/equipment-types-data.ts @@ -10,7 +10,6 @@ import { CATALYST_EQUIPMENT, SPELL_FOCUS_EQUIPMENT } from './catalysts'; import { FEET_EQUIPMENT } from './feet'; import { HANDS_EQUIPMENT } from './hands'; import { HEAD_EQUIPMENT } from './head'; -import { SHIELD_EQUIPMENT } from './shields'; import { SWORD_EQUIPMENT } from './swords'; export const EQUIPMENT_TYPES = { @@ -22,6 +21,5 @@ export const EQUIPMENT_TYPES = { ...FEET_EQUIPMENT, ...HANDS_EQUIPMENT, ...HEAD_EQUIPMENT, - ...SHIELD_EQUIPMENT, ...SWORD_EQUIPMENT, }; diff --git a/src/lib/game/data/equipment/index.ts b/src/lib/game/data/equipment/index.ts index 16239ab..4f6a3f5 100644 --- a/src/lib/game/data/equipment/index.ts +++ b/src/lib/game/data/equipment/index.ts @@ -35,5 +35,4 @@ export { CATALYST_EQUIPMENT, SPELL_FOCUS_EQUIPMENT } from './catalysts'; export { FEET_EQUIPMENT } from './feet'; export { HANDS_EQUIPMENT } from './hands'; export { HEAD_EQUIPMENT } from './head'; -export { SHIELD_EQUIPMENT } from './shields'; export { SWORD_EQUIPMENT } from './swords'; diff --git a/src/lib/game/data/equipment/shields.ts b/src/lib/game/data/equipment/shields.ts index 06bc1f0..1dacdc2 100644 --- a/src/lib/game/data/equipment/shields.ts +++ b/src/lib/game/data/equipment/shields.ts @@ -1,39 +1,7 @@ // ─── Shield Equipment Types ─────────────────────────────────────────── +// Shields have been removed from the game. The offHand slot remains for +// non-shield items (e.g. catalysts, spell focuses). import type { EquipmentType } from './types'; -export const SHIELD_EQUIPMENT: Record = { - // ─── Off Hand - Shields ─────────────────────────────────────────── - basicShield: { - id: 'basicShield', - name: 'Basic Shield', - category: 'shield', - slot: 'offHand', - baseCapacity: 40, - description: 'A simple wooden shield. Provides basic protection.', - }, - reinforcedShield: { - id: 'reinforcedShield', - name: 'Reinforced Shield', - category: 'shield', - slot: 'offHand', - baseCapacity: 55, - description: 'A metal-reinforced shield with enhanced durability and capacity.', - }, - runicShield: { - id: 'runicShield', - name: 'Runic Shield', - category: 'shield', - slot: 'offHand', - baseCapacity: 70, - description: 'A shield engraved with protective runes. Excellent for defensive enchantments.', - }, - manaShield: { - id: 'manaShield', - name: 'Mana Shield', - category: 'shield', - slot: 'offHand', - baseCapacity: 60, - description: 'A crystalline shield that can store and reflect mana.', - }, -}; +export const SHIELD_EQUIPMENT: Record = {}; diff --git a/src/lib/game/data/equipment/types.ts b/src/lib/game/data/equipment/types.ts index ae20d69..2793ab2 100644 --- a/src/lib/game/data/equipment/types.ts +++ b/src/lib/game/data/equipment/types.ts @@ -2,7 +2,7 @@ import type { EquipmentSlot } from '../../types/equipmentSlot'; export type { EquipmentSlot }; -export type EquipmentCategory = 'caster' | 'shield' | 'catalyst' | 'sword' | 'head' | 'body' | 'hands' | 'feet' | 'accessory'; +export type EquipmentCategory = 'caster' | 'catalyst' | 'sword' | 'head' | 'body' | 'hands' | 'feet' | 'accessory'; // All equipment slots in order export const EQUIPMENT_SLOTS: EquipmentSlot[] = ['mainHand', 'offHand', 'head', 'body', 'hands', 'feet', 'accessory1', 'accessory2']; diff --git a/src/lib/game/data/equipment/utils.ts b/src/lib/game/data/equipment/utils.ts index 433dac3..3c07b90 100644 --- a/src/lib/game/data/equipment/utils.ts +++ b/src/lib/game/data/equipment/utils.ts @@ -27,8 +27,7 @@ export function getValidSlotsForCategory(category: EquipmentCategory): Equipment case 'catalyst': case 'sword': return ['mainHand']; - case 'shield': - return ['offHand']; + case 'head': return ['head']; case 'body': diff --git a/src/lib/game/data/fabricator-physical-recipes.ts b/src/lib/game/data/fabricator-physical-recipes.ts index 4896e15..d5ecc73 100644 --- a/src/lib/game/data/fabricator-physical-recipes.ts +++ b/src/lib/game/data/fabricator-physical-recipes.ts @@ -123,38 +123,4 @@ export const PHYSICAL_BRANCH_RECIPES: FabricatorRecipe[] = [ { effectId: 'attack_speed_10', stacks: 1, actualCost: 22 }, ], }, - { - id: 'runicShield', - name: 'Runic Shield', - description: 'A shield engraved with protective runes. Excellent for defensive enchantments.', - manaType: 'earth', - equipmentTypeId: 'runicShield', - slot: 'offHand', - materials: { manaCrystalDust: 8, earthShard: 4, elementalCore: 2 }, - manaCost: 450, - craftTime: 5, - rarity: 'rare', - gearTrait: '+20% Defensive Enchantment Power, +25 Earth Mana Capacity', - bonusEnchantments: [ - { effectId: 'earth_cap_10', stacks: 1, actualCost: 30 }, - { effectId: 'mana_cap_50', stacks: 1, actualCost: 20 }, - ], - }, - { - id: 'manaShield', - name: 'Mana Shield', - description: 'A crystalline shield that can store and reflect mana. Deadly in the right hands.', - manaType: 'crystal', - equipmentTypeId: 'manaShield', - slot: 'offHand', - materials: { manaCrystalDust: 10, crystalShard: 5, elementalCore: 2 }, - manaCost: 550, - craftTime: 6, - rarity: 'epic', - gearTrait: '+15% Spell Damage, +15% Defensive Enchantment Power', - bonusEnchantments: [ - { effectId: 'mana_cap_50', stacks: 1, actualCost: 20 }, - { effectId: 'damage_5', stacks: 1, actualCost: 15 }, - ], - }, ]; diff --git a/src/lib/game/effects.ts b/src/lib/game/effects.ts index b7db66c..2275f00 100644 --- a/src/lib/game/effects.ts +++ b/src/lib/game/effects.ts @@ -128,7 +128,7 @@ export function computeAllEffects( perElementCapBonus, perElementRegenBonus, maxManaMultiplier: upgradeEffects.maxManaMultiplier * (equipmentEffects.multipliers.maxMana || 1), - regenMultiplier: upgradeEffects.regenMultiplier * (equipmentEffects.multipliers.regen || 1), + regenMultiplier: upgradeEffects.regenMultiplier * (equipmentEffects.multipliers.regen || 1) * (1 + (disciplineEffects.multipliers.regenMultiplier || 0)), clickManaMultiplier: upgradeEffects.clickManaMultiplier * (equipmentEffects.multipliers.clickMana || 1), baseDamageMultiplier: upgradeEffects.baseDamageMultiplier * (equipmentEffects.multipliers.baseDamage || 1), attackSpeedMultiplier: upgradeEffects.attackSpeedMultiplier * (equipmentEffects.multipliers.attackSpeed || 1), diff --git a/src/lib/game/effects/discipline-effects.ts b/src/lib/game/effects/discipline-effects.ts index 3c44252..009205a 100644 --- a/src/lib/game/effects/discipline-effects.ts +++ b/src/lib/game/effects/discipline-effects.ts @@ -22,9 +22,14 @@ const KNOWN_BONUS_STATS = new Set([ 'clickManaBonus', 'baseDamageBonus', 'elementCapBonus', + 'elementCap_lightning', 'meditationCapBonus', 'pactAffinityBonus', 'guardianBoonMultiplier', + 'enchantPower', + 'golemCapacity', + 'craftingCostReduction', + 'disciplineXpBonus', ]); export interface DisciplineEffectsResult { diff --git a/src/lib/game/hooks/useGameDerived.ts b/src/lib/game/hooks/useGameDerived.ts index 3982f3c..365b9ee 100644 --- a/src/lib/game/hooks/useGameDerived.ts +++ b/src/lib/game/hooks/useGameDerived.ts @@ -54,6 +54,7 @@ export function useManaStats() { ); const disciplineEffects = computeDisciplineEffects(); + const meditationCap = 5.0 + disciplineEffects.meditationCapBonus; const meditationMultiplier = useMemo( () => getMeditationBonus(meditateTicks, {}, upgradeEffects.meditationEfficiency, disciplineEffects.meditationCapBonus), [meditateTicks, upgradeEffects.meditationEfficiency, disciplineEffects.meditationCapBonus] @@ -86,6 +87,7 @@ export function useManaStats() { baseRegen, clickMana, meditationMultiplier, + meditationCap, incursionStrength, effectiveRegenWithSpecials, manaCascadeBonus, diff --git a/src/lib/game/stores/discipline-slice.ts b/src/lib/game/stores/discipline-slice.ts index b4c8318..83c9dcb 100644 --- a/src/lib/game/stores/discipline-slice.ts +++ b/src/lib/game/stores/discipline-slice.ts @@ -10,6 +10,7 @@ import { checkDisciplinePrerequisites, getUnlockedPerks } from '../utils/discipline-math'; +import { computeDisciplineEffects } from '../effects/discipline-effects'; import { baseDisciplines } from '../data/disciplines/base'; import { elementalAttunementDisciplines } from '../data/disciplines/elemental'; import { elementalRegenDisciplines } from '../data/disciplines/elemental-regen'; @@ -99,8 +100,8 @@ export const useDisciplineStore = create()( if (signedPacts.length === 0) return s; } - // Check discipline prerequisites (requires field → discipline XP) - const prereqCheck = checkDisciplinePrerequisites(def, s.disciplines, ALL_DISCIPLINES); + // Check discipline prerequisites (requires field → discipline XP or mana type unlock) + const prereqCheck = checkDisciplinePrerequisites(def, s.disciplines, ALL_DISCIPLINES, gameState?.elements); if (!prereqCheck.canProceed) return s; // For conversion disciplines: gate on having all source mana types unlocked @@ -183,8 +184,10 @@ export const useDisciplineStore = create()( } const oldXP = disc.xp; - newDisciplines[id] = { ...disc, xp: disc.xp + 1 }; - newXP += 1; + const xpBonus = computeDisciplineEffects().bonuses.disciplineXpBonus || 0; + const xpGain = 1 + xpBonus; + newDisciplines[id] = { ...disc, xp: disc.xp + xpGain }; + newXP += xpGain; // Check for newly unlocked perks that unlock effects if (def.perks.length > 0) { diff --git a/src/lib/game/utils/discipline-math.ts b/src/lib/game/utils/discipline-math.ts index ec38d97..92bead7 100644 --- a/src/lib/game/utils/discipline-math.ts +++ b/src/lib/game/utils/discipline-math.ts @@ -116,6 +116,7 @@ export function checkDisciplinePrerequisites( discipline: DisciplineDefinition, allDisciplines: Record, allDefinitions: DisciplineDefinition[], + elements?: Record, ): { canProceed: boolean; missingPrereqs: string[] } { if (!discipline.requires || discipline.requires.length === 0) { return { canProceed: true, missingPrereqs: [] }; @@ -138,10 +139,13 @@ export function checkDisciplinePrerequisites( missingPrereqs.push(reqDef?.name ?? reqId); } } else { - // Treat as mana type requirement — display as " mana" + // Treat as mana type requirement — verify against player's unlocked elements const typeName = MANA_TYPE_NAMES[reqId]; if (typeName) { - missingPrereqs.push(`${typeName} mana`); + const elementState = elements?.[reqId]; + if (!elementState || !elementState.unlocked) { + missingPrereqs.push(`${typeName} mana`); + } } else { missingPrereqs.push(reqId); }