feat(game-engine): port city changes and NPC troop leaders
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { City, CitySeed } from '@sammo-ts/logic';
|
||||
|
||||
import { applyChangeCity, applyInitialChangeCityEvents } from '../src/turn/monthlyChangeCityAction.js';
|
||||
|
||||
const buildCity = (id: number, nationId: number, name = `도시${id}`): City => ({
|
||||
id,
|
||||
name,
|
||||
nationId,
|
||||
level: 4,
|
||||
state: 0,
|
||||
population: 1_001,
|
||||
populationMax: 2_000,
|
||||
agriculture: 501,
|
||||
agricultureMax: 1_000,
|
||||
commerce: 499,
|
||||
commerceMax: 1_000,
|
||||
security: 99,
|
||||
securityMax: 1_000,
|
||||
supplyState: 1,
|
||||
frontState: 0,
|
||||
defence: 101,
|
||||
defenceMax: 1_000,
|
||||
wall: 50,
|
||||
wallMax: 1_000,
|
||||
meta: { trust: 40, trade: 100 },
|
||||
});
|
||||
|
||||
describe('ChangeCity monthly action', () => {
|
||||
it('applies legacy target selection, percentage rounding, clamping, and ordered max changes', () => {
|
||||
const cities = [buildCity(1, 0, '낙양'), buildCity(2, 1, '장안')];
|
||||
|
||||
expect(applyChangeCity(cities, 'free', { pop: '50%', trust: '+70', trade: 999 })).toEqual([
|
||||
expect.objectContaining({
|
||||
id: 1,
|
||||
population: 1_000,
|
||||
meta: expect.objectContaining({ trust: 100, trade: 105 }),
|
||||
}),
|
||||
]);
|
||||
expect(applyChangeCity(cities, 'occupied', { agri: '*2', comm: '-600' })).toEqual([
|
||||
expect.objectContaining({ id: 2, agriculture: 1_000, commerce: 0 }),
|
||||
]);
|
||||
expect(
|
||||
applyChangeCity(cities, ['cities', 1, '장안'], {
|
||||
pop_max: '+100',
|
||||
pop: '100%',
|
||||
})
|
||||
).toEqual([
|
||||
expect.objectContaining({ id: 2, populationMax: 2_100, population: 2_100 }),
|
||||
]);
|
||||
});
|
||||
|
||||
it('applies unconditional scenario initial events without changing non-target cities', () => {
|
||||
const cities = [
|
||||
{ ...buildCity(1, 0), trust: 40, trade: 100 },
|
||||
{ ...buildCity(2, 1), trust: 40, trade: 100 },
|
||||
] as CitySeed[];
|
||||
|
||||
const result = applyInitialChangeCityEvents(cities, [
|
||||
[
|
||||
true,
|
||||
['ChangeCity', 'free', { pop: '70%', trust: 80 }],
|
||||
['ChangeCity', 'occupied', { def: '70%', wall: '70%' }],
|
||||
],
|
||||
]);
|
||||
|
||||
expect(result[0]).toMatchObject({ population: 1_400, trust: 80, defence: 101, wall: 50 });
|
||||
expect(result[1]).toMatchObject({ population: 1_001, trust: 40, defence: 700, wall: 700 });
|
||||
});
|
||||
|
||||
it('rejects invalid fields and division by zero', () => {
|
||||
expect(() => applyChangeCity([buildCity(1, 0)], 'all', { unknown: 1 })).toThrow(
|
||||
'Unsupported ChangeCity key'
|
||||
);
|
||||
expect(() => applyChangeCity([buildCity(1, 0)], 'all', { pop: '/0' })).toThrow(
|
||||
'divide by zero'
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user