246 lines
5.7 KiB
JavaScript
246 lines
5.7 KiB
JavaScript
import {
|
|
tick,
|
|
startGameLoop,
|
|
stopGameLoop,
|
|
setSpeed,
|
|
} from '../src/engine/tickLoop.js';
|
|
|
|
// ==================== 测试配置 ====================
|
|
|
|
const careerConfig = {
|
|
careers: [
|
|
{
|
|
id: 'student',
|
|
name: '学童',
|
|
type: 'scholar',
|
|
unlockConditions: { type: 'age', op: '>=', value: 6 },
|
|
dailyIncome: 1,
|
|
expCurve: { base: 10, factor: 1.15 },
|
|
},
|
|
{
|
|
id: 'soldier',
|
|
name: '士兵',
|
|
type: 'military',
|
|
unlockConditions: {
|
|
type: 'and',
|
|
conditions: [
|
|
{ type: 'age', op: '>=', value: 14 },
|
|
{ type: 'stat', op: '>=', stat: 'body', value: 10 },
|
|
],
|
|
},
|
|
dailyIncome: 2,
|
|
expCurve: { base: 20, factor: 1.18 },
|
|
},
|
|
],
|
|
};
|
|
|
|
const eventConfig = {
|
|
events: [
|
|
{
|
|
id: 'invasion_military_40',
|
|
name: '蛮族入侵',
|
|
trigger: { type: 'age', value: 40 },
|
|
isChoice: true,
|
|
choices: [
|
|
{
|
|
id: 'resist',
|
|
text: '率军抵抗',
|
|
requirements: {
|
|
type: 'careerLevel',
|
|
careerId: 'soldier',
|
|
op: '>=',
|
|
value: 50,
|
|
},
|
|
effects: [{ type: 'addLog', text: '你率领大军击退了蛮族入侵!' }],
|
|
},
|
|
{
|
|
id: 'flee',
|
|
text: '弃城逃跑',
|
|
effects: [{ type: 'addLog', text: '你选择了弃城逃跑。' }],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
id: 'daily_training',
|
|
name: '日常修炼',
|
|
trigger: { type: 'random', pool: 'normal' },
|
|
weight: 50,
|
|
effects: [{ type: 'addStat', stat: 'body', value: 1 }],
|
|
},
|
|
],
|
|
};
|
|
|
|
const shopConfig = {
|
|
artifacts: [],
|
|
};
|
|
|
|
// ==================== 测试辅助函数 ====================
|
|
|
|
function assertEqual(actual, expected, message) {
|
|
const actualStr = JSON.stringify(actual);
|
|
const expectedStr = JSON.stringify(expected);
|
|
if (actualStr !== expectedStr) {
|
|
throw new Error(
|
|
`${message}\n 期望: ${expectedStr}\n 实际: ${actualStr}`
|
|
);
|
|
}
|
|
}
|
|
|
|
function assertTrue(value, message) {
|
|
if (!value) {
|
|
throw new Error(message || `期望为 true,实际为 ${value}`);
|
|
}
|
|
}
|
|
|
|
// ==================== 测试用例 ====================
|
|
|
|
function testTickAdvancesDay() {
|
|
const state = {
|
|
alive: true,
|
|
paused: false,
|
|
day: 0,
|
|
year: 0,
|
|
age: 5,
|
|
stats: {},
|
|
careers: {},
|
|
money: 0,
|
|
worldFlags: {},
|
|
};
|
|
|
|
tick(state, 1, careerConfig, eventConfig, shopConfig);
|
|
assertEqual(state.day, 1, 'day should advance by 1');
|
|
}
|
|
|
|
function testTickUnlocksCareer() {
|
|
const state = {
|
|
alive: true,
|
|
paused: false,
|
|
day: 0,
|
|
year: 0,
|
|
age: 5,
|
|
stats: {},
|
|
careers: {},
|
|
money: 0,
|
|
worldFlags: {},
|
|
};
|
|
|
|
// Age 5 -> 6 after 365 days
|
|
tick(state, 365, careerConfig, eventConfig, shopConfig);
|
|
assertEqual(state.age, 6, 'age should be 6 after 365 days');
|
|
assertTrue(state.careers.student !== undefined, 'student should unlock at age 6');
|
|
}
|
|
|
|
function testTickIncome() {
|
|
const state = {
|
|
alive: true,
|
|
paused: false,
|
|
day: 0,
|
|
year: 0,
|
|
age: 10,
|
|
stats: {},
|
|
careers: { student: { level: 0, exp: 0 } },
|
|
money: 0,
|
|
worldFlags: {},
|
|
};
|
|
|
|
tick(state, 10, careerConfig, eventConfig, shopConfig);
|
|
assertEqual(state.money, 10, 'money should increase by 10 (1 per day from student)');
|
|
}
|
|
|
|
function testTickExp() {
|
|
const state = {
|
|
alive: true,
|
|
paused: false,
|
|
day: 0,
|
|
year: 0,
|
|
age: 10,
|
|
stats: { wisdom: 0 },
|
|
careers: { student: { level: 0, exp: 0 } },
|
|
money: 0,
|
|
metaExp: {},
|
|
worldFlags: {},
|
|
};
|
|
|
|
tick(state, 1, careerConfig, eventConfig, shopConfig);
|
|
assertEqual(state.careers.student.level, 1, 'student should level up to 1 after 1 day');
|
|
assertEqual(state.careers.student.exp, 0, 'remaining exp should be 0 after level up');
|
|
|
|
// Test level up: need 10 exp for level 1, 11.5 for level 2
|
|
const state2 = {
|
|
alive: true,
|
|
paused: false,
|
|
day: 0,
|
|
year: 0,
|
|
age: 10,
|
|
stats: { wisdom: 0 },
|
|
careers: { student: { level: 0, exp: 0 } },
|
|
money: 0,
|
|
metaExp: {},
|
|
worldFlags: {},
|
|
};
|
|
|
|
tick(state2, 2, careerConfig, eventConfig, shopConfig);
|
|
assertEqual(state2.careers.student.level, 1, 'student should level up to 1');
|
|
assertEqual(state2.careers.student.exp, 10, 'remaining exp should be 10 (20 - 10)');
|
|
}
|
|
|
|
function testAgeEventTriggers() {
|
|
const state = {
|
|
alive: true,
|
|
paused: false,
|
|
day: 0,
|
|
year: 0,
|
|
age: 39,
|
|
stats: {},
|
|
careers: {},
|
|
money: 0,
|
|
worldFlags: {},
|
|
triggeredEvents: new Set(),
|
|
};
|
|
|
|
// 365 ticks to go from age 39 to 40
|
|
tick(state, 365, careerConfig, eventConfig, shopConfig);
|
|
assertEqual(state.age, 40, 'age should be 40');
|
|
assertEqual(state.paused, true, 'game should pause on choice event');
|
|
assertTrue(state.currentEvent !== undefined, 'currentEvent should be set');
|
|
assertEqual(state.currentEvent.id, 'invasion_military_40', 'should trigger invasion event at 40');
|
|
}
|
|
|
|
// ==================== 运行测试 ====================
|
|
|
|
function runTests() {
|
|
const tests = [
|
|
{ name: 'testTickAdvancesDay', fn: testTickAdvancesDay },
|
|
{ name: 'testTickUnlocksCareer', fn: testTickUnlocksCareer },
|
|
{ name: 'testTickIncome', fn: testTickIncome },
|
|
{ name: 'testTickExp', fn: testTickExp },
|
|
{ name: 'testAgeEventTriggers', fn: testAgeEventTriggers },
|
|
];
|
|
|
|
let passed = 0;
|
|
let failed = 0;
|
|
|
|
for (const test of tests) {
|
|
try {
|
|
test.fn();
|
|
console.log(` PASS: ${test.name}`);
|
|
passed++;
|
|
} catch (err) {
|
|
console.log(` FAIL: ${test.name}`);
|
|
console.log(` ${err.message}`);
|
|
failed++;
|
|
}
|
|
}
|
|
|
|
console.log(`\n${passed} passed, ${failed} failed`);
|
|
|
|
if (failed === 0) {
|
|
console.log('All tickLoop tests PASS');
|
|
process.exit(0);
|
|
} else {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
runTests();
|