fix staged nation tax rate consumption

This commit is contained in:
2026-07-27 10:07:59 +00:00
parent de510da48e
commit 26e033665a
7 changed files with 118 additions and 49 deletions
@@ -258,4 +258,60 @@ describe('core monthly event actions at the real month boundary', () => {
})
);
});
it('uses the staged rate_tmp for income when the desired rate changes mid-period', async () => {
const nation = buildNation();
nation.meta = { ...nation.meta, rate: 35, rate_tmp: 10 };
const world = buildWorld(
[
{
id: 3,
targetCode: 'month',
priority: 1,
condition: true,
action: [['ProcessIncome', 'gold']],
meta: {},
},
],
(getWorld) => {
const incomeHandler = createIncomeHandler({
getWorld,
scenarioConfig: {
stat: {
total: 300,
min: 10,
max: 100,
npcTotal: 150,
npcMax: 50,
npcMin: 10,
chiefMin: 70,
},
iconPath: '',
map: {},
const: { baseGold: 0, baseRice: 0 },
environment: { mapName: map.id, unitSet: 'default' },
},
nationTraits: new Map(),
});
return new Map([['ProcessIncome', createProcessIncomeActionHandler(incomeHandler)]]);
},
{
currentMonth: 6,
generals: [buildGeneral(1, 1, 3)],
nations: [nation],
cities: [buildCity()],
}
);
await world.advanceMonth(new Date('0190-07-01T00:00:00.000Z'));
expect(world.getNationById(1)).toMatchObject({
gold: 10_105,
meta: {
rate: 35,
rate_tmp: 10,
prev_income_gold: 105,
},
});
});
});
@@ -65,7 +65,7 @@ integration('core monthly event action persistence', () => {
power: 0,
level: 1,
typeCode: 'che_중립',
meta: { rate: 20, bill: 0, chief_set: 1 },
meta: { rate: 35, rate_tmp: 10, bill: 0, chief_set: 1 },
};
const city: City = {
id: cityId,
@@ -287,9 +287,14 @@ integration('core monthly event action persistence', () => {
expect(
await db.nation.findUnique({ where: { id: nationId }, select: { gold: true, rice: true, meta: true } })
).toEqual({
gold: 10_210,
gold: 10_105,
rice: 20_000,
meta: expect.objectContaining({ chief_set: 0, prev_income_gold: 210 }),
meta: expect.objectContaining({
rate: 35,
rate_tmp: 10,
chief_set: 0,
prev_income_gold: 105,
}),
});
expect(await db.city.findUnique({ where: { id: cityId }, select: { meta: true } })).toEqual({
meta: expect.objectContaining({ officer_set: 0 }),
@@ -93,13 +93,15 @@ const populationTrait: NationTraitModule = {
onCalcNationalIncome: (_context, type, amount) => (type === 'pop' ? amount * 1.2 : amount),
};
const buildHarness = (options: {
cities?: City[];
nations?: Nation[];
generals?: TurnGeneral[];
configConst?: Record<string, unknown>;
traits?: ReadonlyMap<string, NationTraitModule>;
} = {}) => {
const buildHarness = (
options: {
cities?: City[];
nations?: Nation[];
generals?: TurnGeneral[];
configConst?: Record<string, unknown>;
traits?: ReadonlyMap<string, NationTraitModule>;
} = {}
) => {
const state: TurnWorldState = {
id: 1,
currentYear: 193,
@@ -152,11 +154,7 @@ describe('ProcessSemiAnnual monthly action', () => {
it('preserves the global popIncrease order, neutral double decay, supplied filtering, and nation trait', async () => {
const { world, handler } = buildHarness({
configConst: { basePopIncreaseAmount: 15_000 },
cities: [
buildCity(1),
buildCity(2, { supplyState: 0 }),
buildCity(3, { nationId: 0 }),
],
cities: [buildCity(1), buildCity(2, { supplyState: 0 }), buildCity(3, { nationId: 0 })],
nations: [buildNation(1, { typeCode: populationTrait.key })],
traits: new Map([[populationTrait.key, populationTrait]]),
});
@@ -224,6 +222,25 @@ describe('ProcessSemiAnnual monthly action', () => {
});
});
it('uses the staged rate_tmp when the desired rate changes mid-period', async () => {
const { world, handler } = buildHarness({
cities: [buildCity(1)],
nations: [buildNation(1, { meta: { rate: 50, rate_tmp: 20 } })],
});
await handler(['gold'], environment, event);
expect(world.getCityById(1)).toMatchObject({
population: 15_525,
agriculture: 991,
commerce: 991,
security: 991,
defence: 991,
wall: 991,
meta: { trust: 55, dead: 0 },
});
});
it('applies strict legacy resource thresholds to generals and nations for either resource', async () => {
const amounts = [1_000, 1_001, 10_000, 10_001, 100_000, 100_001];
const { world, handler } = buildHarness({
@@ -236,18 +253,10 @@ describe('ProcessSemiAnnual monthly action', () => {
await handler(['gold'], environment, event);
await handler(['rice'], environment, event);
expect(world.listGenerals().map((general) => general.gold)).toEqual([
1_000, 991, 9_900, 9_701, 97_000, 97_001,
]);
expect(world.listGenerals().map((general) => general.rice)).toEqual([
1_000, 991, 9_900, 9_701, 97_000, 97_001,
]);
expect(world.listNations().map((nation) => nation.gold)).toEqual([
1_000, 991, 9_900, 9_701, 97_000, 95_001,
]);
expect(world.listNations().map((nation) => nation.rice)).toEqual([
1_000, 991, 9_900, 9_701, 97_000, 95_001,
]);
expect(world.listGenerals().map((general) => general.gold)).toEqual([1_000, 991, 9_900, 9_701, 97_000, 97_001]);
expect(world.listGenerals().map((general) => general.rice)).toEqual([1_000, 991, 9_900, 9_701, 97_000, 97_001]);
expect(world.listNations().map((nation) => nation.gold)).toEqual([1_000, 991, 9_900, 9_701, 97_000, 95_001]);
expect(world.listNations().map((nation) => nation.rice)).toEqual([1_000, 991, 9_900, 9_701, 97_000, 95_001]);
});
it('rejects resources outside the legacy enum', () => {
@@ -25,7 +25,7 @@ const nation: Nation = {
power: 0,
level: 1,
typeCode: 'che_중립',
meta: { rate: 20 },
meta: { rate: 50, rate_tmp: 20 },
};
const buildCity = (id: number, nationIdValue: number): City => ({
@@ -265,7 +265,7 @@ integration('monthly semi-annual database persistence', () => {
expect(await db.nation.findUniqueOrThrow({ where: { id: nationId } })).toMatchObject({
gold: 95_001,
rice: 7_777,
meta: { rate: 20 },
meta: { rate: 50, rate_tmp: 20 },
});
} finally {
await dbHooks.close();