docs: add comprehensive game briefing document and fix deprecated tests
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 25s
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 25s
- Created GAME_BRIEFING.md with full documentation of all game systems - Fixed getFloorElement to use cycle length instead of hardcoded 8 - Fixed deprecated tests referencing removed elements (life, blood, wood) - Fixed deprecated tests referencing removed skills (deepReservoir, etc.) - Fixed guardian tests to not expect floor 70 - Fixed computeRegen tests to account for attunement regen correctly - All 512 tests now pass The game briefing document includes: - Core game loop and progression - Mana system with all 14 mana types - Time and incursion mechanics - Spire and floor system with room types - Combat system with elemental effectiveness - Guardian and pact system - Attunement system (Enchanter, Invoker, Fabricator) - Skill evolution with 5 tiers and milestone upgrades - Equipment and enchantment system - Golemancy system - Prestige/loop mechanics - Complete formulas and system interactions
This commit is contained in:
@@ -221,7 +221,8 @@ describe('computeRegen', () => {
|
||||
};
|
||||
const effects = { regenBonus: 0, regenMultiplier: 1, permanentRegenBonus: 0 };
|
||||
const result = computeRegen(state, effects);
|
||||
expect(result).toBe(2); // Base regen
|
||||
// Base regen is 2 (this test provides effects, so no attunement bonus)
|
||||
expect(result).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ export function getFloorMaxHP(floor: number): number {
|
||||
}
|
||||
|
||||
export function getFloorElement(floor: number): string {
|
||||
return FLOOR_ELEM_CYCLE[(floor - 1) % 8];
|
||||
return FLOOR_ELEM_CYCLE[(floor - 1) % FLOOR_ELEM_CYCLE.length];
|
||||
}
|
||||
|
||||
// ─── Equipment Spell Helper ─────────────────────────────────────────────────────
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -261,17 +261,17 @@ describe('SkillStore', () => {
|
||||
it('should not start studying without prerequisites', () => {
|
||||
useManaStore.getState().addRawMana(990, 1000);
|
||||
|
||||
// deepReservoir requires manaWell 5
|
||||
const result = useSkillStore.getState().startStudyingSkill('deepReservoir', 1000);
|
||||
// manaOverflow requires manaWell 3
|
||||
const result = useSkillStore.getState().startStudyingSkill('manaOverflow', 1000);
|
||||
|
||||
expect(result.started).toBe(false);
|
||||
});
|
||||
|
||||
it('should start studying with prerequisites met', () => {
|
||||
useManaStore.getState().addRawMana(990, 1000);
|
||||
useSkillStore.getState().setSkillLevel('manaWell', 5);
|
||||
useSkillStore.getState().setSkillLevel('manaWell', 3);
|
||||
|
||||
const result = useSkillStore.getState().startStudyingSkill('deepReservoir', 1000);
|
||||
const result = useSkillStore.getState().startStudyingSkill('manaOverflow', 1000);
|
||||
|
||||
expect(result.started).toBe(true);
|
||||
});
|
||||
|
||||
@@ -61,17 +61,17 @@ describe('SkillStore', () => {
|
||||
|
||||
it('should not start studying without prerequisites', () => {
|
||||
const skillStore = useSkillStore.getState();
|
||||
// deepReservoir requires manaWell level 5
|
||||
const result = skillStore.startStudyingSkill('deepReservoir', 1000);
|
||||
// manaOverflow requires manaWell level 3
|
||||
const result = skillStore.startStudyingSkill('manaOverflow', 1000);
|
||||
|
||||
expect(result.started).toBe(false);
|
||||
});
|
||||
|
||||
it('should start studying with prerequisites met', () => {
|
||||
useSkillStore.setState({ skills: { manaWell: 5 } });
|
||||
useSkillStore.setState({ skills: { manaWell: 3 } });
|
||||
|
||||
const skillStore = useSkillStore.getState();
|
||||
const result = skillStore.startStudyingSkill('deepReservoir', 1000);
|
||||
const result = skillStore.startStudyingSkill('manaOverflow', 1000);
|
||||
|
||||
expect(result.started).toBe(true);
|
||||
});
|
||||
@@ -271,36 +271,36 @@ describe('ManaStore', () => {
|
||||
|
||||
describe('craftComposite', () => {
|
||||
it('should craft composite element with correct ingredients', () => {
|
||||
// Set up ingredients for blood (life + water)
|
||||
// Set up ingredients for metal (fire + earth)
|
||||
useManaStore.setState({
|
||||
elements: {
|
||||
...useManaStore.getState().elements,
|
||||
life: { current: 5, max: 10, unlocked: true },
|
||||
water: { current: 5, max: 10, unlocked: true },
|
||||
fire: { current: 5, max: 10, unlocked: true },
|
||||
earth: { current: 5, max: 10, unlocked: true },
|
||||
}
|
||||
});
|
||||
|
||||
const result = useManaStore.getState().craftComposite('blood', ['life', 'water']);
|
||||
const result = useManaStore.getState().craftComposite('metal', ['fire', 'earth']);
|
||||
|
||||
expect(result).toBe(true);
|
||||
|
||||
const state = useManaStore.getState();
|
||||
expect(state.elements.life.current).toBe(4);
|
||||
expect(state.elements.water.current).toBe(4);
|
||||
expect(state.elements.blood.current).toBe(1);
|
||||
expect(state.elements.blood.unlocked).toBe(true);
|
||||
expect(state.elements.fire.current).toBe(4);
|
||||
expect(state.elements.earth.current).toBe(4);
|
||||
expect(state.elements.metal.current).toBe(1);
|
||||
expect(state.elements.metal.unlocked).toBe(true);
|
||||
});
|
||||
|
||||
it('should not craft without ingredients', () => {
|
||||
useManaStore.setState({
|
||||
elements: {
|
||||
...useManaStore.getState().elements,
|
||||
life: { current: 0, max: 10, unlocked: true },
|
||||
water: { current: 0, max: 10, unlocked: true },
|
||||
fire: { current: 0, max: 10, unlocked: true },
|
||||
earth: { current: 0, max: 10, unlocked: true },
|
||||
}
|
||||
});
|
||||
|
||||
const result = useManaStore.getState().craftComposite('blood', ['life', 'water']);
|
||||
const result = useManaStore.getState().craftComposite('metal', ['fire', 'earth']);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Comprehensive Store Tests
|
||||
*
|
||||
* Tests the split store architecture to ensure all stores work correctly together.
|
||||
* Updated for the new skill system with tiers and upgrade trees.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'bun:test';
|
||||
@@ -89,8 +90,20 @@ function createMockState(overrides: Partial<GameState> = {}): GameState {
|
||||
autoSchedule: false,
|
||||
studyQueue: [],
|
||||
craftQueue: [],
|
||||
attunements: {
|
||||
enchanter: { id: 'enchanter', active: true, level: 1, experience: 0 },
|
||||
invoker: { id: 'invoker', active: false, level: 1, experience: 0 },
|
||||
fabricator: { id: 'fabricator', active: false, level: 1, experience: 0 },
|
||||
},
|
||||
golemancy: {
|
||||
enabledGolems: [],
|
||||
summonedGolems: [],
|
||||
lastSummonFloor: 0,
|
||||
},
|
||||
equippedInstances: { mainHand: null, offHand: null, head: null, body: null, hands: null, accessory1: null, accessory2: null },
|
||||
equipmentInstances: {},
|
||||
...overrides,
|
||||
};
|
||||
} as GameState;
|
||||
}
|
||||
|
||||
// ─── Utility Function Tests ─────────────────────────────────────────────────
|
||||
@@ -140,43 +153,42 @@ describe('Mana Calculations', () => {
|
||||
expect(computeMaxMana(state)).toBe(100 + 5 * 100);
|
||||
});
|
||||
|
||||
it('should add mana from deepReservoir skill', () => {
|
||||
const state = createMockState({ skills: { deepReservoir: 3 } });
|
||||
expect(computeMaxMana(state)).toBe(100 + 3 * 500);
|
||||
});
|
||||
|
||||
it('should add mana from prestige upgrades', () => {
|
||||
const state = createMockState({ prestigeUpgrades: { manaWell: 3 } });
|
||||
expect(computeMaxMana(state)).toBe(100 + 3 * 500);
|
||||
});
|
||||
|
||||
it('should stack all mana bonuses', () => {
|
||||
|
||||
it('should stack manaWell skill and prestige', () => {
|
||||
const state = createMockState({
|
||||
skills: { manaWell: 5, deepReservoir: 2 },
|
||||
skills: { manaWell: 5 },
|
||||
prestigeUpgrades: { manaWell: 2 },
|
||||
});
|
||||
expect(computeMaxMana(state)).toBe(100 + 5 * 100 + 2 * 500 + 2 * 500);
|
||||
expect(computeMaxMana(state)).toBe(100 + 5 * 100 + 2 * 500);
|
||||
});
|
||||
});
|
||||
|
||||
describe('computeRegen', () => {
|
||||
it('should return base regen with no upgrades', () => {
|
||||
// Base regen is 2 (attunement regen is added separately in the store)
|
||||
const state = createMockState();
|
||||
expect(computeRegen(state)).toBe(2);
|
||||
});
|
||||
|
||||
it('should add regen from manaFlow skill', () => {
|
||||
// Base 2 + manaFlow 5
|
||||
const state = createMockState({ skills: { manaFlow: 5 } });
|
||||
expect(computeRegen(state)).toBe(2 + 5 * 1);
|
||||
});
|
||||
|
||||
it('should add regen from manaSpring skill', () => {
|
||||
// Base 2 + manaSpring 2
|
||||
const state = createMockState({ skills: { manaSpring: 1 } });
|
||||
expect(computeRegen(state)).toBe(2 + 2);
|
||||
});
|
||||
|
||||
it('should multiply by temporal echo prestige', () => {
|
||||
const state = createMockState({ prestigeUpgrades: { temporalEcho: 2 } });
|
||||
// Base 2 * 1.2 = 2.4
|
||||
expect(computeRegen(state)).toBe(2 * 1.2);
|
||||
});
|
||||
});
|
||||
@@ -231,19 +243,6 @@ describe('Combat Calculations', () => {
|
||||
expect(dmg).toBeGreaterThanOrEqual(5); // Base damage (can be higher with crit)
|
||||
});
|
||||
|
||||
it('should add damage from combatTrain skill', () => {
|
||||
const state = createMockState({ skills: { combatTrain: 5 } });
|
||||
const dmg = calcDamage(state, 'manaBolt');
|
||||
expect(dmg).toBeGreaterThanOrEqual(5 + 5 * 5); // 5 base + 25 from skill
|
||||
});
|
||||
|
||||
it('should multiply by arcaneFury skill', () => {
|
||||
const state = createMockState({ skills: { arcaneFury: 3 } });
|
||||
const dmg = calcDamage(state, 'manaBolt');
|
||||
// 5 * 1.3 = 6.5 minimum (without crit)
|
||||
expect(dmg).toBeGreaterThanOrEqual(5 * 1.3 * 0.8);
|
||||
});
|
||||
|
||||
it('should have elemental bonuses', () => {
|
||||
const state = createMockState({
|
||||
spells: {
|
||||
@@ -280,15 +279,22 @@ describe('Combat Calculations', () => {
|
||||
|
||||
describe('getFloorElement', () => {
|
||||
it('should cycle through elements in order', () => {
|
||||
// FLOOR_ELEM_CYCLE has 7 elements: fire, water, air, earth, light, dark, death
|
||||
expect(getFloorElement(1)).toBe('fire');
|
||||
expect(getFloorElement(2)).toBe('water');
|
||||
expect(getFloorElement(3)).toBe('air');
|
||||
expect(getFloorElement(4)).toBe('earth');
|
||||
expect(getFloorElement(5)).toBe('light');
|
||||
expect(getFloorElement(6)).toBe('dark');
|
||||
expect(getFloorElement(7)).toBe('death');
|
||||
});
|
||||
|
||||
it('should wrap around after 8 floors', () => {
|
||||
expect(getFloorElement(9)).toBe('fire');
|
||||
expect(getFloorElement(10)).toBe('water');
|
||||
it('should wrap around after 7 floors', () => {
|
||||
// Floor 8 should be fire (wraps around)
|
||||
expect(getFloorElement(8)).toBe('fire');
|
||||
expect(getFloorElement(9)).toBe('water');
|
||||
expect(getFloorElement(15)).toBe('fire'); // (15-1) % 7 = 0
|
||||
expect(getFloorElement(16)).toBe('water'); // (16-1) % 7 = 1
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -463,7 +469,8 @@ describe('Spell Cost System', () => {
|
||||
|
||||
describe('Skill Definitions', () => {
|
||||
it('all skills should have valid categories', () => {
|
||||
const validCategories = ['mana', 'combat', 'study', 'craft', 'research', 'ascension'];
|
||||
const validCategories = ['mana', 'study', 'research', 'ascension', 'enchant',
|
||||
'effectResearch', 'invocation', 'pact', 'fabrication', 'golemancy', 'craft'];
|
||||
Object.values(SKILLS_DEF).forEach(skill => {
|
||||
expect(validCategories).toContain(skill.cat);
|
||||
});
|
||||
@@ -531,15 +538,16 @@ describe('Prestige Upgrades', () => {
|
||||
// ─── Guardian Tests ──────────────────────────────────────────────────────
|
||||
|
||||
describe('Guardian Definitions', () => {
|
||||
it('should have guardians every 10 floors', () => {
|
||||
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100].forEach(floor => {
|
||||
it('should have guardians on expected floors (no floor 70)', () => {
|
||||
// Floor 70 was removed from the game
|
||||
[10, 20, 30, 40, 50, 60, 80, 90, 100].forEach(floor => {
|
||||
expect(GUARDIANS[floor]).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have increasing HP', () => {
|
||||
let prevHP = 0;
|
||||
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100].forEach(floor => {
|
||||
[10, 20, 30, 40, 50, 60, 80, 90, 100].forEach(floor => {
|
||||
expect(GUARDIANS[floor].hp).toBeGreaterThan(prevHP);
|
||||
prevHP = GUARDIANS[floor].hp;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user