201 lines
6.7 KiB
JavaScript
201 lines
6.7 KiB
JavaScript
|
|
import { applyEffect } from '../src/engine/effectApplier.js';
|
||
|
|
|
||
|
|
function assert(cond, msg) {
|
||
|
|
if (!cond) throw new Error(msg);
|
||
|
|
}
|
||
|
|
|
||
|
|
function createState() {
|
||
|
|
return {
|
||
|
|
day: 5,
|
||
|
|
year: 1,
|
||
|
|
age: 10,
|
||
|
|
alive: true,
|
||
|
|
money: 0,
|
||
|
|
stats: { body: 0, wisdom: 0, charm: 0, destiny: 0, business: 0, intelligence: 0 },
|
||
|
|
careers: {},
|
||
|
|
shopItems: {},
|
||
|
|
activeBuffs: {},
|
||
|
|
artifacts: {},
|
||
|
|
worldFlags: {},
|
||
|
|
logs: [],
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
// === addMoney ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addMoney', value: 100 }, state);
|
||
|
|
assert(state.money === 100, 'addMoney should add to money');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.money = 50;
|
||
|
|
applyEffect({ type: 'addMoney', value: 25 }, state);
|
||
|
|
assert(state.money === 75, 'addMoney should add to existing money');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === addStat ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addStat', stat: 'body', value: 5 }, state);
|
||
|
|
assert(state.stats.body === 5, 'addStat should add to stat');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.stats.body = 10;
|
||
|
|
applyEffect({ type: 'addStat', stat: 'body', value: 3 }, state);
|
||
|
|
assert(state.stats.body === 13, 'addStat should add to existing stat');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === setStat ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'setStat', stat: 'wisdom', value: 20 }, state);
|
||
|
|
assert(state.stats.wisdom === 20, 'setStat should set stat to value');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.stats.wisdom = 50;
|
||
|
|
applyEffect({ type: 'setStat', stat: 'wisdom', value: 10 }, state);
|
||
|
|
assert(state.stats.wisdom === 10, 'setStat should overwrite existing stat');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === setFlag ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'setFlag', flag: 'met_taoist', value: true }, state);
|
||
|
|
assert(state.worldFlags.met_taoist === true, 'setFlag should set flag');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.worldFlags.met_taoist = true;
|
||
|
|
applyEffect({ type: 'setFlag', flag: 'met_taoist', value: false }, state);
|
||
|
|
assert(state.worldFlags.met_taoist === false, 'setFlag should overwrite flag');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === unlockCareer ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'unlockCareer', careerId: 'student' }, state);
|
||
|
|
assert(state.careers.student !== undefined, 'unlockCareer should create career');
|
||
|
|
assert(state.careers.student.level === 0, 'unlockCareer should set level to 0');
|
||
|
|
assert(state.careers.student.exp === 0, 'unlockCareer should set exp to 0');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.careers.student = { level: 5, exp: 100 };
|
||
|
|
applyEffect({ type: 'unlockCareer', careerId: 'student' }, state);
|
||
|
|
assert(state.careers.student.level === 5, 'unlockCareer should not overwrite existing career');
|
||
|
|
assert(state.careers.student.exp === 100, 'unlockCareer should not overwrite existing career exp');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === addExp ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addExp', careerId: 'student', value: 50 }, state);
|
||
|
|
assert(state.careers.student !== undefined, 'addExp should create career if missing');
|
||
|
|
assert(state.careers.student.exp === 50, 'addExp should add exp');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.careers.student = { level: 1, exp: 10 };
|
||
|
|
applyEffect({ type: 'addExp', careerId: 'student', value: 20 }, state);
|
||
|
|
assert(state.careers.student.exp === 30, 'addExp should add to existing exp');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === addItem ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addItem', itemId: 'taoist_license' }, state);
|
||
|
|
assert(state.shopItems.taoist_license === 1, 'addItem should add item');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.shopItems.taoist_license = 2;
|
||
|
|
applyEffect({ type: 'addItem', itemId: 'taoist_license' }, state);
|
||
|
|
assert(state.shopItems.taoist_license === 3, 'addItem should increment existing item');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === addBuff ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addBuff', buffId: 'blessing', duration: 10 }, state);
|
||
|
|
assert(state.activeBuffs.blessing !== undefined, 'addBuff should add buff');
|
||
|
|
assert(state.activeBuffs.blessing.expiresDay === 15, 'addBuff should calculate expiresDay');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addBuff', buffId: 'curse', duration: -1 }, state);
|
||
|
|
assert(state.activeBuffs.curse !== undefined, 'addBuff should add permanent buff');
|
||
|
|
assert(state.activeBuffs.curse.expiresDay === -1, 'addBuff should set expiresDay to -1 for permanent');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === upgradeArtifact ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'upgradeArtifact', artifactId: 'immortal_sword' }, state);
|
||
|
|
assert(state.artifacts.immortal_sword === 1, 'upgradeArtifact should add artifact');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
state.artifacts.immortal_sword = 2;
|
||
|
|
applyEffect({ type: 'upgradeArtifact', artifactId: 'immortal_sword' }, state);
|
||
|
|
assert(state.artifacts.immortal_sword === 3, 'upgradeArtifact should increment existing artifact');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === addLog ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addLog', text: 'Test log entry' }, state);
|
||
|
|
assert(state.logs.length === 1, 'addLog should add log entry');
|
||
|
|
assert(state.logs[0].text === 'Test log entry', 'addLog should set text');
|
||
|
|
assert(state.logs[0].day === 5, 'addLog should set day');
|
||
|
|
assert(state.logs[0].year === 1, 'addLog should set year');
|
||
|
|
assert(state.logs[0].age === 10, 'addLog should set age');
|
||
|
|
assert(state.logs[0].type === 'normal', 'addLog should default type to normal');
|
||
|
|
assert(typeof state.logs[0].timestamp === 'number', 'addLog should set timestamp');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'addLog', text: 'First' }, state);
|
||
|
|
applyEffect({ type: 'addLog', text: 'Second' }, state);
|
||
|
|
assert(state.logs.length === 2, 'addLog should add multiple entries');
|
||
|
|
assert(state.logs[0].text === 'Second', 'addLog should add newest to front');
|
||
|
|
assert(state.logs[1].text === 'First', 'addLog should keep older entries at back');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
for (let i = 0; i < 510; i++) {
|
||
|
|
applyEffect({ type: 'addLog', text: `Log ${i}` }, state);
|
||
|
|
}
|
||
|
|
assert(state.logs.length === 500, 'addLog should cap at 500 entries');
|
||
|
|
assert(state.logs[0].text === 'Log 509', 'addLog should keep newest entries');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === die ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'die', reason: 'old_age' }, state);
|
||
|
|
assert(state.alive === false, 'die should set alive to false');
|
||
|
|
assert(state.deathReason === 'old_age', 'die should set deathReason');
|
||
|
|
}
|
||
|
|
|
||
|
|
// === edge cases ===
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect(null, state);
|
||
|
|
assert(state.money === 0, 'null effect should not modify state');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({}, state);
|
||
|
|
assert(state.money === 0, 'empty effect should not modify state');
|
||
|
|
}
|
||
|
|
{
|
||
|
|
const state = createState();
|
||
|
|
applyEffect({ type: 'unknownEffect' }, state);
|
||
|
|
assert(state.money === 0, 'unknown effect should not modify state');
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('All effectApplier tests PASS');
|