fix: 전투 시뮬레이터 기본 특기 계약 복원

Ref와 같이 시나리오 defaultSpecialDomestic을 공격자와 수비자 payload에 적용한다. 기본 5병종 5x5 고정 seed Ref 차등 회귀와 API·Chromium 회귀를 추가한다.
This commit is contained in:
2026-08-25 05:58:26 +00:00
parent 28758b0bb5
commit ac48f2ae5b
7 changed files with 144 additions and 4 deletions
+12
View File
@@ -31,6 +31,16 @@ const resolveNumber = (record: Record<string, unknown>, keys: string[], fallback
return fallback;
};
const resolveOptionalString = (record: Record<string, unknown>, keys: string[]): string | null => {
for (const key of keys) {
const value = record[key];
if (typeof value === 'string' && value.trim().length > 0) {
return value;
}
}
return null;
};
const resolveUnitSetName = (worldState: WorldStateRow, fallback: string): string => {
const config = asRecord(worldState.config);
const environment = asRecord(config.environment ?? config.map);
@@ -88,6 +98,7 @@ export interface BattleSimEnvironment {
config: WarEngineConfig;
startYear: number;
scenarioEffect: ScenarioEffectKey | null;
defaultSpecialDomestic: string;
}
export const buildBattleSimSeedBase = (request: Pick<BattleSimRequestPayload, 'seed'>): string | null =>
@@ -133,6 +144,7 @@ export const buildBattleSimEnvironment = async (
config,
startYear: resolveStartYear(worldState),
scenarioEffect: normalizeScenarioEffect(scenarioEnvironment.scenarioEffect),
defaultSpecialDomestic: resolveOptionalString(constValues, ['defaultSpecialDomestic']) ?? 'None',
};
};
+1
View File
@@ -109,6 +109,7 @@ export const battleRouter = router({
config: environment.config,
unitSet: environment.unitSet,
scenarioEffect: environment.scenarioEffect,
defaultSpecialDomestic: environment.defaultSpecialDomestic,
nationTypes: traits.nationTypes,
eventDomesticTraits: traits.eventDomesticTraits,
warTraits: traits.warTraits,
+5 -1
View File
@@ -319,7 +319,10 @@ describe('battle router orchestration', () => {
currentYear: 200,
currentMonth: 1,
tickSeconds: 600,
config: { environment: { scenarioEffect: 'event_MoreEffect' } },
config: {
environment: { scenarioEffect: 'event_MoreEffect' },
const: { defaultSpecialDomestic: 'che_event_신산' },
},
meta: { scenarioMeta: { startYear: 180 } },
updatedAt: new Date('2026-01-01T00:00:00Z'),
};
@@ -339,6 +342,7 @@ describe('battle router orchestration', () => {
expect(context).toMatchObject({
world: { startYear: 180, currentYear: 200, currentMonth: 1 },
scenarioEffect: 'event_MoreEffect',
defaultSpecialDomestic: 'che_event_신산',
config: { armPerPhase: 500, maxTrainByWar: 110, maxAtmosByWar: 150 },
});
expect(context.unitSet.crewTypes?.[0]).toMatchObject({
@@ -45,6 +45,7 @@ const readImage = async (relative: string): Promise<Buffer> => {
const simulatorFormOptions = {
world: { startYear: 190, currentYear: 205, currentMonth: 8 },
defaultSpecialDomestic: 'None',
config: {
maxTrainByWar: 120,
maxAtmosByWar: 120,
@@ -416,7 +417,8 @@ test('operates independent/game presets, imports my general, and renders battle
expect(fixture.prepareResponseBytes).toEqual([expect.any(Number)]);
expect(fixture.prepareResponseBytes?.every((bytes) => bytes < 128)).toBe(true);
expect(fixture.preparedPayloads[0]).toMatchObject({
attackerGeneral: { special: 'che_event_신산' },
attackerGeneral: { special: 'None' },
defenderGenerals: [{ special: 'None' }],
});
const downloadPromise = page.waitForEvent('download');
@@ -437,7 +439,8 @@ test('operates independent/game presets, imports my general, and renders battle
await expect.poll(() => fixture.preparedPayloads.length).toBe(2);
expect(await readBrowserWorkerResult(page, 1)).toEqual(fixture.serverResults[1]);
expect(fixture.preparedPayloads[1]).toMatchObject({
attackerGeneral: { special: 'che_event_신산' },
attackerGeneral: { special: 'None' },
defenderGenerals: [{ special: 'None' }],
});
if (artifactRoot) {
@@ -52,6 +52,7 @@ export type BattleSimOptions = {
config: WarEngineConfig;
unitSet: UnitSetDefinition;
scenarioEffect: string | null;
defaultSpecialDomestic: string;
nationTypes: Array<{ key: string; name: string; info: string }>;
eventDomesticTraits: Array<{ key: string; name: string; info: string }>;
warTraits: Array<{ key: string; name: string; info: string }>;
@@ -461,7 +461,7 @@ const buildGeneralPayload = (
personal: general.personal,
// Ref does not expose the domestic speciality in this form. Battle requests
// always use the scenario's default domestic speciality instead.
special: options.value?.eventDomesticTraits[0]?.key ?? general.special,
special: options.value?.defaultSpecialDomestic ?? 'None',
special2: general.special2,
crew: general.crew,
crewtype: general.crewtype,