fix general founding and rebellion parity
This commit is contained in:
@@ -154,12 +154,10 @@ export const do거병 = (ai: GeneralAI) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const do건국 = (ai: GeneralAI) => {
|
export const do건국 = (ai: GeneralAI) => {
|
||||||
const mapName = ai.scenarioConfig.environment.mapName ?? 'sammo';
|
|
||||||
const prefix = mapName.endsWith('_') ? mapName : `${mapName}_`;
|
|
||||||
const nationType =
|
const nationType =
|
||||||
ai.aiConst.availableNationTypes.length > 0
|
ai.aiConst.availableNationTypes.length > 0
|
||||||
? (ai.rng.choice(ai.aiConst.availableNationTypes) as string)
|
? (ai.rng.choice(ai.aiConst.availableNationTypes) as string)
|
||||||
: `${prefix}def`;
|
: 'che_도적';
|
||||||
const colorType = ai.rng.nextRangeInt(0, 32);
|
const colorType = ai.rng.nextRangeInt(0, 32);
|
||||||
const nationName = `㉿${Array.from(ai.general.name).slice(1).join('')}`;
|
const nationName = `㉿${Array.from(ai.general.name).slice(1).join('')}`;
|
||||||
|
|
||||||
|
|||||||
@@ -1261,6 +1261,12 @@ export const createReservedTurnHandler = async (options: {
|
|||||||
if (resolution.created?.nations) {
|
if (resolution.created?.nations) {
|
||||||
const newNations = resolution.created.nations as Nation[];
|
const newNations = resolution.created.nations as Nation[];
|
||||||
createdNations.push(...newNations);
|
createdNations.push(...newNations);
|
||||||
|
if (actionKey === 'che_거병') {
|
||||||
|
for (const nation of newNations) {
|
||||||
|
options.reservedTurns.ensureNationTurns(nation.id, 12);
|
||||||
|
options.reservedTurns.ensureNationTurns(nation.id, 11);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (worldOverlay) {
|
if (worldOverlay) {
|
||||||
for (const nation of newNations) {
|
for (const nation of newNations) {
|
||||||
worldOverlay.syncNation(nation);
|
worldOverlay.syncNation(nation);
|
||||||
|
|||||||
@@ -787,7 +787,7 @@ describe('Reserved Turn Execution Integration', () => {
|
|||||||
generalId: 1,
|
generalId: 1,
|
||||||
turnIdx: 1,
|
turnIdx: 1,
|
||||||
actionCode: 'che_건국',
|
actionCode: 'che_건국',
|
||||||
arg: { nationName: 'NewEmpire', nationType: 'che_def', colorType: 0 },
|
arg: { nationName: 'NewEmpire', nationType: 'che_도적', colorType: 0 },
|
||||||
},
|
},
|
||||||
{ generalId: 2, turnIdx: 1, actionCode: 'che_농지개간', arg: {} },
|
{ generalId: 2, turnIdx: 1, actionCode: 'che_농지개간', arg: {} },
|
||||||
// 2
|
// 2
|
||||||
@@ -798,7 +798,7 @@ describe('Reserved Turn Execution Integration', () => {
|
|||||||
generalId: 1,
|
generalId: 1,
|
||||||
turnIdx: 3,
|
turnIdx: 3,
|
||||||
actionCode: 'che_건국',
|
actionCode: 'che_건국',
|
||||||
arg: { nationName: 'NewEmpire', nationType: 'che_def', colorType: 0 },
|
arg: { nationName: 'NewEmpire', nationType: 'che_도적', colorType: 0 },
|
||||||
},
|
},
|
||||||
{ generalId: 2, turnIdx: 3, actionCode: 'che_농지개간', arg: {} },
|
{ generalId: 2, turnIdx: 3, actionCode: 'che_농지개간', arg: {} },
|
||||||
// 4
|
// 4
|
||||||
@@ -809,7 +809,7 @@ describe('Reserved Turn Execution Integration', () => {
|
|||||||
generalId: 1,
|
generalId: 1,
|
||||||
turnIdx: 5,
|
turnIdx: 5,
|
||||||
actionCode: 'che_건국',
|
actionCode: 'che_건국',
|
||||||
arg: { nationName: 'NewEmpire', nationType: 'che_def', colorType: 0 },
|
arg: { nationName: 'NewEmpire', nationType: 'che_도적', colorType: 0 },
|
||||||
},
|
},
|
||||||
{ generalId: 2, turnIdx: 5, actionCode: 'che_농지개간', arg: {} },
|
{ generalId: 2, turnIdx: 5, actionCode: 'che_농지개간', arg: {} },
|
||||||
];
|
];
|
||||||
@@ -1058,7 +1058,7 @@ describe('Reserved Turn Execution Integration', () => {
|
|||||||
generalId: 1,
|
generalId: 1,
|
||||||
turnIdx: 0,
|
turnIdx: 0,
|
||||||
actionCode: 'che_건국',
|
actionCode: 'che_건국',
|
||||||
arg: { nationName: 'Empire', nationType: 'che_def', colorType: 0 },
|
arg: { nationName: 'Empire', nationType: 'che_도적', colorType: 0 },
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -1102,7 +1102,7 @@ describe('Reserved Turn Execution Integration', () => {
|
|||||||
turnsG1[0] = { action: 'che_거병', args: {} };
|
turnsG1[0] = { action: 'che_거병', args: {} };
|
||||||
turnsG1[1] = {
|
turnsG1[1] = {
|
||||||
action: 'che_건국',
|
action: 'che_건국',
|
||||||
args: { nationName: 'Empire', nationType: 'che_def', colorType: 0 },
|
args: { nationName: 'Empire', nationType: 'che_도적', colorType: 0 },
|
||||||
};
|
};
|
||||||
await reservedTurnStore.flushChanges();
|
await reservedTurnStore.flushChanges();
|
||||||
|
|
||||||
@@ -1112,6 +1112,11 @@ describe('Reserved Turn Execution Integration', () => {
|
|||||||
maxGenerals: 100,
|
maxGenerals: 100,
|
||||||
catchUpCap: 10,
|
catchUpCap: 10,
|
||||||
});
|
});
|
||||||
|
const uprisingNationId = world.getGeneralById(1)!.nationId;
|
||||||
|
expect(reservedTurnStore.peekDirtyState().nationInitializationKeys).toEqual([
|
||||||
|
`${uprisingNationId}:12`,
|
||||||
|
`${uprisingNationId}:11`,
|
||||||
|
]);
|
||||||
world.consumeDirtyState(); // clear logs
|
world.consumeDirtyState(); // clear logs
|
||||||
|
|
||||||
// Execute Founding attempt (Turn 2)
|
// Execute Founding attempt (Turn 2)
|
||||||
@@ -1145,7 +1150,7 @@ describe('Reserved Turn Execution Integration', () => {
|
|||||||
const turnsG1_v2 = reservedTurnStore.getGeneralTurns(1);
|
const turnsG1_v2 = reservedTurnStore.getGeneralTurns(1);
|
||||||
turnsG1_v2[0] = {
|
turnsG1_v2[0] = {
|
||||||
action: 'che_건국',
|
action: 'che_건국',
|
||||||
args: { nationName: 'Empire', nationType: 'che_def', colorType: 0 },
|
args: { nationName: 'Empire', nationType: 'che_도적', colorType: 0 },
|
||||||
};
|
};
|
||||||
await reservedTurnStore.flushChanges();
|
await reservedTurnStore.flushChanges();
|
||||||
|
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ import {
|
|||||||
createDiplomacyPatchEffect,
|
createDiplomacyPatchEffect,
|
||||||
createNationAddEffect,
|
createNationAddEffect,
|
||||||
} from '@sammo-ts/logic/actions/engine.js';
|
} from '@sammo-ts/logic/actions/engine.js';
|
||||||
import { LogCategory, LogFormat } from '@sammo-ts/logic/logging/types.js';
|
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
||||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||||
import type { ActionContextBuilder, ActionContextBase } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
import type { ActionContextBuilder, ActionContextBase } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||||
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
||||||
import type { GeneralTurnCommandSpec } from './index.js';
|
import type { GeneralTurnCommandSpec } from './index.js';
|
||||||
|
import { getLegacyStringWidth } from '@sammo-ts/logic/troop/management.js';
|
||||||
|
|
||||||
export interface UprisingArgs {}
|
export interface UprisingArgs {}
|
||||||
|
|
||||||
@@ -29,6 +30,19 @@ export interface UprisingContext extends ActionContextBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ACTION_NAME = '거병';
|
const ACTION_NAME = '거병';
|
||||||
|
const truncateLegacyWidth = (value: string, maxWidth: number): string => {
|
||||||
|
let result = '';
|
||||||
|
let width = 0;
|
||||||
|
for (const character of value) {
|
||||||
|
const characterWidth = getLegacyStringWidth(character);
|
||||||
|
if (width + characterWidth > maxWidth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result += character;
|
||||||
|
width += characterWidth;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
export class ActionDefinition<
|
export class ActionDefinition<
|
||||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||||
@@ -65,8 +79,7 @@ export class ActionDefinition<
|
|||||||
const nations = uprisingCtx.listNations ? uprisingCtx.listNations() : [];
|
const nations = uprisingCtx.listNations ? uprisingCtx.listNations() : [];
|
||||||
|
|
||||||
if (nations.some((n) => n.name === nationName)) {
|
if (nations.some((n) => n.name === nationName)) {
|
||||||
nationName = '㉥' + nationName;
|
nationName = truncateLegacyWidth('㉥' + nationName, 18);
|
||||||
if (nationName.length > 18) nationName = nationName.substring(0, 18);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nations.some((n) => n.name === nationName)) {
|
if (nations.some((n) => n.name === nationName)) {
|
||||||
@@ -88,7 +101,7 @@ export class ActionDefinition<
|
|||||||
color: '#330000',
|
color: '#330000',
|
||||||
typeCode: 'che_중립',
|
typeCode: 'che_중립',
|
||||||
level: 0,
|
level: 0,
|
||||||
capitalCityId: null,
|
capitalCityId: 0,
|
||||||
chiefGeneralId: general.id,
|
chiefGeneralId: general.id,
|
||||||
gold: 0,
|
gold: 0,
|
||||||
rice: uprisingCtx.baseRice,
|
rice: uprisingCtx.baseRice,
|
||||||
@@ -110,24 +123,29 @@ export class ActionDefinition<
|
|||||||
category: LogCategory.ACTION,
|
category: LogCategory.ACTION,
|
||||||
format: LogFormat.MONTH,
|
format: LogFormat.MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(`${general.name}${josaYi} ${cityName}에 거병하였습니다.`, {
|
context.addLog(`<Y>${general.name}</>${josaYi} <G><b>${cityName}</b></>에 거병하였습니다.`, {
|
||||||
category: LogCategory.USER,
|
scope: LogScope.SYSTEM,
|
||||||
format: LogFormat.PLAIN,
|
category: LogCategory.SUMMARY,
|
||||||
|
format: LogFormat.MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(`【거병】${general.name}${josaYi} 세력을 결성하였습니다.`, {
|
context.addLog(
|
||||||
|
`<Y><b>【거병】</b></><D><b>${general.name}</b></>${josaYi} 세력을 결성하였습니다.`,
|
||||||
|
{
|
||||||
|
scope: LogScope.SYSTEM,
|
||||||
|
category: LogCategory.HISTORY,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
context.addLog(`<G><b>${cityName}</b></>에서 거병`, {
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
format: LogFormat.PLAIN,
|
format: LogFormat.YEAR_MONTH,
|
||||||
});
|
|
||||||
context.addLog(`${cityName}에서 거병`, {
|
|
||||||
category: LogCategory.HISTORY,
|
|
||||||
format: LogFormat.PLAIN,
|
|
||||||
});
|
|
||||||
context.addLog(`${general.name}${josaYi} ${cityName}에서 거병`, {
|
|
||||||
category: LogCategory.HISTORY,
|
|
||||||
format: LogFormat.PLAIN,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
tryApplyUniqueLottery(context, { acquireType: '아이템', reason: ACTION_NAME });
|
tryApplyUniqueLottery(context, {
|
||||||
|
acquireType: '아이템',
|
||||||
|
reason: ACTION_NAME,
|
||||||
|
nationName,
|
||||||
|
});
|
||||||
|
|
||||||
const effects: GeneralActionEffect<TriggerState>[] = [
|
const effects: GeneralActionEffect<TriggerState>[] = [
|
||||||
createNationAddEffect(newNation),
|
createNationAddEffect(newNation),
|
||||||
|
|||||||
@@ -19,57 +19,26 @@ import {
|
|||||||
createNationPatchEffect,
|
createNationPatchEffect,
|
||||||
} from '@sammo-ts/logic/actions/engine.js';
|
} from '@sammo-ts/logic/actions/engine.js';
|
||||||
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
||||||
import { z } from 'zod';
|
|
||||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||||
import { defaultActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||||
|
import { resolveInitYearMonth } from '@sammo-ts/logic/actions/turn/actionContextHelpers.js';
|
||||||
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
||||||
import type { GeneralTurnCommandSpec } from './index.js';
|
import type { GeneralTurnCommandSpec } from './index.js';
|
||||||
import { parseArgsWithSchema } from '../parseArgs.js';
|
import { parseArgsWithSchema } from '../parseArgs.js';
|
||||||
import { JosaUtil } from '@sammo-ts/common';
|
import { JosaUtil } from '@sammo-ts/common';
|
||||||
|
import {
|
||||||
|
FOUNDING_ARGS_SCHEMA,
|
||||||
|
getNationTypeDisplayName,
|
||||||
|
NATION_COLORS,
|
||||||
|
type FoundingArgs,
|
||||||
|
} from './foundingShared.js';
|
||||||
|
|
||||||
const ACTION_NAME = '건국';
|
const ACTION_NAME = '건국';
|
||||||
const ARGS_SCHEMA = z.object({
|
interface FoundingResolveContext<TriggerState extends GeneralTriggerState = GeneralTriggerState>
|
||||||
nationName: z.string().min(1),
|
extends GeneralActionResolveContext<TriggerState> {
|
||||||
nationType: z.string().min(1),
|
currentYearMonth?: number;
|
||||||
colorType: z.number(),
|
initYearMonth?: number;
|
||||||
});
|
}
|
||||||
export type FoundingArgs = z.infer<typeof ARGS_SCHEMA>;
|
|
||||||
|
|
||||||
const NATION_COLORS = [
|
|
||||||
'#FF0000',
|
|
||||||
'#800000',
|
|
||||||
'#A0522D',
|
|
||||||
'#FF6347',
|
|
||||||
'#FFA500',
|
|
||||||
'#FFDAB9',
|
|
||||||
'#FFD700',
|
|
||||||
'#FFFF00',
|
|
||||||
'#7CFC00',
|
|
||||||
'#00FF00',
|
|
||||||
'#808000',
|
|
||||||
'#008000',
|
|
||||||
'#2E8B57',
|
|
||||||
'#008080',
|
|
||||||
'#20B2AA',
|
|
||||||
'#6495ED',
|
|
||||||
'#7FFFD4',
|
|
||||||
'#AFEEEE',
|
|
||||||
'#87CEEB',
|
|
||||||
'#00FFFF',
|
|
||||||
'#00BFFF',
|
|
||||||
'#0000FF',
|
|
||||||
'#000080',
|
|
||||||
'#483D8B',
|
|
||||||
'#7B68EE',
|
|
||||||
'#BA55D3',
|
|
||||||
'#800080',
|
|
||||||
'#FF00FF',
|
|
||||||
'#FFC0CB',
|
|
||||||
'#F5F5DC',
|
|
||||||
'#E0FFFF',
|
|
||||||
'#FFFFFF',
|
|
||||||
'#A9A9A9',
|
|
||||||
];
|
|
||||||
|
|
||||||
export class ActionDefinition<
|
export class ActionDefinition<
|
||||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||||
@@ -81,7 +50,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseArgs(raw: unknown): FoundingArgs | null {
|
parseArgs(raw: unknown): FoundingArgs | null {
|
||||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
return parseArgsWithSchema(FOUNDING_ARGS_SCHEMA, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildMinConstraints(_ctx: ConstraintContext, _args: FoundingArgs): Constraint[] {
|
buildMinConstraints(_ctx: ConstraintContext, _args: FoundingArgs): Constraint[] {
|
||||||
@@ -106,15 +75,20 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
context: GeneralActionResolveContext<TriggerState>,
|
context: FoundingResolveContext<TriggerState>,
|
||||||
args: FoundingArgs
|
args: FoundingArgs
|
||||||
): GeneralActionOutcome<TriggerState> {
|
): GeneralActionOutcome<TriggerState> {
|
||||||
const general = context.general;
|
const general = context.general;
|
||||||
const nation = context.nation!;
|
const nation = context.nation!;
|
||||||
const cityId = general.cityId!;
|
const cityId = general.cityId!;
|
||||||
|
|
||||||
if (args.colorType < 0 || args.colorType >= NATION_COLORS.length) {
|
if ((context.currentYearMonth ?? 0) <= (context.initYearMonth ?? 0)) {
|
||||||
throw new Error('Invalid color type');
|
context.addLog('다음 턴부터 건국할 수 있습니다.', {
|
||||||
|
scope: LogScope.GENERAL,
|
||||||
|
category: LogCategory.ACTION,
|
||||||
|
format: LogFormat.MONTH,
|
||||||
|
});
|
||||||
|
return { effects: [], alternative: { commandKey: 'che_인재탐색', args: {} } };
|
||||||
}
|
}
|
||||||
const color = NATION_COLORS[args.colorType];
|
const color = NATION_COLORS[args.colorType];
|
||||||
|
|
||||||
@@ -123,33 +97,42 @@ export class ActionDefinition<
|
|||||||
const josaGeneralYi = JosaUtil.pick(general.name, '이');
|
const josaGeneralYi = JosaUtil.pick(general.name, '이');
|
||||||
const city = context.city;
|
const city = context.city;
|
||||||
|
|
||||||
context.addLog(`${args.nationName}${josaNationUl} 건국하였습니다.`, {
|
|
||||||
category: LogCategory.USER,
|
|
||||||
format: LogFormat.PLAIN,
|
|
||||||
});
|
|
||||||
context.addLog(`<D><b>${args.nationName}</b></>${josaNationUl} 건국하였습니다.`, {
|
context.addLog(`<D><b>${args.nationName}</b></>${josaNationUl} 건국하였습니다.`, {
|
||||||
category: LogCategory.ACTION,
|
category: LogCategory.ACTION,
|
||||||
format: LogFormat.MONTH,
|
format: LogFormat.MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(`${general.name}${josaGeneralYi} ${city?.name}에 국가를 건설하였습니다.`, {
|
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <G><b>${city?.name}</b></>에 국가를 건설하였습니다.`, {
|
||||||
category: LogCategory.ACTION,
|
category: LogCategory.SUMMARY,
|
||||||
scope: LogScope.SYSTEM,
|
scope: LogScope.SYSTEM,
|
||||||
format: LogFormat.PLAIN,
|
format: LogFormat.MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(`【건국】${args.nationType} ${args.nationName}${josaNationYi} 새로이 등장하였습니다.`, {
|
context.addLog(
|
||||||
|
`<Y><b>【건국】</b></>${getNationTypeDisplayName(args.nationType)} <D><b>${args.nationName}</b></>${josaNationYi} 새로이 등장하였습니다.`,
|
||||||
|
{
|
||||||
|
category: LogCategory.HISTORY,
|
||||||
|
scope: LogScope.SYSTEM,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
context.addLog(`<D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
format: LogFormat.PLAIN,
|
scope: LogScope.GENERAL,
|
||||||
});
|
format: LogFormat.YEAR_MONTH,
|
||||||
context.addLog(`${args.nationName}${josaNationUl} 건국`, {
|
|
||||||
category: LogCategory.HISTORY,
|
|
||||||
format: LogFormat.PLAIN,
|
|
||||||
});
|
|
||||||
context.addLog(`${general.name}${josaGeneralYi} ${args.nationName}${josaNationUl} 건국`, {
|
|
||||||
category: LogCategory.HISTORY,
|
|
||||||
format: LogFormat.PLAIN,
|
|
||||||
});
|
});
|
||||||
|
context.addLog(
|
||||||
|
`<Y>${general.name}</>${josaGeneralYi} <D><b>${args.nationName}</b></>${josaNationUl} 건국`,
|
||||||
|
{
|
||||||
|
category: LogCategory.HISTORY,
|
||||||
|
scope: LogScope.NATION,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
tryApplyUniqueLottery(context, { acquireType: '건국', reason: ACTION_NAME });
|
tryApplyUniqueLottery(context, {
|
||||||
|
acquireType: '건국',
|
||||||
|
reason: ACTION_NAME,
|
||||||
|
nationName: args.nationName,
|
||||||
|
});
|
||||||
|
|
||||||
const effects = [
|
const effects = [
|
||||||
createNationPatchEffect(
|
createNationPatchEffect(
|
||||||
@@ -169,6 +152,7 @@ export class ActionDefinition<
|
|||||||
createCityPatchEffect(
|
createCityPatchEffect(
|
||||||
{
|
{
|
||||||
nationId: nation.id,
|
nationId: nation.id,
|
||||||
|
conflict: {},
|
||||||
},
|
},
|
||||||
cityId
|
cityId
|
||||||
),
|
),
|
||||||
@@ -183,7 +167,14 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 예약 턴 실행은 기본 컨텍스트만 사용한다.
|
// 예약 턴 실행은 기본 컨텍스트만 사용한다.
|
||||||
export const actionContextBuilder = defaultActionContextBuilder;
|
export const actionContextBuilder: ActionContextBuilder = (base, options) => {
|
||||||
|
const init = resolveInitYearMonth(options.world, options.scenarioMeta);
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
currentYearMonth: options.world.currentYear * 12 + options.world.currentMonth - 1,
|
||||||
|
initYearMonth: init.year * 12 + init.month - 1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const commandSpec: GeneralTurnCommandSpec = {
|
export const commandSpec: GeneralTurnCommandSpec = {
|
||||||
key: 'che_건국',
|
key: 'che_건국',
|
||||||
@@ -194,6 +185,6 @@ export const commandSpec: GeneralTurnCommandSpec = {
|
|||||||
nationType: 'string',
|
nationType: 'string',
|
||||||
colorType: 'number',
|
colorType: 'number',
|
||||||
},
|
},
|
||||||
argsSchema: ARGS_SCHEMA,
|
argsSchema: FOUNDING_ARGS_SCHEMA,
|
||||||
createDefinition: (_env: TurnCommandEnv) => new ActionDefinition(),
|
createDefinition: (_env: TurnCommandEnv) => new ActionDefinition(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,11 @@ import type {
|
|||||||
GeneralActionOutcome,
|
GeneralActionOutcome,
|
||||||
GeneralActionResolveContext,
|
GeneralActionResolveContext,
|
||||||
} from '@sammo-ts/logic/actions/engine.js';
|
} from '@sammo-ts/logic/actions/engine.js';
|
||||||
import { createGeneralPatchEffect, createLogEffect } from '@sammo-ts/logic/actions/engine.js';
|
import {
|
||||||
|
createGeneralPatchEffect,
|
||||||
|
createLogEffect,
|
||||||
|
createNationPatchEffect,
|
||||||
|
} from '@sammo-ts/logic/actions/engine.js';
|
||||||
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
||||||
import { JosaUtil } from '@sammo-ts/common';
|
import { JosaUtil } from '@sammo-ts/common';
|
||||||
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||||
@@ -118,7 +122,7 @@ export class ActionDefinition<
|
|||||||
createGeneralPatchEffect(
|
createGeneralPatchEffect(
|
||||||
{
|
{
|
||||||
officerLevel: 1,
|
officerLevel: 1,
|
||||||
experience: Math.floor(lord.experience * 0.7),
|
experience: Math.round(lord.experience * 0.7),
|
||||||
meta: {
|
meta: {
|
||||||
...lord.meta,
|
...lord.meta,
|
||||||
officer_city: 0,
|
officer_city: 0,
|
||||||
@@ -126,7 +130,8 @@ export class ActionDefinition<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
lord.id
|
lord.id
|
||||||
)
|
),
|
||||||
|
createNationPatchEffect({ chiefGeneralId: general.id }, nation.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
return { effects };
|
return { effects };
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
createNationPatchEffect,
|
createNationPatchEffect,
|
||||||
} from '@sammo-ts/logic/actions/engine.js';
|
} from '@sammo-ts/logic/actions/engine.js';
|
||||||
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
||||||
import { z } from 'zod';
|
|
||||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||||
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
||||||
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||||
@@ -29,15 +28,15 @@ import { resolveInitYearMonth } from '@sammo-ts/logic/actions/turn/actionContext
|
|||||||
import type { GeneralTurnCommandSpec } from './index.js';
|
import type { GeneralTurnCommandSpec } from './index.js';
|
||||||
import { parseArgsWithSchema } from '../parseArgs.js';
|
import { parseArgsWithSchema } from '../parseArgs.js';
|
||||||
import { JosaUtil } from '@sammo-ts/common';
|
import { JosaUtil } from '@sammo-ts/common';
|
||||||
|
import {
|
||||||
|
FOUNDING_ARGS_SCHEMA,
|
||||||
|
getNationTypeDisplayName,
|
||||||
|
NATION_COLORS,
|
||||||
|
type FoundingArgs,
|
||||||
|
} from './foundingShared.js';
|
||||||
|
|
||||||
const ACTION_NAME = '무작위 도시 건국';
|
const ACTION_NAME = '무작위 도시 건국';
|
||||||
const ACTION_KEY = 'che_무작위건국';
|
const ACTION_KEY = 'che_무작위건국';
|
||||||
const ARGS_SCHEMA = z.object({
|
|
||||||
nationName: z.string().min(1),
|
|
||||||
nationType: z.string().min(1),
|
|
||||||
colorType: z.number(),
|
|
||||||
});
|
|
||||||
export type FoundingArgs = z.infer<typeof ARGS_SCHEMA>;
|
|
||||||
|
|
||||||
export interface RandomFoundingResolveContext<
|
export interface RandomFoundingResolveContext<
|
||||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||||
@@ -48,41 +47,14 @@ export interface RandomFoundingResolveContext<
|
|||||||
initYearMonth?: number;
|
initYearMonth?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NATION_COLORS = [
|
type InclusiveRandomGenerator = GeneralActionResolveContext['rng'] & {
|
||||||
'#FF0000',
|
nextIntInclusive?: (maxInclusive: number) => number;
|
||||||
'#800000',
|
};
|
||||||
'#A0522D',
|
|
||||||
'#FF6347',
|
const legacyChoiceIndex = (rng: GeneralActionResolveContext['rng'], length: number): number => {
|
||||||
'#FFA500',
|
const inclusive = rng as InclusiveRandomGenerator;
|
||||||
'#FFDAB9',
|
return inclusive.nextIntInclusive ? inclusive.nextIntInclusive(length - 1) : rng.nextInt(0, length);
|
||||||
'#FFD700',
|
};
|
||||||
'#FFFF00',
|
|
||||||
'#7CFC00',
|
|
||||||
'#00FF00',
|
|
||||||
'#808000',
|
|
||||||
'#008000',
|
|
||||||
'#2E8B57',
|
|
||||||
'#008080',
|
|
||||||
'#20B2AA',
|
|
||||||
'#6495ED',
|
|
||||||
'#7FFFD4',
|
|
||||||
'#AFEEEE',
|
|
||||||
'#87CEEB',
|
|
||||||
'#00FFFF',
|
|
||||||
'#00BFFF',
|
|
||||||
'#0000FF',
|
|
||||||
'#000080',
|
|
||||||
'#483D8B',
|
|
||||||
'#7B68EE',
|
|
||||||
'#BA55D3',
|
|
||||||
'#800080',
|
|
||||||
'#FF00FF',
|
|
||||||
'#FFC0CB',
|
|
||||||
'#F5F5DC',
|
|
||||||
'#E0FFFF',
|
|
||||||
'#FFFFFF',
|
|
||||||
'#A9A9A9',
|
|
||||||
];
|
|
||||||
|
|
||||||
export class ActionDefinition<
|
export class ActionDefinition<
|
||||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||||
@@ -94,7 +66,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseArgs(raw: unknown): FoundingArgs | null {
|
parseArgs(raw: unknown): FoundingArgs | null {
|
||||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
return parseArgsWithSchema(FOUNDING_ARGS_SCHEMA, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildMinConstraints(_ctx: ConstraintContext, _args: FoundingArgs): Constraint[] {
|
buildMinConstraints(_ctx: ConstraintContext, _args: FoundingArgs): Constraint[] {
|
||||||
@@ -131,10 +103,6 @@ export class ActionDefinition<
|
|||||||
return { effects: [], alternative: { commandKey: 'che_인재탐색', args: {} } };
|
return { effects: [], alternative: { commandKey: 'che_인재탐색', args: {} } };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.colorType < 0 || args.colorType >= NATION_COLORS.length) {
|
|
||||||
throw new Error('Invalid color type');
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidates = (context.allCities ?? []).filter(
|
const candidates = (context.allCities ?? []).filter(
|
||||||
(city) => city.nationId === 0 && [5, 6].includes(city.level)
|
(city) => city.nationId === 0 && [5, 6].includes(city.level)
|
||||||
);
|
);
|
||||||
@@ -147,7 +115,7 @@ export class ActionDefinition<
|
|||||||
return { effects: [], alternative: { commandKey: 'che_해산', args: {} } };
|
return { effects: [], alternative: { commandKey: 'che_해산', args: {} } };
|
||||||
}
|
}
|
||||||
|
|
||||||
const picked = candidates[context.rng.nextInt(0, candidates.length)]!;
|
const picked = candidates[legacyChoiceIndex(context.rng, candidates.length)]!;
|
||||||
const cityId = picked.id;
|
const cityId = picked.id;
|
||||||
|
|
||||||
const josaNationUl = JosaUtil.pick(args.nationName, '을');
|
const josaNationUl = JosaUtil.pick(args.nationName, '을');
|
||||||
@@ -161,25 +129,33 @@ export class ActionDefinition<
|
|||||||
});
|
});
|
||||||
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <G><b>${picked.name}</b></>에 국가를 건설하였습니다.`, {
|
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <G><b>${picked.name}</b></>에 국가를 건설하였습니다.`, {
|
||||||
scope: LogScope.SYSTEM,
|
scope: LogScope.SYSTEM,
|
||||||
category: LogCategory.ACTION,
|
category: LogCategory.SUMMARY,
|
||||||
|
format: LogFormat.MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(
|
context.addLog(
|
||||||
`<Y><b>【건국】</b></>${args.nationType} <D><b>${args.nationName}</b></>${josaNationYi} 새로이 등장하였습니다.`,
|
`<Y><b>【건국】</b></>${getNationTypeDisplayName(args.nationType)} <D><b>${args.nationName}</b></>${josaNationYi} 새로이 등장하였습니다.`,
|
||||||
{
|
{
|
||||||
scope: LogScope.SYSTEM,
|
scope: LogScope.SYSTEM,
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
context.addLog(`<D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
context.addLog(`<D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
||||||
scope: LogScope.GENERAL,
|
scope: LogScope.GENERAL,
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
||||||
scope: LogScope.NATION,
|
scope: LogScope.NATION,
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
});
|
});
|
||||||
|
|
||||||
tryApplyUniqueLottery(context, { acquireType: '건국', reason: ACTION_NAME });
|
tryApplyUniqueLottery(context, {
|
||||||
|
acquireType: '건국',
|
||||||
|
reason: ACTION_NAME,
|
||||||
|
nationName: args.nationName,
|
||||||
|
});
|
||||||
|
|
||||||
const effects: Array<GeneralActionEffect<TriggerState>> = [
|
const effects: Array<GeneralActionEffect<TriggerState>> = [
|
||||||
createNationPatchEffect(
|
createNationPatchEffect(
|
||||||
@@ -200,6 +176,7 @@ export class ActionDefinition<
|
|||||||
createCityPatchEffect(
|
createCityPatchEffect(
|
||||||
{
|
{
|
||||||
nationId: nation.id,
|
nationId: nation.id,
|
||||||
|
conflict: {},
|
||||||
},
|
},
|
||||||
cityId
|
cityId
|
||||||
),
|
),
|
||||||
@@ -248,6 +225,6 @@ export const commandSpec: GeneralTurnCommandSpec = {
|
|||||||
nationType: 'string',
|
nationType: 'string',
|
||||||
colorType: 'number',
|
colorType: 'number',
|
||||||
},
|
},
|
||||||
argsSchema: ARGS_SCHEMA,
|
argsSchema: FOUNDING_ARGS_SCHEMA,
|
||||||
createDefinition: (_env: TurnCommandEnv) => new ActionDefinition(),
|
createDefinition: (_env: TurnCommandEnv) => new ActionDefinition(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,58 +18,27 @@ import {
|
|||||||
createNationPatchEffect,
|
createNationPatchEffect,
|
||||||
} from '@sammo-ts/logic/actions/engine.js';
|
} from '@sammo-ts/logic/actions/engine.js';
|
||||||
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
|
||||||
import { z } from 'zod';
|
|
||||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||||
import { defaultActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||||
|
import { resolveInitYearMonth } from '@sammo-ts/logic/actions/turn/actionContextHelpers.js';
|
||||||
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
import { tryApplyUniqueLottery } from '@sammo-ts/logic/rewards/uniqueLottery.js';
|
||||||
import type { GeneralTurnCommandSpec } from './index.js';
|
import type { GeneralTurnCommandSpec } from './index.js';
|
||||||
import { parseArgsWithSchema } from '../parseArgs.js';
|
import { parseArgsWithSchema } from '../parseArgs.js';
|
||||||
import { JosaUtil } from '@sammo-ts/common';
|
import { JosaUtil } from '@sammo-ts/common';
|
||||||
|
import {
|
||||||
|
FOUNDING_ARGS_SCHEMA,
|
||||||
|
getNationTypeDisplayName,
|
||||||
|
NATION_COLORS,
|
||||||
|
type FoundingArgs,
|
||||||
|
} from './foundingShared.js';
|
||||||
|
|
||||||
const ACTION_NAME = '건국';
|
const ACTION_NAME = '건국';
|
||||||
const ACTION_KEY = 'cr_건국';
|
const ACTION_KEY = 'cr_건국';
|
||||||
const ARGS_SCHEMA = z.object({
|
interface FoundingResolveContext<TriggerState extends GeneralTriggerState = GeneralTriggerState>
|
||||||
nationName: z.string().min(1),
|
extends GeneralActionResolveContext<TriggerState> {
|
||||||
nationType: z.string().min(1),
|
currentYearMonth?: number;
|
||||||
colorType: z.number(),
|
initYearMonth?: number;
|
||||||
});
|
}
|
||||||
export type FoundingArgs = z.infer<typeof ARGS_SCHEMA>;
|
|
||||||
|
|
||||||
const NATION_COLORS = [
|
|
||||||
'#FF0000',
|
|
||||||
'#800000',
|
|
||||||
'#A0522D',
|
|
||||||
'#FF6347',
|
|
||||||
'#FFA500',
|
|
||||||
'#FFDAB9',
|
|
||||||
'#FFD700',
|
|
||||||
'#FFFF00',
|
|
||||||
'#7CFC00',
|
|
||||||
'#00FF00',
|
|
||||||
'#808000',
|
|
||||||
'#008000',
|
|
||||||
'#2E8B57',
|
|
||||||
'#008080',
|
|
||||||
'#20B2AA',
|
|
||||||
'#6495ED',
|
|
||||||
'#7FFFD4',
|
|
||||||
'#AFEEEE',
|
|
||||||
'#87CEEB',
|
|
||||||
'#00FFFF',
|
|
||||||
'#00BFFF',
|
|
||||||
'#0000FF',
|
|
||||||
'#000080',
|
|
||||||
'#483D8B',
|
|
||||||
'#7B68EE',
|
|
||||||
'#BA55D3',
|
|
||||||
'#800080',
|
|
||||||
'#FF00FF',
|
|
||||||
'#FFC0CB',
|
|
||||||
'#F5F5DC',
|
|
||||||
'#E0FFFF',
|
|
||||||
'#FFFFFF',
|
|
||||||
'#A9A9A9',
|
|
||||||
];
|
|
||||||
|
|
||||||
export class ActionDefinition<
|
export class ActionDefinition<
|
||||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||||
@@ -81,7 +50,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
parseArgs(raw: unknown): FoundingArgs | null {
|
parseArgs(raw: unknown): FoundingArgs | null {
|
||||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
return parseArgsWithSchema(FOUNDING_ARGS_SCHEMA, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
buildMinConstraints(_ctx: ConstraintContext, _args: FoundingArgs): Constraint[] {
|
buildMinConstraints(_ctx: ConstraintContext, _args: FoundingArgs): Constraint[] {
|
||||||
@@ -101,7 +70,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
context: GeneralActionResolveContext<TriggerState>,
|
context: FoundingResolveContext<TriggerState>,
|
||||||
args: FoundingArgs
|
args: FoundingArgs
|
||||||
): GeneralActionOutcome<TriggerState> {
|
): GeneralActionOutcome<TriggerState> {
|
||||||
const general = context.general;
|
const general = context.general;
|
||||||
@@ -111,8 +80,13 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cityId = general.cityId;
|
const cityId = general.cityId;
|
||||||
if (args.colorType < 0 || args.colorType >= NATION_COLORS.length) {
|
if ((context.currentYearMonth ?? 0) <= (context.initYearMonth ?? 0)) {
|
||||||
throw new Error('Invalid color type');
|
context.addLog('다음 턴부터 건국할 수 있습니다.', {
|
||||||
|
scope: LogScope.GENERAL,
|
||||||
|
category: LogCategory.ACTION,
|
||||||
|
format: LogFormat.MONTH,
|
||||||
|
});
|
||||||
|
return { effects: [], alternative: { commandKey: 'che_인재탐색', args: {} } };
|
||||||
}
|
}
|
||||||
const color = NATION_COLORS[args.colorType];
|
const color = NATION_COLORS[args.colorType];
|
||||||
|
|
||||||
@@ -126,26 +100,34 @@ export class ActionDefinition<
|
|||||||
format: LogFormat.MONTH,
|
format: LogFormat.MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <G><b>${cityName}</b></>에 국가를 건설하였습니다.`, {
|
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <G><b>${cityName}</b></>에 국가를 건설하였습니다.`, {
|
||||||
category: LogCategory.ACTION,
|
category: LogCategory.SUMMARY,
|
||||||
scope: LogScope.SYSTEM,
|
scope: LogScope.SYSTEM,
|
||||||
|
format: LogFormat.MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(
|
context.addLog(
|
||||||
`<Y><b>【건국】</b></>${args.nationType} <D><b>${args.nationName}</b></>${josaNationYi} 새로이 등장하였습니다.`,
|
`<Y><b>【건국】</b></>${getNationTypeDisplayName(args.nationType)} <D><b>${args.nationName}</b></>${josaNationYi} 새로이 등장하였습니다.`,
|
||||||
{
|
{
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
scope: LogScope.SYSTEM,
|
scope: LogScope.SYSTEM,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
context.addLog(`<D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
context.addLog(`<D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
scope: LogScope.GENERAL,
|
scope: LogScope.GENERAL,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
});
|
});
|
||||||
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
context.addLog(`<Y>${general.name}</>${josaGeneralYi} <D><b>${args.nationName}</b></>${josaNationUl} 건국`, {
|
||||||
category: LogCategory.HISTORY,
|
category: LogCategory.HISTORY,
|
||||||
scope: LogScope.NATION,
|
scope: LogScope.NATION,
|
||||||
|
format: LogFormat.YEAR_MONTH,
|
||||||
});
|
});
|
||||||
|
|
||||||
tryApplyUniqueLottery(context, { acquireType: '건국', reason: ACTION_NAME });
|
tryApplyUniqueLottery(context, {
|
||||||
|
acquireType: '건국',
|
||||||
|
reason: ACTION_NAME,
|
||||||
|
nationName: args.nationName,
|
||||||
|
});
|
||||||
|
|
||||||
const effects = [
|
const effects = [
|
||||||
createNationPatchEffect(
|
createNationPatchEffect(
|
||||||
@@ -165,6 +147,7 @@ export class ActionDefinition<
|
|||||||
createCityPatchEffect(
|
createCityPatchEffect(
|
||||||
{
|
{
|
||||||
nationId: nation.id,
|
nationId: nation.id,
|
||||||
|
conflict: {},
|
||||||
},
|
},
|
||||||
cityId
|
cityId
|
||||||
),
|
),
|
||||||
@@ -178,7 +161,14 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const actionContextBuilder = defaultActionContextBuilder;
|
export const actionContextBuilder: ActionContextBuilder = (base, options) => {
|
||||||
|
const init = resolveInitYearMonth(options.world, options.scenarioMeta);
|
||||||
|
return {
|
||||||
|
...base,
|
||||||
|
currentYearMonth: options.world.currentYear * 12 + options.world.currentMonth - 1,
|
||||||
|
initYearMonth: init.year * 12 + init.month - 1,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const commandSpec: GeneralTurnCommandSpec = {
|
export const commandSpec: GeneralTurnCommandSpec = {
|
||||||
key: ACTION_KEY,
|
key: ACTION_KEY,
|
||||||
@@ -189,6 +179,6 @@ export const commandSpec: GeneralTurnCommandSpec = {
|
|||||||
nationType: 'string',
|
nationType: 'string',
|
||||||
colorType: 'number',
|
colorType: 'number',
|
||||||
},
|
},
|
||||||
argsSchema: ARGS_SCHEMA,
|
argsSchema: FOUNDING_ARGS_SCHEMA,
|
||||||
createDefinition: (_env: TurnCommandEnv) => new ActionDefinition(),
|
createDefinition: (_env: TurnCommandEnv) => new ActionDefinition(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { NATION_TRAIT_KEYS } from '@sammo-ts/logic/triggers/special/nation/index.js';
|
||||||
|
import { getLegacyStringWidth } from '@sammo-ts/logic/troop/management.js';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const NATION_COLORS = [
|
||||||
|
'#FF0000',
|
||||||
|
'#800000',
|
||||||
|
'#A0522D',
|
||||||
|
'#FF6347',
|
||||||
|
'#FFA500',
|
||||||
|
'#FFDAB9',
|
||||||
|
'#FFD700',
|
||||||
|
'#FFFF00',
|
||||||
|
'#7CFC00',
|
||||||
|
'#00FF00',
|
||||||
|
'#808000',
|
||||||
|
'#008000',
|
||||||
|
'#2E8B57',
|
||||||
|
'#008080',
|
||||||
|
'#20B2AA',
|
||||||
|
'#6495ED',
|
||||||
|
'#7FFFD4',
|
||||||
|
'#AFEEEE',
|
||||||
|
'#87CEEB',
|
||||||
|
'#00FFFF',
|
||||||
|
'#00BFFF',
|
||||||
|
'#0000FF',
|
||||||
|
'#000080',
|
||||||
|
'#483D8B',
|
||||||
|
'#7B68EE',
|
||||||
|
'#BA55D3',
|
||||||
|
'#800080',
|
||||||
|
'#FF00FF',
|
||||||
|
'#FFC0CB',
|
||||||
|
'#F5F5DC',
|
||||||
|
'#E0FFFF',
|
||||||
|
'#FFFFFF',
|
||||||
|
'#A9A9A9',
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const SELECTABLE_NATION_TYPES = new Set<string>(NATION_TRAIT_KEYS.filter((key) => key !== 'che_중립'));
|
||||||
|
|
||||||
|
export const FOUNDING_ARGS_SCHEMA = z.object({
|
||||||
|
nationName: z
|
||||||
|
.string()
|
||||||
|
.min(1)
|
||||||
|
.refine((value) => getLegacyStringWidth(value) <= 18),
|
||||||
|
nationType: z.string().refine((value) => SELECTABLE_NATION_TYPES.has(value)),
|
||||||
|
colorType: z
|
||||||
|
.number()
|
||||||
|
.int()
|
||||||
|
.min(0)
|
||||||
|
.max(NATION_COLORS.length - 1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type FoundingArgs = z.infer<typeof FOUNDING_ARGS_SCHEMA>;
|
||||||
|
|
||||||
|
export const getNationTypeDisplayName = (nationType: string): string =>
|
||||||
|
nationType.startsWith('che_') ? nationType.slice(4) : nationType;
|
||||||
@@ -268,7 +268,7 @@ export const noPenalty = (penaltyKey: string): Constraint => ({
|
|||||||
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const penalty = general.meta.penalty;
|
const penalty = (general as General & { penalty?: unknown }).penalty ?? general.meta.penalty;
|
||||||
if (!penalty || typeof penalty !== 'object' || Array.isArray(penalty)) {
|
if (!penalty || typeof penalty !== 'object' || Array.isArray(penalty)) {
|
||||||
return allow();
|
return allow();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -265,24 +265,16 @@ export const differentDestNation = (): Constraint => ({
|
|||||||
export const reqNationGeneralCount = (min: number): Constraint => ({
|
export const reqNationGeneralCount = (min: number): Constraint => ({
|
||||||
name: 'reqNationGeneralCount',
|
name: 'reqNationGeneralCount',
|
||||||
requires: (ctx) => {
|
requires: (ctx) => {
|
||||||
const reqs: RequirementKey[] = [{ kind: 'generalList' }];
|
const reqs: RequirementKey[] = [];
|
||||||
if (ctx.nationId !== undefined) {
|
if (ctx.nationId !== undefined) {
|
||||||
reqs.push({ kind: 'nation', id: ctx.nationId });
|
reqs.push({ kind: 'nation', id: ctx.nationId });
|
||||||
} else {
|
} else {
|
||||||
reqs.push({ kind: 'general', id: ctx.actorId });
|
reqs.push({ kind: 'general', id: ctx.actorId });
|
||||||
|
reqs.push({ kind: 'nationList' });
|
||||||
}
|
}
|
||||||
return reqs;
|
return reqs;
|
||||||
},
|
},
|
||||||
test: (ctx, view) => {
|
test: (ctx, view) => {
|
||||||
const listReq: RequirementKey = { kind: 'generalList' };
|
|
||||||
if (!view.has(listReq)) {
|
|
||||||
return unknownOrDeny(ctx, [listReq], '장수가 없습니다.');
|
|
||||||
}
|
|
||||||
const generals = view.get(listReq) as General[] | null;
|
|
||||||
if (!generals) {
|
|
||||||
return unknownOrDeny(ctx, [listReq], '장수가 없습니다.');
|
|
||||||
}
|
|
||||||
|
|
||||||
let baseNationId = ctx.nationId;
|
let baseNationId = ctx.nationId;
|
||||||
if (baseNationId === undefined) {
|
if (baseNationId === undefined) {
|
||||||
const general = readGeneral(ctx, view);
|
const general = readGeneral(ctx, view);
|
||||||
@@ -296,7 +288,16 @@ export const reqNationGeneralCount = (min: number): Constraint => ({
|
|||||||
baseNationId = general.nationId;
|
baseNationId = general.nationId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const count = generals.filter((g) => g.nationId === baseNationId).length;
|
const nationReq: RequirementKey =
|
||||||
|
ctx.nationId !== undefined ? { kind: 'nation', id: baseNationId } : { kind: 'nationList' };
|
||||||
|
const nation =
|
||||||
|
ctx.nationId !== undefined
|
||||||
|
? (view.get(nationReq) as Nation | null)
|
||||||
|
: ((view.get(nationReq) as Nation[] | null)?.find((entry) => entry.id === baseNationId) ?? null);
|
||||||
|
if (!nation) {
|
||||||
|
return unknownOrDeny(ctx, [nationReq], '국가 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
const count = typeof nation.meta.gennum === 'number' ? nation.meta.gennum : 0;
|
||||||
if (count >= min) {
|
if (count >= min) {
|
||||||
return allow();
|
return allow();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ describe('Blank Start Scenario', () => {
|
|||||||
|
|
||||||
const FOUNDING_ARGS = {
|
const FOUNDING_ARGS = {
|
||||||
nationName: 'NewChosen',
|
nationName: 'NewChosen',
|
||||||
nationType: 'che_def',
|
nationType: 'che_도적',
|
||||||
colorType: 1,
|
colorType: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -218,6 +218,7 @@ describe('Blank Start Scenario', () => {
|
|||||||
nationId: newNationId,
|
nationId: newNationId,
|
||||||
officerLevel: 1,
|
officerLevel: 1,
|
||||||
} as General;
|
} as General;
|
||||||
|
world.getNation(newNationId)!.meta.gennum = 2;
|
||||||
|
|
||||||
// --- Step 3: Gen 0 performs Founding ---
|
// --- Step 3: Gen 0 performs Founding ---
|
||||||
// First, check name duplicate
|
// First, check name duplicate
|
||||||
@@ -232,7 +233,7 @@ describe('Blank Start Scenario', () => {
|
|||||||
power: 0,
|
power: 0,
|
||||||
level: 1,
|
level: 1,
|
||||||
typeCode: 'che_def',
|
typeCode: 'che_def',
|
||||||
meta: {},
|
meta: { gennum: 2 },
|
||||||
};
|
};
|
||||||
world.snapshot.nations.push(duplicateNation);
|
world.snapshot.nations.push(duplicateNation);
|
||||||
const ctxDuplicate = createConstraintContext(world.getGeneral(gen0.id)!, 189, {
|
const ctxDuplicate = createConstraintContext(world.getGeneral(gen0.id)!, 189, {
|
||||||
@@ -263,6 +264,10 @@ describe('Blank Start Scenario', () => {
|
|||||||
commandKey: 'che_건국',
|
commandKey: 'che_건국',
|
||||||
resolver: foundNationDef,
|
resolver: foundNationDef,
|
||||||
args: FOUNDING_ARGS,
|
args: FOUNDING_ARGS,
|
||||||
|
context: {
|
||||||
|
currentYearMonth: 189 * 12 + 2,
|
||||||
|
initYearMonth: 189 * 12,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -293,7 +298,7 @@ describe('Blank Start Scenario', () => {
|
|||||||
power: 0,
|
power: 0,
|
||||||
level: 0,
|
level: 0,
|
||||||
typeCode: 'che_def',
|
typeCode: 'che_def',
|
||||||
meta: {},
|
meta: { gennum: 2 },
|
||||||
};
|
};
|
||||||
world.snapshot.nations.push(newNation);
|
world.snapshot.nations.push(newNation);
|
||||||
const gen0Idx = world.snapshot.generals.findIndex((g) => g.id === gen0.id);
|
const gen0Idx = world.snapshot.generals.findIndex((g) => g.id === gen0.id);
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ describe('migrated general commands', () => {
|
|||||||
expect(updatedChief.officerLevel).toBe(12);
|
expect(updatedChief.officerLevel).toBe(12);
|
||||||
expect(updatedLord.officerLevel).toBe(1);
|
expect(updatedLord.officerLevel).toBe(1);
|
||||||
expect(updatedLord.experience).toBe(700);
|
expect(updatedLord.experience).toBe(700);
|
||||||
|
expect(world.getNation(nation.id)?.chiefGeneralId).toBe(chief.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('che_선양: 제약 없는 대상에게 선양한다', async () => {
|
it('che_선양: 제약 없는 대상에게 선양한다', async () => {
|
||||||
@@ -316,7 +317,7 @@ describe('migrated general commands', () => {
|
|||||||
capitalCityId: null,
|
capitalCityId: null,
|
||||||
level: 0,
|
level: 0,
|
||||||
typeCode: 'None',
|
typeCode: 'None',
|
||||||
meta: {},
|
meta: { gennum: 2 },
|
||||||
});
|
});
|
||||||
const city = makeCity({ id: 1, nationId: 0, level: 5 });
|
const city = makeCity({ id: 1, nationId: 0, level: 5 });
|
||||||
|
|
||||||
@@ -330,7 +331,11 @@ describe('migrated general commands', () => {
|
|||||||
generalId: lord.id,
|
generalId: lord.id,
|
||||||
commandKey: 'cr_건국',
|
commandKey: 'cr_건국',
|
||||||
resolver: foundNationSpec.createDefinition(SYSTEM_ENV),
|
resolver: foundNationSpec.createDefinition(SYSTEM_ENV),
|
||||||
args: { nationName: '신국', nationType: 'che_def', colorType: 1 },
|
args: { nationName: '신국', nationType: 'che_도적', colorType: 1 },
|
||||||
|
context: {
|
||||||
|
currentYearMonth: 201 * 12,
|
||||||
|
initYearMonth: 200 * 12,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ export const projectCoreDatabaseSnapshot = (rows: {
|
|||||||
dex5: readNumber(meta, 'dex5'),
|
dex5: readNumber(meta, 'dex5'),
|
||||||
specAge: readNumber(meta, 'specage'),
|
specAge: readNumber(meta, 'specage'),
|
||||||
specAge2: readNumber(meta, 'specage2'),
|
specAge2: readNumber(meta, 'specage2'),
|
||||||
|
makeLimit: readNumber(meta, 'makelimit'),
|
||||||
penalty: asRecord(row.penalty),
|
penalty: asRecord(row.penalty),
|
||||||
killTurn: readNumber(meta, 'killturn'),
|
killTurn: readNumber(meta, 'killturn'),
|
||||||
mySet: readNumber(meta, 'myset'),
|
mySet: readNumber(meta, 'myset'),
|
||||||
|
|||||||
@@ -254,6 +254,7 @@ const buildGeneral = (row: Record<string, unknown>, fallbackTurnTime: Date): Tur
|
|||||||
...meta,
|
...meta,
|
||||||
specage: readNumber(row, 'specAge', readNumber(meta, 'specage')),
|
specage: readNumber(row, 'specAge', readNumber(meta, 'specage')),
|
||||||
specage2: readNumber(row, 'specAge2', readNumber(meta, 'specage2')),
|
specage2: readNumber(row, 'specAge2', readNumber(meta, 'specage2')),
|
||||||
|
makelimit: readNumber(row, 'makeLimit', readNumber(meta, 'makelimit')),
|
||||||
killturn: readNumber(row, 'killTurn', readNumber(meta, 'killturn', 24)),
|
killturn: readNumber(row, 'killTurn', readNumber(meta, 'killturn', 24)),
|
||||||
leadership_exp: readNumber(row, 'leadershipExp', readNumber(meta, 'leadership_exp')),
|
leadership_exp: readNumber(row, 'leadershipExp', readNumber(meta, 'leadership_exp')),
|
||||||
strength_exp: readNumber(row, 'strengthExp', readNumber(meta, 'strength_exp')),
|
strength_exp: readNumber(row, 'strengthExp', readNumber(meta, 'strength_exp')),
|
||||||
@@ -573,6 +574,7 @@ const projectWorld = (
|
|||||||
dex5: toDatabaseInt(readNumber(general.meta, 'dex5')),
|
dex5: toDatabaseInt(readNumber(general.meta, 'dex5')),
|
||||||
specAge: toDatabaseInt(readNumber(general.meta, 'specage')),
|
specAge: toDatabaseInt(readNumber(general.meta, 'specage')),
|
||||||
specAge2: toDatabaseInt(readNumber(general.meta, 'specage2')),
|
specAge2: toDatabaseInt(readNumber(general.meta, 'specage2')),
|
||||||
|
makeLimit: toDatabaseInt(readNumber(general.meta, 'makelimit')),
|
||||||
penalty: asRecord(general.penalty),
|
penalty: asRecord(general.penalty),
|
||||||
killTurn: readNumber(general.meta, 'killturn'),
|
killTurn: readNumber(general.meta, 'killturn'),
|
||||||
mySet: readNumber(general.meta, 'myset'),
|
mySet: readNumber(general.meta, 'myset'),
|
||||||
@@ -721,7 +723,10 @@ export const runCoreTurnCommandTrace = async (
|
|||||||
...(request.observe?.generalIds ?? []),
|
...(request.observe?.generalIds ?? []),
|
||||||
]),
|
]),
|
||||||
cityIds: new Set(referenceBefore.cities.map((row) => readNumber(row, 'id'))),
|
cityIds: new Set(referenceBefore.cities.map((row) => readNumber(row, 'id'))),
|
||||||
nationIds: new Set(referenceBefore.nations.map((row) => readNumber(row, 'id'))),
|
nationIds: new Set([
|
||||||
|
...referenceBefore.nations.map((row) => readNumber(row, 'id')),
|
||||||
|
...(request.observe?.nationIds ?? []),
|
||||||
|
]),
|
||||||
initialGeneralIds: new Set(referenceBefore.generals.map((row) => readNumber(row, 'id'))),
|
initialGeneralIds: new Set(referenceBefore.generals.map((row) => readNumber(row, 'id'))),
|
||||||
generalCooldowns: request.observe?.generalCooldowns ?? [],
|
generalCooldowns: request.observe?.generalCooldowns ?? [],
|
||||||
nationCooldowns: request.observe?.nationCooldowns ?? [],
|
nationCooldowns: request.observe?.nationCooldowns ?? [],
|
||||||
|
|||||||
@@ -2240,6 +2240,192 @@ integration('general resignation, retirement, and abdication boundary, state, an
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
type GeneralFoundingRebellionBoundaryCase = {
|
||||||
|
name: string;
|
||||||
|
action: 'che_거병' | 'che_모반시도' | 'che_건국' | 'cr_건국' | 'che_무작위건국';
|
||||||
|
args?: Record<string, unknown>;
|
||||||
|
actorPatch?: Record<string, unknown>;
|
||||||
|
fixturePatches?: FixturePatches;
|
||||||
|
completed: boolean;
|
||||||
|
expectedActionKey?: string;
|
||||||
|
compareLogs?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const foundingArgs = { nationName: '신국', nationType: 'che_도적', colorType: 1 };
|
||||||
|
const wanderingFoundingFixture = (overrides: FixturePatches = {}): FixturePatches => ({
|
||||||
|
...overrides,
|
||||||
|
world: { startYear: 180, initYear: 180, initMonth: 1, year: 181, ...overrides.world },
|
||||||
|
nations: {
|
||||||
|
...overrides.nations,
|
||||||
|
1: { level: 0, capitalCityId: 0, typeCode: 'None', generalCount: 2, ...overrides.nations?.[1] },
|
||||||
|
},
|
||||||
|
cities: {
|
||||||
|
...overrides.cities,
|
||||||
|
3: { nationId: 0, level: 5, ...overrides.cities?.[3] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const generalFoundingRebellionBoundaryCases: GeneralFoundingRebellionBoundaryCase[] = [
|
||||||
|
{
|
||||||
|
name: 'uprising rejects a no-founding penalty from the general column',
|
||||||
|
action: 'che_거병',
|
||||||
|
actorPatch: { nationId: 0, officerLevel: 0, penalty: { noFoundNation: true } },
|
||||||
|
fixturePatches: { world: { startYear: 180, year: 181 } },
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'uprising rejects a remaining join-action cooldown',
|
||||||
|
action: 'che_거병',
|
||||||
|
actorPatch: { nationId: 0, officerLevel: 0, makeLimit: 1 },
|
||||||
|
fixturePatches: { world: { startYear: 180, year: 181 } },
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'uprising trims a duplicate full-width nation name and preserves logs',
|
||||||
|
action: 'che_거병',
|
||||||
|
actorPatch: { name: '가나다라마바사아자차', nationId: 0, officerLevel: 0 },
|
||||||
|
fixturePatches: {
|
||||||
|
world: { startYear: 180, year: 181 },
|
||||||
|
nations: { 1: { name: '가나다라마바사아자차' } },
|
||||||
|
},
|
||||||
|
completed: true,
|
||||||
|
compareLogs: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rebellion rejects an active monarch',
|
||||||
|
action: 'che_모반시도',
|
||||||
|
actorPatch: { officerLevel: 11 },
|
||||||
|
fixturePatches: { generals: { 3: { officerLevel: 12, officerCityId: 3, killTurn: 999_999 } } },
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rebellion rejects an npc monarch',
|
||||||
|
action: 'che_모반시도',
|
||||||
|
actorPatch: { officerLevel: 11 },
|
||||||
|
fixturePatches: { generals: { 3: { officerLevel: 12, officerCityId: 3, killTurn: 0, npcState: 2 } } },
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'rebellion rounds the former monarch experience and transfers the chief cache',
|
||||||
|
action: 'che_모반시도',
|
||||||
|
actorPatch: { officerLevel: 11 },
|
||||||
|
fixturePatches: {
|
||||||
|
generals: { 3: { officerLevel: 12, officerCityId: 3, killTurn: 0, experience: 1001 } },
|
||||||
|
},
|
||||||
|
completed: true,
|
||||||
|
compareLogs: true,
|
||||||
|
},
|
||||||
|
...(['che_건국', 'cr_건국', 'che_무작위건국'] as const).map((action) => ({
|
||||||
|
name: `${action} rejects a fractional color index`,
|
||||||
|
action,
|
||||||
|
args: { ...foundingArgs, colorType: 1.5 },
|
||||||
|
fixturePatches: wanderingFoundingFixture(),
|
||||||
|
completed: false,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: 'founding rejects a nation name wider than eighteen legacy columns',
|
||||||
|
action: 'che_건국',
|
||||||
|
args: { ...foundingArgs, nationName: '가나다라마바사아자차' },
|
||||||
|
fixturePatches: wanderingFoundingFixture(),
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'founding rejects an unavailable nation type',
|
||||||
|
action: 'che_건국',
|
||||||
|
args: { ...foundingArgs, nationType: 'che_없는국가형' },
|
||||||
|
fixturePatches: wanderingFoundingFixture(),
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
...(['che_건국', 'cr_건국', 'che_무작위건국'] as const).map((action) => ({
|
||||||
|
name: `${action} uses the cached nation general count`,
|
||||||
|
action,
|
||||||
|
args: foundingArgs,
|
||||||
|
fixturePatches: wanderingFoundingFixture({ nations: { 1: { generalCount: 1 } } }),
|
||||||
|
completed: false,
|
||||||
|
})),
|
||||||
|
{
|
||||||
|
name: 'founding continues into talent scout during the initial month',
|
||||||
|
action: 'che_건국',
|
||||||
|
args: foundingArgs,
|
||||||
|
fixturePatches: wanderingFoundingFixture({
|
||||||
|
world: { startYear: 180, initYear: 181, initMonth: 1, year: 181 },
|
||||||
|
}),
|
||||||
|
completed: false,
|
||||||
|
expectedActionKey: 'che_인재탐색',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'restricted founding continues into talent scout during the initial month',
|
||||||
|
action: 'cr_건국',
|
||||||
|
args: foundingArgs,
|
||||||
|
fixturePatches: wanderingFoundingFixture({
|
||||||
|
world: { startYear: 180, initYear: 181, initMonth: 1, year: 181 },
|
||||||
|
}),
|
||||||
|
completed: false,
|
||||||
|
expectedActionKey: 'che_인재탐색',
|
||||||
|
},
|
||||||
|
...(['che_건국', 'cr_건국', 'che_무작위건국'] as const).map((action) => ({
|
||||||
|
name: `${action} clears the founding city conflict and preserves success logs`,
|
||||||
|
action,
|
||||||
|
args: foundingArgs,
|
||||||
|
fixturePatches: wanderingFoundingFixture({
|
||||||
|
cities: { 3: { conflict: { 2: 7 } } },
|
||||||
|
randomFoundingCandidateCityIds: action === 'che_무작위건국' ? [3] : undefined,
|
||||||
|
}),
|
||||||
|
completed: true,
|
||||||
|
compareLogs: true,
|
||||||
|
})),
|
||||||
|
];
|
||||||
|
|
||||||
|
integration('general uprising, rebellion, and founding boundary, state, RNG, and log parity', () => {
|
||||||
|
it.each(generalFoundingRebellionBoundaryCases)(
|
||||||
|
'$name',
|
||||||
|
async ({
|
||||||
|
name,
|
||||||
|
action,
|
||||||
|
args,
|
||||||
|
actorPatch,
|
||||||
|
fixturePatches,
|
||||||
|
completed,
|
||||||
|
expectedActionKey,
|
||||||
|
compareLogs,
|
||||||
|
}) => {
|
||||||
|
const request = buildRequest(action, args, actorPatch, fixturePatches);
|
||||||
|
request.setup!.world!.hiddenSeed = `general-founding-rebellion-${name}`;
|
||||||
|
if (action === 'che_거병') {
|
||||||
|
request.observe!.nationIds!.push(3);
|
||||||
|
}
|
||||||
|
request.observe!.includeGlobalHistoryLogs = true;
|
||||||
|
request.observe!.includeNationHistoryLogs = true;
|
||||||
|
const reference = runReferenceTurnCommandTraceRequest(
|
||||||
|
workspaceRoot!,
|
||||||
|
request as unknown as Record<string, unknown>
|
||||||
|
);
|
||||||
|
const core = await runCoreTurnCommandTrace(request, reference.before);
|
||||||
|
const actionKey = expectedActionKey ?? (completed ? action : '휴식');
|
||||||
|
|
||||||
|
expect(reference.execution.outcome).toMatchObject({ completed });
|
||||||
|
expect(core.execution.outcome).toMatchObject({
|
||||||
|
requestedAction: action,
|
||||||
|
actionKey,
|
||||||
|
usedFallback: !completed && actionKey === '휴식',
|
||||||
|
});
|
||||||
|
expect(core.rng).toEqual(reference.rng);
|
||||||
|
expect(
|
||||||
|
compareTurnSnapshotDeltas(reference.before, reference.after, core.before, core.after, {
|
||||||
|
ignoredPathPatterns: ignoredLifecyclePaths,
|
||||||
|
})
|
||||||
|
).toEqual([]);
|
||||||
|
|
||||||
|
if (compareLogs) {
|
||||||
|
expect(semanticLogSignatures(core.after.logs)).toEqual(
|
||||||
|
semanticLogSignatures(addedReferenceLogs(reference.before, reference.after.logs))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
120_000
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
type SabotageProbabilityClampCase = {
|
type SabotageProbabilityClampCase = {
|
||||||
name: string;
|
name: string;
|
||||||
action: 'che_화계' | 'che_선동' | 'che_파괴' | 'che_탈취';
|
action: 'che_화계' | 'che_선동' | 'che_파괴' | 'che_탈취';
|
||||||
|
|||||||
Reference in New Issue
Block a user