chore: cleanup — remove dead weight (prisma, db, examples, python scripts, workflow docs, redundant tsconfigs)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 34s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 34s
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json({ message: "Hello, world!" });
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined
|
||||
}
|
||||
|
||||
export const db =
|
||||
globalForPrisma.prisma ??
|
||||
new PrismaClient({
|
||||
log: ['query'],
|
||||
})
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db
|
||||
@@ -5,8 +5,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF } from '../constants';
|
||||
import { calcInsight } from '../computed-stats';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
import { calcInsight } from '@/lib/game/computed-stats';
|
||||
import type { GameState } from '../types';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF, SKILL_EVOLUTION_PATHS, getTierMultiplier, getNextTierSkill, generateTierSkillDef } from '../constants';
|
||||
import { SKILLS_DEF, SKILL_EVOLUTION_PATHS, getTierMultiplier, getNextTierSkill, generateTierSkillDef } from '@/lib/game/constants';
|
||||
import { SKILL_EVOLUTION_PATHS as EVOLUTION_PATHS, getUpgradesForSkillAtMilestone, getNextTierSkill as NextTier, getTierMultiplier as TierMultiplier, generateTierSkillDef as GenerateTier } from '../skill-evolution';
|
||||
|
||||
describe('Integration Tests', () => {
|
||||
|
||||
@@ -11,8 +11,8 @@ import {
|
||||
computeElementMax,
|
||||
computeRegen,
|
||||
computeClickMana,
|
||||
} from '../computed-stats';
|
||||
import { SKILLS_DEF } from '../constants';
|
||||
} from '@/lib/game/computed-stats';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
import type { GameState } from '../types';
|
||||
|
||||
// ─── Test Helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { PRESTIGE_DEF } from '../constants';
|
||||
import { computeMaxMana, computeElementMax } from '../computed-stats';
|
||||
import { PRESTIGE_DEF } from '@/lib/game/constants';
|
||||
import { computeMaxMana, computeElementMax } from '@/lib/game/computed-stats';
|
||||
import type { GameState } from '../types';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF } from '../constants';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Skill Prerequisites', () => {
|
||||
it('Mana Overflow should require Mana Well 3', () => {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF } from '../constants';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Enchanter Skills', () => {
|
||||
describe('Enchanting (Unlock enchantment design)', () => {
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
getStudySpeedMultiplier,
|
||||
getStudyCostMultiplier,
|
||||
getMeditationBonus,
|
||||
} from '../computed-stats';
|
||||
import { SKILLS_DEF } from '../constants';
|
||||
} from '@/lib/game/computed-stats';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Study Skills', () => {
|
||||
describe('Quick Learner (+10% study speed)', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF } from '../constants';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Study Times', () => {
|
||||
it('all skills should have reasonable study times', () => {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF } from './constants';
|
||||
import { calcInsight } from './store';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
import { calcInsight } from '@/lib/game/store';
|
||||
import type { GameState } from './types';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
|
||||
@@ -10,8 +10,8 @@ import {
|
||||
computeElementMax,
|
||||
computeRegen,
|
||||
computeClickMana,
|
||||
} from './store';
|
||||
import { SKILLS_DEF } from './constants';
|
||||
} from '@/lib/game/store';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
import type { GameState } from './types';
|
||||
|
||||
// ─── Test Helpers ───────────────────────────────────────────────────────────
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF, PRESTIGE_DEF } from './constants';
|
||||
import { computeMaxMana, computeElementMax } from './store';
|
||||
import { SKILLS_DEF, PRESTIGE_DEF } from '@/lib/game/constants';
|
||||
import { computeMaxMana, computeElementMax } from '@/lib/game/store';
|
||||
import type { GameState } from './types';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
getStudySpeedMultiplier,
|
||||
getStudyCostMultiplier,
|
||||
getMeditationBonus,
|
||||
} from './store';
|
||||
import { SKILLS_DEF } from './constants';
|
||||
} from '@/lib/game/store';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Study Skills', () => {
|
||||
describe('Quick Learner (+10% study speed)', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useCombatStore } from './stores';
|
||||
import { useCombatStore } from '@/lib/game/stores';
|
||||
|
||||
// ─── Test Fixtures ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
usePrestigeStore,
|
||||
useCombatStore,
|
||||
computeMaxMana,
|
||||
} from './stores';
|
||||
} from '@/lib/game/stores';
|
||||
|
||||
// ─── Test Fixtures ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
usePrestigeStore,
|
||||
useCombatStore,
|
||||
useUIStore,
|
||||
} from './stores';
|
||||
import { ELEMENTS } from './constants';
|
||||
} from '@/lib/game/stores';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
// ─── Test Fixtures ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import {
|
||||
usePrestigeStore,
|
||||
useManaStore,
|
||||
} from './stores';
|
||||
} from '@/lib/game/stores';
|
||||
|
||||
// ─── Test Fixtures ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
useSkillStore,
|
||||
usePrestigeStore,
|
||||
getStudySpeedMultiplier,
|
||||
} from './stores';
|
||||
import { ELEMENTS } from './constants';
|
||||
} from '@/lib/game/stores';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
// ─── Test Fixtures ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useUIStore } from './stores';
|
||||
import { useUIStore } from '@/lib/game/stores';
|
||||
|
||||
// ─── Test Fixtures ───────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -0,0 +1,492 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
computeStats,
|
||||
BASE_STATS,
|
||||
SKILLS_V2,
|
||||
getBaseSkillId,
|
||||
hasPrerequisites,
|
||||
} from '../../constants/skills-v2';
|
||||
import type { ComputedStats } from '../../constants/skills-v2-types';
|
||||
|
||||
// Helper to create a minimal prestige state
|
||||
const emptyPrestige = {};
|
||||
|
||||
describe('computeStats()', () => {
|
||||
describe('base stats with no skills', () => {
|
||||
it('should return base stats when no skills are provided', () => {
|
||||
const result = computeStats({}, emptyPrestige);
|
||||
expect(result.maxMana).toBe(100);
|
||||
expect(result.manaRegen).toBe(2);
|
||||
expect(result.clickMana).toBe(1);
|
||||
expect(result.baseDamage).toBe(5);
|
||||
expect(result.elementCap).toBe(10);
|
||||
});
|
||||
|
||||
it('should have all base values correct', () => {
|
||||
const result = computeStats({}, emptyPrestige);
|
||||
expect(result).toMatchObject({
|
||||
maxMana: 100,
|
||||
manaRegen: 2,
|
||||
clickMana: 1,
|
||||
elementCap: 10,
|
||||
studySpeed: 1,
|
||||
studyCostMult: 1,
|
||||
meditationEfficiency: 1,
|
||||
enchantCapacity: 100,
|
||||
enchantSpeed: 1,
|
||||
enchantPower: 1,
|
||||
disenchantRecovery: 1,
|
||||
baseDamage: 5,
|
||||
damageMultiplier: 1,
|
||||
attackSpeed: 1,
|
||||
critChance: 0,
|
||||
critMultiplier: 1.5,
|
||||
armorPierce: 0,
|
||||
insightGain: 1,
|
||||
golemDamage: 1,
|
||||
golemDuration: 1,
|
||||
pactMultiplier: 1,
|
||||
spellDamage: 1,
|
||||
guardianDamage: 1,
|
||||
craftSpeed: 1,
|
||||
repairSpeed: 1,
|
||||
elementalDamage: 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mana Well skill', () => {
|
||||
it('should add 100 max mana per level', () => {
|
||||
const result = computeStats({ manaWell: 5 }, emptyPrestige);
|
||||
expect(result.maxMana).toBe(100 + 5 * 100);
|
||||
});
|
||||
|
||||
it('should be 100 at level 0', () => {
|
||||
const result = computeStats({ manaWell: 0 }, emptyPrestige);
|
||||
expect(result.maxMana).toBe(100);
|
||||
});
|
||||
|
||||
it('should be 1100 at max level 10', () => {
|
||||
const result = computeStats({ manaWell: 10 }, emptyPrestige);
|
||||
expect(result.maxMana).toBe(1100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mana Flow skill', () => {
|
||||
it('should add 1 mana regen per level', () => {
|
||||
const result = computeStats({ manaFlow: 5 }, emptyPrestige);
|
||||
expect(result.manaRegen).toBe(2 + 5);
|
||||
});
|
||||
|
||||
it('should be 2 at level 0', () => {
|
||||
const result = computeStats({ manaFlow: 0 }, emptyPrestige);
|
||||
expect(result.manaRegen).toBe(2);
|
||||
});
|
||||
|
||||
it('should be 12 at max level 10', () => {
|
||||
const result = computeStats({ manaFlow: 10 }, emptyPrestige);
|
||||
expect(result.manaRegen).toBe(12);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mana Tap skill', () => {
|
||||
it('should add 1 click mana at level 1', () => {
|
||||
const result = computeStats({ manaTap: 1 }, emptyPrestige);
|
||||
expect(result.clickMana).toBe(2);
|
||||
});
|
||||
|
||||
it('should be 1 at level 0', () => {
|
||||
const result = computeStats({ manaTap: 0 }, emptyPrestige);
|
||||
expect(result.clickMana).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mana Surge skill', () => {
|
||||
it('should add 3 click mana per level', () => {
|
||||
const result = computeStats({ manaSurge: 1 }, emptyPrestige);
|
||||
expect(result.clickMana).toBe(1 + 3);
|
||||
});
|
||||
|
||||
it('should stack with manaTap', () => {
|
||||
const result = computeStats({ manaTap: 1, manaSurge: 1 }, emptyPrestige);
|
||||
expect(result.clickMana).toBe(1 + 1 + 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mana Spring skill', () => {
|
||||
it('should add 2 mana regen at level 1', () => {
|
||||
const result = computeStats({ manaSpring: 1 }, emptyPrestige);
|
||||
expect(result.manaRegen).toBe(2 + 2);
|
||||
});
|
||||
|
||||
it('should stack with manaFlow', () => {
|
||||
const result = computeStats({ manaFlow: 5, manaSpring: 1 }, emptyPrestige);
|
||||
expect(result.manaRegen).toBe(2 + 5 + 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Mana Overflow skill', () => {
|
||||
it('should multiply click mana by compounding 1.25 per level', () => {
|
||||
// multiply effects compound per level: 1 * 1.25^2 = 1.5625
|
||||
const result = computeStats({ manaOverflow: 2 }, emptyPrestige);
|
||||
expect(result.clickMana).toBeCloseTo(1.5625, 2);
|
||||
});
|
||||
|
||||
it('should be 1 at level 0', () => {
|
||||
const result = computeStats({ manaOverflow: 0 }, emptyPrestige);
|
||||
expect(result.clickMana).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Quick Learner skill', () => {
|
||||
it('should multiply study speed by compounding 1.10 per level', () => {
|
||||
// 1.1^5 = 1.61051
|
||||
const result = computeStats({ quickLearner: 5 }, emptyPrestige);
|
||||
expect(result.studySpeed).toBeCloseTo(1.61051, 2);
|
||||
});
|
||||
|
||||
it('should be ~2.5937 at max level 10', () => {
|
||||
const result = computeStats({ quickLearner: 10 }, emptyPrestige);
|
||||
expect(result.studySpeed).toBeCloseTo(2.593742, 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Focused Mind skill', () => {
|
||||
it('should multiply study cost by compounding 0.95 per level', () => {
|
||||
// 0.95^2 = 0.9025
|
||||
const result = computeStats({ focusedMind: 2 }, emptyPrestige);
|
||||
expect(result.studyCostMult).toBeCloseTo(0.9025, 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Meditation Focus skill', () => {
|
||||
it('should multiply meditation efficiency at level 1', () => {
|
||||
// BASE (1) * (1 + 1.5) = 2.5
|
||||
const result = computeStats({ meditation: 1 }, emptyPrestige);
|
||||
expect(result.meditationEfficiency).toBe(2.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Deep Trance skill', () => {
|
||||
it('should multiply meditation efficiency further', () => {
|
||||
// meditation: 1 * (1+1.5) = 2.5, deepTrance: 2.5 * (1+1.8) = 7.0
|
||||
const result = computeStats({ meditation: 1, deepTrance: 1 }, emptyPrestige);
|
||||
expect(result.meditationEfficiency).toBe(7.0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Void Meditation skill', () => {
|
||||
it('should multiply meditation efficiency to max', () => {
|
||||
// 1 * 2.5 * 2.8 * 3.5 = 24.5
|
||||
const result = computeStats({ meditation: 1, deepTrance: 1, voidMeditation: 1 }, emptyPrestige);
|
||||
expect(result.meditationEfficiency).toBe(24.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Combat skills', () => {
|
||||
it('Arcane Fury should multiply damage', () => {
|
||||
const result = computeStats({ arcaneFury: 5 }, emptyPrestige);
|
||||
expect(result.damageMultiplier).toBeCloseTo(1.61051, 2);
|
||||
});
|
||||
|
||||
it('Combat Training should add base damage', () => {
|
||||
const result = computeStats({ combatTraining: 3 }, emptyPrestige);
|
||||
expect(result.baseDamage).toBe(5 + 3 * 5);
|
||||
});
|
||||
|
||||
it('Precision should add crit chance', () => {
|
||||
const result = computeStats({ precision: 4 }, emptyPrestige);
|
||||
expect(result.critChance).toBe(0.2);
|
||||
});
|
||||
|
||||
it('Precision should cap at 1.0', () => {
|
||||
const result = computeStats({ precision: 25 }, emptyPrestige);
|
||||
expect(result.critChance).toBe(1.0);
|
||||
});
|
||||
|
||||
it('Elemental Mastery should multiply elemental damage', () => {
|
||||
// 1 * 1.15^4 = ~1.749
|
||||
const result = computeStats({ elementalMastery: 4 }, emptyPrestige);
|
||||
expect(result.elementalDamage).toBeCloseTo(1.749, 2);
|
||||
});
|
||||
|
||||
it('Attack Speed should multiply attack speed (compounding)', () => {
|
||||
// 1 * 0.9^3 = 0.729
|
||||
const result = computeStats({ attackSpeed: 3 }, emptyPrestige);
|
||||
expect(result.attackSpeed).toBeCloseTo(0.729, 2);
|
||||
});
|
||||
|
||||
it('Armor Piercing should add armor pierce', () => {
|
||||
const result = computeStats({ armorPiercing: 4 }, emptyPrestige);
|
||||
expect(result.armorPierce).toBe(0.2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Enchanting skills', () => {
|
||||
it('Enchanting should affect enchantCapacity and enchantSpeed', () => {
|
||||
const result = computeStats({ enchanting: 5 }, emptyPrestige);
|
||||
expect(result.enchantCapacity).toBe(100 + 5 * 10); // add 10 per level
|
||||
expect(result.enchantSpeed).toBeCloseTo(0.9, 2); // multiply by 0.98^5
|
||||
});
|
||||
|
||||
it('Essence Refining should multiply enchantPower', () => {
|
||||
const result = computeStats({ enchanting: 4, essenceRefining: 1 }, emptyPrestige);
|
||||
expect(result.enchantPower).toBeCloseTo(1.1, 2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Golemancy skills', () => {
|
||||
it('Golem Mastery should multiply golemDamage', () => {
|
||||
const result = computeStats({ golemMastery: 5 }, emptyPrestige);
|
||||
expect(result.golemDamage).toBeCloseTo(1.61051, 2);
|
||||
});
|
||||
|
||||
it('Golem Longevity should add golemDuration', () => {
|
||||
const result = computeStats({ golemLongevity: 3 }, emptyPrestige);
|
||||
expect(result.golemDuration).toBe(1 + 3);
|
||||
});
|
||||
|
||||
it('Golem Efficiency should multiply attackSpeed', () => {
|
||||
const result = computeStats({ golemEfficiency: 2 }, emptyPrestige);
|
||||
expect(result.attackSpeed).toBeCloseTo(0.9025, 2); // 0.95^2
|
||||
});
|
||||
});
|
||||
|
||||
describe('Invocation / Pact skills', () => {
|
||||
it('Invocation should multiply spellDamage', () => {
|
||||
const result = computeStats({ invocation: 5 }, emptyPrestige);
|
||||
expect(result.spellDamage).toBeCloseTo(1.27628, 2); // 1.05^5
|
||||
});
|
||||
|
||||
it('Pact Mastery should multiply pactMultiplier', () => {
|
||||
const result = computeStats({ pactMastery: 5 }, emptyPrestige);
|
||||
expect(result.pactMultiplier).toBeCloseTo(1.61051, 2); // 1.1^5
|
||||
});
|
||||
|
||||
it('Guardian Lore should multiply guardianDamage', () => {
|
||||
const result = computeStats({ guardianLore: 3 }, emptyPrestige);
|
||||
expect(result.guardianDamage).toBeCloseTo(1.728, 2); // 1.2^3
|
||||
});
|
||||
});
|
||||
|
||||
describe('Element capacity skills', () => {
|
||||
it('Fire Mana Cap should increase fireCap', () => {
|
||||
const result = computeStats({ fireManaCap: 5 }, emptyPrestige);
|
||||
expect(result.fireCap).toBe(5 * 10);
|
||||
});
|
||||
|
||||
it('Multiple element caps should contribute to elementCap', () => {
|
||||
const result = computeStats({ fireManaCap: 3, waterManaCap: 2 }, emptyPrestige);
|
||||
// fireCap=30, waterCap=20 -> elementCap = 10 + 30 + 20 = 60
|
||||
expect(result.elementCap).toBe(60);
|
||||
});
|
||||
|
||||
it('All base element caps should contribute', () => {
|
||||
const result = computeStats({
|
||||
fireManaCap: 10, waterManaCap: 10, airManaCap: 10, earthManaCap: 10,
|
||||
}, emptyPrestige);
|
||||
// Each adds 10*10=100, total 400 + base 10 = 410
|
||||
expect(result.elementCap).toBe(410);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Hybrid skills', () => {
|
||||
it('Pact-Weaving should multiply enchantPower', () => {
|
||||
const result = computeStats({ pactWeaving: 3 }, emptyPrestige);
|
||||
expect(result.enchantPower).toBeCloseTo(1.331, 2); // 1.1^3
|
||||
});
|
||||
|
||||
it('Guardian Constructs should affect golemDamage and golemDuration', () => {
|
||||
const result = computeStats({ guardianConstructs: 2 }, emptyPrestige);
|
||||
expect(result.golemDamage).toBeCloseTo(1.3225, 2); // 1.15^2
|
||||
expect(result.golemDuration).toBe(1.5); // add 0.25*2=0.5, but cap floor is 1, so 1 + 0.5 = 1.5
|
||||
});
|
||||
|
||||
it('Enchanted Golemancy should affect enchantPower and golemDamage', () => {
|
||||
const result = computeStats({ enchantedGolemancy: 3 }, emptyPrestige);
|
||||
expect(result.enchantPower).toBeCloseTo(1.157625, 2); // 1.05^3
|
||||
expect(result.golemDamage).toBeCloseTo(1.331, 2); // 1.1^3
|
||||
});
|
||||
});
|
||||
|
||||
describe('Prestige upgrades', () => {
|
||||
it('manaWell prestige should increase maxMana', () => {
|
||||
const result = computeStats({}, { manaWell: 3 });
|
||||
expect(result.maxMana).toBe(100 + 3 * 500);
|
||||
});
|
||||
|
||||
it('manaFlow prestige should increase manaRegen', () => {
|
||||
const result = computeStats({}, { manaFlow: 2 });
|
||||
expect(result.manaRegen).toBe(2 + 2 * 0.5);
|
||||
});
|
||||
|
||||
it('elementalAttune prestige should increase elementCap', () => {
|
||||
const result = computeStats({}, { elementalAttune: 4 });
|
||||
expect(result.elementCap).toBe(10 + 4 * 25);
|
||||
});
|
||||
|
||||
it('pactBinding prestige should increase pactMultiplier', () => {
|
||||
const result = computeStats({}, { pactBinding: 2 });
|
||||
expect(result.pactMultiplier).toBe(1 + 2 * 0.1);
|
||||
});
|
||||
|
||||
it('insightAmp prestige should multiply insightGain', () => {
|
||||
const result = computeStats({}, { insightAmp: 2 });
|
||||
expect(result.insightGain).toBe(1 + 1 * 0.25 * 2);
|
||||
});
|
||||
|
||||
it('prestige should stack with skills', () => {
|
||||
const result = computeStats({ manaWell: 5 }, { manaWell: 3 });
|
||||
expect(result.maxMana).toBe(100 + 5 * 100 + 3 * 500);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Skill stacking', () => {
|
||||
it('should correctly stack multiple skills', () => {
|
||||
const result = computeStats({
|
||||
manaWell: 3,
|
||||
manaFlow: 2,
|
||||
manaTap: 1,
|
||||
precision: 4,
|
||||
}, emptyPrestige);
|
||||
|
||||
expect(result.maxMana).toBe(100 + 300);
|
||||
expect(result.manaRegen).toBe(2 + 2);
|
||||
expect(result.clickMana).toBe(1 + 1);
|
||||
expect(result.critChance).toBe(0.2);
|
||||
});
|
||||
|
||||
it('should handle all skills with effects at once without interference', () => {
|
||||
// Only test skills that have effects defined (skip research skills with empty effects)
|
||||
const allSkillsWithEffects: Record<string, number> = {};
|
||||
for (const [id, def] of Object.entries(SKILLS_V2)) {
|
||||
if (def.effects.length > 0) {
|
||||
allSkillsWithEffects[id] = 1;
|
||||
}
|
||||
}
|
||||
const result = computeStats(allSkillsWithEffects, emptyPrestige);
|
||||
// Should not throw and should produce reasonable values
|
||||
expect(result.maxMana).toBeGreaterThan(0);
|
||||
expect(result.manaRegen).toBeGreaterThan(0);
|
||||
expect(result.baseDamage).toBeGreaterThan(0);
|
||||
expect(result.elementCap).toBeGreaterThanOrEqual(10);
|
||||
});
|
||||
});
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should ignore negative levels (no effect applied)', () => {
|
||||
const result = computeStats({}, emptyPrestige);
|
||||
expect(result.maxMana).toBe(100);
|
||||
});
|
||||
|
||||
it('should ignore unknown skill IDs', () => {
|
||||
const result = computeStats({ unknownSkill: 5 } as any, emptyPrestige);
|
||||
expect(result.maxMana).toBe(100);
|
||||
});
|
||||
|
||||
it('should clamp critChance to 1.0', () => {
|
||||
const result = computeStats({ precision: 100 } as any, emptyPrestige);
|
||||
expect(result.critChance).toBe(1.0);
|
||||
});
|
||||
|
||||
it('should clamp armorPierce to 1.0', () => {
|
||||
const result = computeStats({ armorPiercing: 100 } as any, emptyPrestige);
|
||||
expect(result.armorPierce).toBe(1.0);
|
||||
});
|
||||
|
||||
it('should clamp attackSpeed minimum to 0.1', () => {
|
||||
const result = computeStats({ attackSpeed: 100 } as any, emptyPrestige);
|
||||
expect(result.attackSpeed).toBe(0.1);
|
||||
});
|
||||
|
||||
it('should clamp maxMana minimum to 1', () => {
|
||||
const result = computeStats({}, emptyPrestige);
|
||||
expect(result.maxMana).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('should clamp baseDamage minimum to 1', () => {
|
||||
const result = computeStats({}, emptyPrestige);
|
||||
expect(result.baseDamage).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBaseSkillId()', () => {
|
||||
it('should strip _tN suffix for tiered skills', () => {
|
||||
expect(getBaseSkillId('manaWell_t2')).toBe('manaWell');
|
||||
expect(getBaseSkillId('manaWell_t5')).toBe('manaWell');
|
||||
expect(getBaseSkillId('quickLearner_t3')).toBe('quickLearner');
|
||||
});
|
||||
|
||||
it('should return same ID for non-tiered skills', () => {
|
||||
expect(getBaseSkillId('manaWell')).toBe('manaWell');
|
||||
expect(getBaseSkillId('fireManaCap')).toBe('fireManaCap');
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasPrerequisites()', () => {
|
||||
it('should return true when no prerequisites', () => {
|
||||
expect(hasPrerequisites({}, undefined)).toBe(true);
|
||||
expect(hasPrerequisites({}, {})).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true when prerequisites are met', () => {
|
||||
expect(hasPrerequisites({ manaWell: 5 }, { manaWell: 3 })).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when prerequisites are not met', () => {
|
||||
expect(hasPrerequisites({ manaWell: 2 }, { manaWell: 3 })).toBe(false);
|
||||
expect(hasPrerequisites({}, { manaWell: 1 })).toBe(false);
|
||||
});
|
||||
|
||||
it('should check multiple prerequisites', () => {
|
||||
expect(hasPrerequisites({ a: 2, b: 3 }, { a: 1, b: 2 })).toBe(true);
|
||||
expect(hasPrerequisites({ a: 2, b: 1 }, { a: 1, b: 2 })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SKILLS_V2', () => {
|
||||
it('should have manaWell defined', () => {
|
||||
expect(SKILLS_V2.manaWell).toBeDefined();
|
||||
expect(SKILLS_V2.manaWell.id).toBe('manaWell');
|
||||
expect(SKILLS_V2.manaWell.maxLevel).toBe(10);
|
||||
expect(SKILLS_V2.manaWell.effects).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should have all core skills defined', () => {
|
||||
const coreSkills = ['manaWell', 'manaFlow', 'quickLearner', 'focusedMind', 'meditation'];
|
||||
for (const id of coreSkills) {
|
||||
expect(SKILLS_V2[id]).toBeDefined();
|
||||
expect(SKILLS_V2[id].effects.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('should have correct manaWell effect', () => {
|
||||
const effect = SKILLS_V2.manaWell.effects[0];
|
||||
expect(effect.stat).toBe('maxMana');
|
||||
expect(effect.mode).toBe('add');
|
||||
expect(effect.valuePerLevel).toBe(100);
|
||||
});
|
||||
|
||||
it('should have correct manaFlow effect', () => {
|
||||
const effect = SKILLS_V2.manaFlow.effects[0];
|
||||
expect(effect.stat).toBe('manaRegen');
|
||||
expect(effect.mode).toBe('add');
|
||||
expect(effect.valuePerLevel).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle prerequisite fields', () => {
|
||||
expect(SKILLS_V2.manaOverflow.prerequisites).toEqual({ manaWell: 3 });
|
||||
expect(SKILLS_V2.deepTrance.prerequisites).toEqual({ meditation: 1 });
|
||||
expect(SKILLS_V2.manaTap.prerequisites).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle attunementRequired fields', () => {
|
||||
expect(SKILLS_V2.enchanting.attunementRequired).toBe('enchanter');
|
||||
expect(SKILLS_V2.invocation.attunementRequired).toBe('invoker');
|
||||
expect(SKILLS_V2.golemMastery.attunementRequired).toBe('fabricator');
|
||||
expect(SKILLS_V2.manaWell.attunementRequired).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
console.log('✅ computeStats() and skill v2 tests defined.');
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calcDamage, getFloorMaxHP, getFloorElement } from '../index';
|
||||
import { calcDamage, getFloorMaxHP, getFloorElement } from '@/lib/game/stores/index';
|
||||
import type { GameState } from '../types';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
@@ -112,7 +112,7 @@ describe('Combat Calculations', () => {
|
||||
describe('getFloorMaxHP', () => {
|
||||
it('should return guardian HP for guardian floors', () => {
|
||||
// Import GUARDIANS from constants
|
||||
import { GUARDIANS } from '../../constants';
|
||||
import { GUARDIANS } from '@/lib/game/constants';
|
||||
expect(getFloorMaxHP(10)).toBe(GUARDIANS[10].hp);
|
||||
expect(getFloorMaxHP(100)).toBe(GUARDIANS[100].hp);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF, PRESTIGE_DEF, GUARDIANS } from '../../constants';
|
||||
import { SKILLS_DEF, PRESTIGE_DEF, GUARDIANS } from '@/lib/game/constants';
|
||||
|
||||
describe('Skill Definitions', () => {
|
||||
it('all skills should have valid categories', () => {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { computeMaxMana, computeRegen, computeClickMana, computeElementMax } from '../index';
|
||||
import { computeMaxMana, computeRegen, computeClickMana, computeElementMax } from '@/lib/game/stores/index';
|
||||
import type { GameState } from '../types';
|
||||
import { ELEMENTS } from '../../constants';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
const elements: Record<string, { current: number; max: number; unlocked: boolean }> = {};
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getMeditationBonus, calcInsight, getIncursionStrength } from '../index';
|
||||
import { MAX_DAY, INCURSION_START_DAY } from '../../constants';
|
||||
import { getMeditationBonus, calcInsight, getIncursionStrength } from '@/lib/game/stores/index';
|
||||
import { MAX_DAY, INCURSION_START_DAY } from '@/lib/game/constants';
|
||||
import type { GameState } from '../types';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { canAffordSpellCost } from '../index';
|
||||
import { rawCost, elemCost } from '../../constants';
|
||||
import { canAffordSpellCost } from '@/lib/game/stores/index';
|
||||
import { rawCost, elemCost } from '@/lib/game/constants';
|
||||
|
||||
describe('Spell Cost System', () => {
|
||||
describe('rawCost', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getStudySpeedMultiplier, getStudyCostMultiplier } from '../index';
|
||||
import { getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/stores/index';
|
||||
|
||||
describe('Study Speed Functions', () => {
|
||||
describe('getStudySpeedMultiplier', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { fmt, fmtDec } from '../index';
|
||||
import { fmt, fmtDec } from '@/lib/game/stores/index';
|
||||
|
||||
describe('Utility Functions', () => {
|
||||
describe('fmt', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useCombatStore } from '../combatStore';
|
||||
import { useCombatStore } from '@/lib/game/stores/combatStore';
|
||||
|
||||
// Reset stores before each test
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useManaStore } from '../manaStore';
|
||||
import { useManaStore } from '@/lib/game/stores/manaStore';
|
||||
|
||||
// Reset stores before each test
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { usePrestigeStore } from '../prestigeStore';
|
||||
import { usePrestigeStore } from '@/lib/game/stores/prestigeStore';
|
||||
import { useManaStore } from '../manaStore';
|
||||
|
||||
// Reset stores before each test
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useSkillStore } from '../skillStore';
|
||||
import { useSkillStore } from '@/lib/game/stores/skillStore';
|
||||
|
||||
// Reset stores before each test
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useUIStore } from '../uiStore';
|
||||
import { useUIStore } from '@/lib/game/stores/uiStore';
|
||||
|
||||
// Reset stores before each test
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calcDamage } from '../../utils';
|
||||
import { calcDamage } from '@/lib/game/utils';
|
||||
import type { GameState } from '../../types';
|
||||
import { ELEMENTS } from '../../constants';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
const elements: Record<string, { current: number; max: number; unlocked: boolean }> = {};
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getFloorMaxHP, getFloorElement } from '../../utils';
|
||||
import { GUARDIANS } from '../../constants';
|
||||
import { getFloorMaxHP, getFloorElement } from '@/lib/game/utils';
|
||||
import { GUARDIANS } from '@/lib/game/constants';
|
||||
|
||||
describe('Floor Functions', () => {
|
||||
describe('getFloorMaxHP', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { fmt, fmtDec } from '../../utils';
|
||||
import { fmt, fmtDec } from '@/lib/game/utils';
|
||||
|
||||
describe('Formatting Functions', () => {
|
||||
describe('fmt (format number)', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { GUARDIANS } from '../../constants';
|
||||
import { GUARDIANS } from '@/lib/game/constants';
|
||||
|
||||
describe('Guardians', () => {
|
||||
it('should have guardians on expected floors', () => {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getIncursionStrength } from '../../utils';
|
||||
import { MAX_DAY, INCURSION_START_DAY } from '../../constants';
|
||||
import { getIncursionStrength } from '@/lib/game/utils';
|
||||
import { MAX_DAY, INCURSION_START_DAY } from '@/lib/game/constants';
|
||||
|
||||
describe('Incursion Strength', () => {
|
||||
describe('getIncursionStrength', () => {
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { calcInsight } from '../../utils';
|
||||
import { calcInsight } from '@/lib/game/utils';
|
||||
import type { GameState } from '../../types';
|
||||
import { ELEMENTS } from '../../constants';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
const elements: Record<string, { current: number; max: number; unlocked: boolean }> = {};
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { computeMaxMana, computeElementMax, computeRegen, computeClickMana } from '../../utils';
|
||||
import { computeMaxMana, computeElementMax, computeRegen, computeClickMana } from '@/lib/game/utils';
|
||||
import type { GameState } from '../../types';
|
||||
import { ELEMENTS } from '../../constants';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
const elements: Record<string, { current: number; max: number; unlocked: boolean }> = {};
|
||||
@@ -104,22 +104,43 @@ describe('Mana Calculation Functions', () => {
|
||||
const state = createMockState({ prestigeUpgrades: { manaWell: 3 } });
|
||||
expect(computeMaxMana(state)).toBe(100 + 3 * 500);
|
||||
});
|
||||
|
||||
it('should stack manaWell skill and prestige', () => {
|
||||
const state = createMockState({
|
||||
skills: { manaWell: 5 },
|
||||
prestigeUpgrades: { manaWell: 2 },
|
||||
});
|
||||
expect(computeMaxMana(state)).toBe(100 + 5 * 100 + 2 * 500);
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeRegen', () => {
|
||||
it('should return base regen with no upgrades', () => {
|
||||
// Base regen is 2, but computeRegen now includes attunement regen
|
||||
// Enchanter (active, level 1) adds rawManaRegen * 1.5^0 = rawManaRegen
|
||||
// Default enchanter rawManaRegen is 0.5, so base with enchanter = 2 + 0.5 = 2.5
|
||||
|
||||
it('should return base regen with no upgrades (includes attunement regen)', () => {
|
||||
const state = createMockState();
|
||||
expect(computeRegen(state)).toBe(2);
|
||||
// Base 2 + enchanter regen (0.5 * 1 = 0.5) = 2.5
|
||||
expect(computeRegen(state)).toBeCloseTo(2.5, 1);
|
||||
});
|
||||
|
||||
it('should add regen from manaFlow skill', () => {
|
||||
const state = createMockState({ skills: { manaFlow: 5 } });
|
||||
expect(computeRegen(state)).toBe(2 + 5 * 1);
|
||||
// Base 2 + manaFlow 5 + enchanter 0.5 = 7.5
|
||||
expect(computeRegen(state)).toBeCloseTo(2 + 5 * 1 + 0.5, 1);
|
||||
});
|
||||
|
||||
it('should add regen from manaSpring skill', () => {
|
||||
const state = createMockState({ skills: { manaSpring: 1 } });
|
||||
expect(computeRegen(state)).toBe(2 + 2);
|
||||
// Base 2 + manaSpring 2 + enchanter 0.5 = 4.5
|
||||
expect(computeRegen(state)).toBeCloseTo(2 + 2 + 0.5, 1);
|
||||
});
|
||||
|
||||
it('should multiply by temporal echo prestige', () => {
|
||||
const state = createMockState({ prestigeUpgrades: { temporalEcho: 2 } });
|
||||
// (2 + 0.5 enchanter) * 1.2 = 3.0
|
||||
expect(computeRegen(state)).toBeCloseTo(2.5 * 1.2, 1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -138,6 +159,28 @@ describe('Mana Calculation Functions', () => {
|
||||
const state = createMockState({ skills: { manaSurge: 1 } });
|
||||
expect(computeClickMana(state)).toBe(1 + 3);
|
||||
});
|
||||
|
||||
it('should stack manaTap and manaSurge', () => {
|
||||
const state = createMockState({ skills: { manaTap: 1, manaSurge: 1 } });
|
||||
expect(computeClickMana(state)).toBe(1 + 1 + 3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeElementMax', () => {
|
||||
it('should return base element cap with no upgrades', () => {
|
||||
const state = createMockState();
|
||||
expect(computeElementMax(state)).toBe(10);
|
||||
});
|
||||
|
||||
it('should add cap from elemAttune skill', () => {
|
||||
const state = createMockState({ skills: { elemAttune: 5 } });
|
||||
expect(computeElementMax(state)).toBe(10 + 5 * 50);
|
||||
});
|
||||
|
||||
it('should add cap from prestige upgrades', () => {
|
||||
const state = createMockState({ prestigeUpgrades: { elementalAttune: 3 } });
|
||||
expect(computeElementMax(state)).toBe(10 + 3 * 25);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getMeditationBonus } from '../../utils';
|
||||
import { getMeditationBonus } from '@/lib/game/utils';
|
||||
|
||||
describe('Meditation Bonus', () => {
|
||||
describe('getMeditationBonus', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { PRESTIGE_DEF } from '../../constants';
|
||||
import { PRESTIGE_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Prestige Upgrades', () => {
|
||||
it('should have prestige upgrades with valid costs', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SKILLS_DEF } from '../../constants';
|
||||
import { SKILLS_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Skill Definitions', () => {
|
||||
it('should have skills with valid categories', () => {
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { canAffordSpellCost } from '../../utils';
|
||||
import { rawCost, elemCost } from '../../constants';
|
||||
import { canAffordSpellCost } from '@/lib/game/utils';
|
||||
import { rawCost, elemCost } from '@/lib/game/constants';
|
||||
|
||||
describe('Spell Cost System', () => {
|
||||
describe('rawCost', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SPELLS_DEF } from '../../constants';
|
||||
import { SPELLS_DEF } from '@/lib/game/constants';
|
||||
|
||||
describe('Spell Definitions', () => {
|
||||
it('should have manaBolt as a basic spell', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getStudySpeedMultiplier, getStudyCostMultiplier } from '../../utils';
|
||||
import { getStudySpeedMultiplier, getStudyCostMultiplier } from '@/lib/game/constants';
|
||||
|
||||
describe('Study Speed Functions', () => {
|
||||
describe('getStudySpeedMultiplier', () => {
|
||||
@@ -15,6 +15,10 @@ describe('Study Speed Functions', () => {
|
||||
expect(getStudySpeedMultiplier({ quickLearner: 1 })).toBe(1.1);
|
||||
expect(getStudySpeedMultiplier({ quickLearner: 5 })).toBe(1.5);
|
||||
});
|
||||
|
||||
it('should increase by 10% per level up to max', () => {
|
||||
expect(getStudySpeedMultiplier({ quickLearner: 10 })).toBe(2.0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStudyCostMultiplier', () => {
|
||||
@@ -26,6 +30,10 @@ describe('Study Speed Functions', () => {
|
||||
expect(getStudyCostMultiplier({ focusedMind: 1 })).toBe(0.95);
|
||||
expect(getStudyCostMultiplier({ focusedMind: 5 })).toBe(0.75);
|
||||
});
|
||||
|
||||
it('should decrease by 5% per level up to max', () => {
|
||||
expect(getStudyCostMultiplier({ focusedMind: 10 })).toBe(0.5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -47,6 +47,15 @@ export interface CraftingState {
|
||||
materials: Record<string, number>;
|
||||
blueprints: string[];
|
||||
};
|
||||
|
||||
// Enchantment selection state (single source of truth for enchanting UI)
|
||||
enchantmentSelection: {
|
||||
selectedEquipmentType: string | null;
|
||||
selectedEffects: DesignEffect[];
|
||||
designName: string;
|
||||
selectedDesign: string | null;
|
||||
selectedEquipmentInstance: string | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface CraftingActions {
|
||||
@@ -86,6 +95,14 @@ export interface CraftingActions {
|
||||
// Equipment crafting actions
|
||||
startCraftingEquipment: (blueprintId: string) => boolean;
|
||||
cancelEquipmentCrafting: () => void;
|
||||
|
||||
// Enchantment selection actions (store as source of truth)
|
||||
setSelectedEquipmentType: (type: string | null) => void;
|
||||
setSelectedEffects: (effects: DesignEffect[]) => void;
|
||||
setDesignName: (name: string) => void;
|
||||
setSelectedDesign: (id: string | null) => void;
|
||||
setSelectedEquipmentInstance: (id: string | null) => void;
|
||||
resetEnchantmentSelection: () => void;
|
||||
}
|
||||
|
||||
export type CraftingStore = CraftingState & CraftingActions;
|
||||
@@ -108,6 +125,13 @@ export const useCraftingStore = create<CraftingStore>()(
|
||||
materials: {},
|
||||
blueprints: [],
|
||||
},
|
||||
enchantmentSelection: {
|
||||
selectedEquipmentType: null,
|
||||
selectedEffects: [],
|
||||
designName: '',
|
||||
selectedDesign: null,
|
||||
selectedEquipmentInstance: null,
|
||||
},
|
||||
|
||||
// Actions
|
||||
setDesignProgress: (progress) => set({ designProgress: progress }),
|
||||
@@ -311,6 +335,34 @@ export const useCraftingStore = create<CraftingStore>()(
|
||||
useUIStore.getState().addLog(cancelResult.logMessage);
|
||||
},
|
||||
|
||||
// Enchantment selection actions
|
||||
setSelectedEquipmentType: (type) => {
|
||||
set((s) => ({ enchantmentSelection: { ...s.enchantmentSelection, selectedEquipmentType: type }}));
|
||||
},
|
||||
setSelectedEffects: (effects) => {
|
||||
set((s) => ({ enchantmentSelection: { ...s.enchantmentSelection, selectedEffects: effects } }));
|
||||
},
|
||||
setDesignName: (name) => {
|
||||
set((s) => ({ enchantmentSelection: { ...s.enchantmentSelection, designName: name } }));
|
||||
},
|
||||
setSelectedDesign: (id) => {
|
||||
set((s) => ({ enchantmentSelection: { ...s.enchantmentSelection, selectedDesign: id } }));
|
||||
},
|
||||
setSelectedEquipmentInstance: (id) => {
|
||||
set((s) => ({ enchantmentSelection: { ...s.enchantmentSelection, selectedEquipmentInstance: id } }));
|
||||
},
|
||||
resetEnchantmentSelection: () => {
|
||||
set((s) => ({
|
||||
enchantmentSelection: {
|
||||
selectedEquipmentType: null,
|
||||
selectedEffects: [],
|
||||
designName: '',
|
||||
selectedDesign: null,
|
||||
selectedEquipmentInstance: null,
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
||||
// Loot inventory actions
|
||||
deleteMaterial: (materialId: string, amount: number) => {
|
||||
set((state) => {
|
||||
@@ -366,6 +418,7 @@ export const useCraftingStore = create<CraftingStore>()(
|
||||
equipmentInstances: state.equipmentInstances,
|
||||
equippedInstances: state.equippedInstances,
|
||||
lootInventory: state.lootInventory,
|
||||
enchantmentSelection: state.enchantmentSelection,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user