feat: NPC 징병 및 예약 턴 핸들러 개선, 대형 테스트 맵 인구 수정

This commit is contained in:
2026-01-24 10:32:38 +00:00
parent 753abdaf47
commit 2964c8ea59
8 changed files with 225 additions and 24 deletions
+1
View File
@@ -53,6 +53,7 @@ const resolveConstraintEnv = (
startYear,
relYear,
openingPartYear: env.openingPartYear,
minAvailableRecruitPop: env.minAvailableRecruitPop,
};
};
@@ -351,6 +351,10 @@ export const do징병 = (ai: GeneralAI) => {
const goldCost = (picked.cost * getTechCost(tech) * crewAmount) / 100;
const riceCost = crewAmount / 100;
if (ai.general.gold <= 0 || ai.general.rice <= 0) {
return null;
}
if (ai.generalPolicy.can('모병') && ai.general.gold >= goldCost * 6) {
const hire = ai.buildGeneralCandidate('che_모병', { crewType: crewTypeId, amount: crewAmount }, '징병');
if (hire) {
@@ -54,6 +54,7 @@ export const buildCommandEnv = (config: ScenarioConfig, unitSet?: UnitSetDefinit
return {
develCost: resolveNumber(constValues, ['develCost', 'develcost', 'develrate'], 0),
minAvailableRecruitPop: resolveNumber(constValues, ['minAvailableRecruitPop'], 30000),
trainDelta: resolveNumber(constValues, ['trainDelta'], 0),
atmosDelta: resolveNumber(constValues, ['atmosDelta'], 0),
maxTrainByCommand: resolveNumber(constValues, ['maxTrainByCommand'], 0),
@@ -46,7 +46,7 @@ const DEFAULT_ACTION = '휴식';
const resolveConstraintEnv = (
world: TurnWorldState,
scenarioMeta: ScenarioMeta | undefined,
openingPartYear: number
env: TurnCommandEnv
): Record<string, unknown> => {
const startYear = typeof scenarioMeta?.startYear === 'number' ? scenarioMeta.startYear : undefined;
const relYear = typeof startYear === 'number' ? world.currentYear - startYear : undefined;
@@ -58,7 +58,8 @@ const resolveConstraintEnv = (
month: world.currentMonth,
startYear,
relYear,
openingPartYear,
openingPartYear: env.openingPartYear,
minAvailableRecruitPop: env.minAvailableRecruitPop,
};
};
@@ -385,6 +386,15 @@ export const createReservedTurnHandler = async (options: {
unitSet?: UnitSetDefinition;
getWorld: () => InMemoryTurnWorld | null;
commandProfile?: TurnCommandProfile;
onActionResolved?: (payload: {
kind: 'nation' | 'general';
generalId: number;
nationId: number | null;
requestedAction: string;
actionKey: string;
usedFallback: boolean;
blockedReason?: string;
}) => void;
}): Promise<GeneralTurnHandler> => {
const env = buildCommandEnv(options.scenarioConfig, options.unitSet);
const commandProfile = options.commandProfile ?? DEFAULT_TURN_COMMAND_PROFILE;
@@ -466,7 +476,7 @@ export const createReservedTurnHandler = async (options: {
const worldOverlay = worldRef ? createWorldOverlay(worldRef) : null;
const worldView = worldOverlay?.view ?? worldRef;
const baseConstraintEnv = {
...resolveConstraintEnv(context.world, options.scenarioMeta, env.openingPartYear),
...resolveConstraintEnv(context.world, options.scenarioMeta, env),
...(options.map ? { map: options.map } : {}),
...(options.unitSet ? { unitSet: options.unitSet } : {}),
};
@@ -494,18 +504,22 @@ export const createReservedTurnHandler = async (options: {
fallbackDefinition: GeneralActionDefinition,
command: ReservedTurnEntry,
applyNextTurnAt: boolean
): { nextTurnAt?: Date; actionKey: string } => {
): { nextTurnAt?: Date; actionKey: string; usedFallback: boolean; blockedReason?: string } => {
const resolvedDefinition = resolveDefinition(command.action, definitionMap, fallbackDefinition);
const rawArgs = extractArgsRecord(command.args);
const parsedArgs = resolvedDefinition.parseArgs(rawArgs);
let definition = resolvedDefinition;
let actionArgs = parsedArgs ?? {};
let actionKey = definition.key;
let usedFallback = false;
let blockedReason: string | undefined = undefined;
if (parsedArgs === null) {
definition = fallbackDefinition;
actionArgs = definition.parseArgs({}) ?? {};
actionKey = definition.key;
usedFallback = true;
blockedReason = '예약된 명령을 실행하지 못했습니다.';
logs.push(createActionLog('예약된 명령을 실행하지 못했습니다.'));
}
@@ -532,7 +546,9 @@ export const createReservedTurnHandler = async (options: {
definition = fallbackDefinition;
actionArgs = definition.parseArgs({}) ?? {};
actionKey = definition.key;
usedFallback = true;
const reason = result.kind === 'deny' ? result.reason : '조건을 확인할 수 없습니다.';
blockedReason = reason;
const meta = result.kind === 'deny' ? { constraintName: result.constraintName } : undefined;
logs.push(createActionLog(reason, meta));
}
@@ -684,7 +700,7 @@ export const createReservedTurnHandler = async (options: {
}
}
return { nextTurnAt: applyNextTurnAt ? resolution.nextTurnAt : undefined, actionKey };
return { nextTurnAt: applyNextTurnAt ? resolution.nextTurnAt : undefined, actionKey, usedFallback, blockedReason };
};
if (currentNation && currentGeneral.officerLevel >= 5) {
@@ -716,7 +732,16 @@ export const createReservedTurnHandler = async (options: {
nationCommand = { action: candidate.action, args: candidate.args };
}
}
runAction(nationDefinitions, nationFallback, nationCommand, false);
const nationResult = runAction(nationDefinitions, nationFallback, nationCommand, false);
options.onActionResolved?.({
kind: 'nation',
generalId: currentGeneral.id,
nationId: currentNation?.id ?? null,
requestedAction: nationCommand.action,
actionKey: nationResult.actionKey,
usedFallback: nationResult.usedFallback,
...(nationResult.blockedReason ? { blockedReason: nationResult.blockedReason } : {}),
});
options.reservedTurns.shiftNationTurns(currentNation.id, currentGeneral.officerLevel, -1);
}
@@ -745,6 +770,15 @@ export const createReservedTurnHandler = async (options: {
}
}
const generalResult = runAction(generalDefinitions, generalFallback, generalCommand, true);
options.onActionResolved?.({
kind: 'general',
generalId: currentGeneral.id,
nationId: currentNation?.id ?? null,
requestedAction: generalCommand.action,
actionKey: generalResult.actionKey,
usedFallback: generalResult.usedFallback,
...(generalResult.blockedReason ? { blockedReason: generalResult.blockedReason } : {}),
});
const nextTurnAt = generalResult.nextTurnAt;
options.reservedTurns.shiftGeneralTurns(currentGeneral.id, -1);