멸망 후 등용장에 현재 임관 모드 적용
This commit is contained in:
@@ -2825,6 +2825,7 @@ export const createImmediateGeneralActionExecutor = async (options: {
|
|||||||
baseContext,
|
baseContext,
|
||||||
{
|
{
|
||||||
world: state,
|
world: state,
|
||||||
|
worldConfig: options.world.getWorldConfig(),
|
||||||
scenarioConfig: options.world.getScenarioConfig(),
|
scenarioConfig: options.world.getScenarioConfig(),
|
||||||
scenarioMeta: options.scenarioMeta,
|
scenarioMeta: options.scenarioMeta,
|
||||||
map: options.map,
|
map: options.map,
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ export interface ActionContextWorldRef {
|
|||||||
|
|
||||||
export interface ActionContextOptions<TArgs extends Record<string, unknown> = Record<string, unknown>> {
|
export interface ActionContextOptions<TArgs extends Record<string, unknown> = Record<string, unknown>> {
|
||||||
world: ActionContextWorldState;
|
world: ActionContextWorldState;
|
||||||
|
/** Current game configuration, distinct from the legacy world meta. */
|
||||||
|
worldConfig?: Record<string, unknown>;
|
||||||
/** Ref Message::sendRaw stamps the current logical game tick, not the actor turn time. */
|
/** Ref Message::sendRaw stamps the current logical game tick, not the actor turn time. */
|
||||||
gameNow?: Date;
|
gameNow?: Date;
|
||||||
/** Differential fixtures may point shared message icons at the Ref asset origin. */
|
/** Differential fixtures may point shared message icons at the Ref asset origin. */
|
||||||
|
|||||||
@@ -824,7 +824,11 @@ export const actionContextBuilder: ActionContextBuilder = (base, options) => {
|
|||||||
const diplomacy = options.worldRef.listDiplomacy();
|
const diplomacy = options.worldRef.listDiplomacy();
|
||||||
const warConfig = buildWarConfig(options.scenarioConfig, options.unitSet);
|
const warConfig = buildWarConfig(options.scenarioConfig, options.unitSet);
|
||||||
const aftermathConfig = buildWarAftermathConfig(options.scenarioConfig, warConfig.castleCrewTypeId);
|
const aftermathConfig = buildWarAftermathConfig(options.scenarioConfig, warConfig.castleCrewTypeId);
|
||||||
const joinModeRaw = options.world.meta?.join_mode ?? options.world.meta?.joinMode;
|
const joinModeRaw =
|
||||||
|
options.worldConfig?.join_mode ??
|
||||||
|
options.worldConfig?.joinMode ??
|
||||||
|
options.world.meta?.join_mode ??
|
||||||
|
options.world.meta?.joinMode;
|
||||||
aftermathConfig.joinMode = joinModeRaw === 'onlyRandom' ? 'onlyRandom' : 'full';
|
aftermathConfig.joinMode = joinModeRaw === 'onlyRandom' ? 'onlyRandom' : 'full';
|
||||||
return {
|
return {
|
||||||
...base,
|
...base,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
|
|||||||
|
|
||||||
import type { City, General, Nation } from '../src/domain/entities.js';
|
import type { City, General, Nation } from '../src/domain/entities.js';
|
||||||
import { resolveGeneralAction } from '../src/actions/engine.js';
|
import { resolveGeneralAction } from '../src/actions/engine.js';
|
||||||
import { ActionDefinition, orderDefenderGenerals } from '../src/actions/turn/general/che_출병.js';
|
import { ActionDefinition, actionContextBuilder, orderDefenderGenerals } from '../src/actions/turn/general/che_출병.js';
|
||||||
import type { DispatchResolveContext } from '../src/actions/turn/general/che_출병.js';
|
import type { DispatchResolveContext } from '../src/actions/turn/general/che_출병.js';
|
||||||
import type { TurnSchedule } from '../src/turn/calendar.js';
|
import type { TurnSchedule } from '../src/turn/calendar.js';
|
||||||
import type { WarAftermathConfig, WarEngineConfig } from '../src/war/types.js';
|
import type { WarAftermathConfig, WarEngineConfig } from '../src/war/types.js';
|
||||||
@@ -192,6 +192,44 @@ const uniqueItem: ItemModule = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe('che_출병', () => {
|
describe('che_출병', () => {
|
||||||
|
it.each(['full', 'onlyRandom'] as const)(
|
||||||
|
'reads %s join mode from current world config for collapse aftermath',
|
||||||
|
(joinMode) => {
|
||||||
|
const general = { ...buildGeneral(1, 1, 1), turnTime: new Date('0200-01-01T00:00:00.000Z') };
|
||||||
|
const destination = buildCity(2, 2);
|
||||||
|
const context = actionContextBuilder(
|
||||||
|
{ general, rng },
|
||||||
|
{
|
||||||
|
world: { currentYear: 200, currentMonth: 1, tickSeconds: 3600, meta: { joinMode: 'full' } },
|
||||||
|
worldConfig: { joinMode },
|
||||||
|
scenarioConfig: {
|
||||||
|
stat: { total: 0, min: 0, max: 0, npcTotal: 0, npcMin: 0, npcMax: 0, chiefMin: 0 },
|
||||||
|
iconPath: '',
|
||||||
|
map: {},
|
||||||
|
const: {},
|
||||||
|
environment: { mapName: 'test', unitSet: 'test' },
|
||||||
|
},
|
||||||
|
map: { id: 'test', name: 'test', cities: [] },
|
||||||
|
unitSet,
|
||||||
|
worldRef: {
|
||||||
|
getCityById: () => destination,
|
||||||
|
getNationById: () => buildNation(2),
|
||||||
|
listCities: () => [destination],
|
||||||
|
listNations: () => [buildNation(2)],
|
||||||
|
listGenerals: () => [general],
|
||||||
|
listDiplomacy: () => [],
|
||||||
|
} as never,
|
||||||
|
actionArgs: { destCityId: destination.id },
|
||||||
|
createGeneralId: () => 3,
|
||||||
|
createNationId: () => 3,
|
||||||
|
seedBase: 'test',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(context?.aftermathConfig).toMatchObject({ joinMode });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
it('runs war battle and emits patches/logs', () => {
|
it('runs war battle and emits patches/logs', () => {
|
||||||
const attackerNation = buildNation(1);
|
const attackerNation = buildNation(1);
|
||||||
const defenderNation = buildNation(2);
|
const defenderNation = buildNation(2);
|
||||||
@@ -494,7 +532,7 @@ describe('che_출병', () => {
|
|||||||
// assignment, so the aftermath alone would not patch it).
|
// assignment, so the aftermath alone would not patch it).
|
||||||
const defenderPatch = resolution.patches?.generals.find((patch) => patch.id === defender.id);
|
const defenderPatch = resolution.patches?.generals.find((patch) => patch.id === defender.id);
|
||||||
expect(defenderPatch).toBeDefined();
|
expect(defenderPatch).toBeDefined();
|
||||||
expect((defenderPatch?.patch.crew ?? defender.crew)).toBeLessThan(1500);
|
expect(defenderPatch?.patch.crew ?? defender.crew).toBeLessThan(1500);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('orders equal-priority defender inputs by general number before the stable battle sort', () => {
|
it('orders equal-priority defender inputs by general number before the stable battle sort', () => {
|
||||||
|
|||||||
@@ -726,6 +726,45 @@ describe('war aftermath', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('does not draw or send collapse recruitment in random-only mode', () => {
|
||||||
|
const attackerNation = buildNation(1);
|
||||||
|
const defenderNation = buildNation(2);
|
||||||
|
defenderNation.chiefGeneralId = 2;
|
||||||
|
const attackerCity = buildCity(1, 1);
|
||||||
|
const defenderCity = buildCity(2, 2);
|
||||||
|
const attacker = buildGeneral(1, 1, 1);
|
||||||
|
const lord = buildGeneral(2, 2, 2);
|
||||||
|
lord.officerLevel = 12;
|
||||||
|
lord.npcState = 2;
|
||||||
|
const rng = {
|
||||||
|
nextRange: vi.fn(() => 0.2),
|
||||||
|
nextBool: vi.fn(() => true),
|
||||||
|
nextRangeInt: vi.fn(() => 1),
|
||||||
|
} as unknown as RandUtil;
|
||||||
|
|
||||||
|
const outcome = resolveWarAftermath({
|
||||||
|
battle: { attacker, defenders: [], defenderCity, logs: [], conquered: true, reports: [] },
|
||||||
|
attackerNation,
|
||||||
|
defenderNation,
|
||||||
|
attackerCity,
|
||||||
|
defenderCity,
|
||||||
|
nations: [attackerNation, defenderNation],
|
||||||
|
cities: [attackerCity, defenderCity],
|
||||||
|
generals: [attacker, lord],
|
||||||
|
unitSet: buildUnitSet(),
|
||||||
|
map: DEFAULT_MAP,
|
||||||
|
config: { ...buildConfig(), joinMode: 'onlyRandom' },
|
||||||
|
time: { year: 186, month: 1, startYear: 179 },
|
||||||
|
messageTime: MESSAGE_TIME,
|
||||||
|
rng,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(outcome.conquest?.nationCollapsed).toBe(true);
|
||||||
|
expect(outcome.conquest?.messages).toEqual([]);
|
||||||
|
expect(outcome.conquest?.ruinedNpcJoinPlans).toEqual([]);
|
||||||
|
expect(rng.nextBool).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('dispatches city conquest to every stationed defender before collapse RNG', () => {
|
it('dispatches city conquest to every stationed defender before collapse RNG', () => {
|
||||||
const rng = new RandUtil(new ConstantRNG(0));
|
const rng = new RandUtil(new ConstantRNG(0));
|
||||||
const draws = [0.01, 0.02, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7];
|
const draws = [0.01, 0.02, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7];
|
||||||
|
|||||||
Reference in New Issue
Block a user