원조 기반 제의의 서신 만료 tick을 영속화하고 거절 상대를 영구 제외한다. 불가침 월별 감소와 만료 전후 선전포고 경계를 통합 회귀로 검증한다.
137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { GAME_TICKS_PER_TURN } from '@sammo-ts/common';
|
|
|
|
import {
|
|
DEFAULT_WAR_TERM,
|
|
DIPLOMACY_STATE,
|
|
processDiplomacyMonth,
|
|
resolveDiplomacyMessageValidMinutes,
|
|
resolveDiplomacyMessageValidUntilTick,
|
|
type DiplomacyEntry,
|
|
} from '@sammo-ts/logic';
|
|
|
|
const buildEntry = (
|
|
fromNationId: number,
|
|
toNationId: number,
|
|
state: number,
|
|
term: number,
|
|
dead = 0
|
|
): DiplomacyEntry => ({
|
|
fromNationId,
|
|
toNationId,
|
|
state,
|
|
term,
|
|
dead,
|
|
meta: {},
|
|
});
|
|
|
|
describe('diplomacy month processing', () => {
|
|
it('promotes declaration to war after term expires', () => {
|
|
const entries = [
|
|
buildEntry(1, 2, DIPLOMACY_STATE.DECLARATION, 1),
|
|
buildEntry(2, 1, DIPLOMACY_STATE.DECLARATION, 1),
|
|
];
|
|
const result = processDiplomacyMonth(
|
|
entries,
|
|
new Map([
|
|
[1, 1],
|
|
[2, 1],
|
|
])
|
|
);
|
|
|
|
for (const entry of result) {
|
|
expect(entry.state).toBe(DIPLOMACY_STATE.WAR);
|
|
expect(entry.term).toBe(DEFAULT_WAR_TERM);
|
|
}
|
|
});
|
|
|
|
it('ends war when both terms reach zero without casualties', () => {
|
|
const entries = [buildEntry(1, 2, DIPLOMACY_STATE.WAR, 1), buildEntry(2, 1, DIPLOMACY_STATE.WAR, 1)];
|
|
const result = processDiplomacyMonth(
|
|
entries,
|
|
new Map([
|
|
[1, 1],
|
|
[2, 1],
|
|
])
|
|
);
|
|
|
|
for (const entry of result) {
|
|
expect(entry.state).toBe(DIPLOMACY_STATE.TRADE);
|
|
expect(entry.term).toBe(0);
|
|
}
|
|
});
|
|
|
|
it('extends war term based on accumulated casualties', () => {
|
|
const entries = [buildEntry(1, 2, DIPLOMACY_STATE.WAR, 3, 400), buildEntry(2, 1, DIPLOMACY_STATE.WAR, 3, 0)];
|
|
const result = processDiplomacyMonth(
|
|
entries,
|
|
new Map([
|
|
[1, 2],
|
|
[2, 2],
|
|
])
|
|
);
|
|
|
|
const forward = result.find((entry) => entry.fromNationId === 1 && entry.toNationId === 2);
|
|
const reverse = result.find((entry) => entry.fromNationId === 2 && entry.toNationId === 1);
|
|
expect(forward?.term).toBe(4);
|
|
expect(forward?.dead).toBe(0);
|
|
expect(reverse?.term).toBe(2);
|
|
});
|
|
|
|
it('expires non-aggression pact into trade', () => {
|
|
const entries = [
|
|
buildEntry(1, 2, DIPLOMACY_STATE.NON_AGGRESSION, 1),
|
|
buildEntry(2, 1, DIPLOMACY_STATE.NON_AGGRESSION, 1),
|
|
];
|
|
const result = processDiplomacyMonth(
|
|
entries,
|
|
new Map([
|
|
[1, 1],
|
|
[2, 1],
|
|
])
|
|
);
|
|
|
|
for (const entry of result) {
|
|
expect(entry.state).toBe(DIPLOMACY_STATE.TRADE);
|
|
expect(entry.term).toBe(0);
|
|
}
|
|
});
|
|
|
|
it('decrements non-aggression exactly once per month and allows declaration only after the final month', () => {
|
|
let entries = [
|
|
buildEntry(1, 2, DIPLOMACY_STATE.NON_AGGRESSION, 3),
|
|
buildEntry(2, 1, DIPLOMACY_STATE.NON_AGGRESSION, 3),
|
|
];
|
|
const generalCounts = new Map([
|
|
[1, 1],
|
|
[2, 1],
|
|
]);
|
|
|
|
entries = processDiplomacyMonth(entries, generalCounts);
|
|
expect(entries.map(({ state, term }) => ({ state, term }))).toEqual([
|
|
{ state: DIPLOMACY_STATE.NON_AGGRESSION, term: 2 },
|
|
{ state: DIPLOMACY_STATE.NON_AGGRESSION, term: 2 },
|
|
]);
|
|
entries = processDiplomacyMonth(entries, generalCounts);
|
|
expect(entries.map(({ state, term }) => ({ state, term }))).toEqual([
|
|
{ state: DIPLOMACY_STATE.NON_AGGRESSION, term: 1 },
|
|
{ state: DIPLOMACY_STATE.NON_AGGRESSION, term: 1 },
|
|
]);
|
|
entries = processDiplomacyMonth(entries, generalCounts);
|
|
expect(entries.map(({ state, term }) => ({ state, term }))).toEqual([
|
|
{ state: DIPLOMACY_STATE.TRADE, term: 0 },
|
|
{ state: DIPLOMACY_STATE.TRADE, term: 0 },
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('diplomacy message validity', () => {
|
|
it('uses the same three-turn or 30-minute logical expiry for messages and NPC retry gates', () => {
|
|
expect(resolveDiplomacyMessageValidMinutes(600)).toBe(30);
|
|
expect(resolveDiplomacyMessageValidUntilTick(100, 600)).toBe(100 + GAME_TICKS_PER_TURN * 3);
|
|
|
|
expect(resolveDiplomacyMessageValidMinutes(300)).toBe(30);
|
|
expect(resolveDiplomacyMessageValidUntilTick(100, 300)).toBe(100 + GAME_TICKS_PER_TURN * 6);
|
|
});
|
|
});
|