fix: 커맨드 차등 생명주기와 로그 그래프를 보강
장수 55종과 수뇌 35종의 상태, 로그, 메시지, 예약 턴 비교를 닫습니다. 실제 시나리오 프로필과 대표 PostgreSQL 수명주기, 즉시 외교와 출병 회귀를 추가하고 발견된 Ref 로그 및 생성 장수 저장 차이를 교정합니다.
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
finalizeLogEntry,
|
||||
LogFormat,
|
||||
MESSAGE_MAILBOX_NATIONAL_BASE,
|
||||
orderLegacyActionLoggerFlush,
|
||||
resolveInstantDiplomacyResponse,
|
||||
sendMessage,
|
||||
type GeneralActionEffect,
|
||||
@@ -121,7 +122,7 @@ const persistEffects = async (
|
||||
logs.push(effect.entry);
|
||||
}
|
||||
}
|
||||
await persistLogs(db, logs, year, month, at);
|
||||
await persistLogs(db, orderLegacyActionLoggerFlush(logs), year, month, at);
|
||||
};
|
||||
|
||||
const refreshFrontStates = async (db: DatabaseClient, mapName: string, nationIds: number[]): Promise<number[]> => {
|
||||
|
||||
@@ -13,8 +13,9 @@ import {
|
||||
} from '../../turns/commandTable.js';
|
||||
import { loadMapDefinitionByName } from '../../maps/mapDefinition.js';
|
||||
import {
|
||||
assertReservedTurnActionAvailable,
|
||||
buildEquipmentTradeItemOptions,
|
||||
parseReservedTurnArgs,
|
||||
parseRegisteredTurnArgs,
|
||||
TURN_COMMAND_NATION_COLORS,
|
||||
type TurnCommandInputOptions,
|
||||
} from '../../turns/commandInput.js';
|
||||
@@ -65,7 +66,7 @@ const buildBulkEntrySchema = (turnList: z.ZodType<number[]>) =>
|
||||
|
||||
const parseCommandArgs = async (scope: 'general' | 'nation', action: string, args: unknown) => {
|
||||
try {
|
||||
return await parseReservedTurnArgs(scope, action, args);
|
||||
return await parseRegisteredTurnArgs(scope, action, args);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
@@ -75,6 +76,22 @@ const parseCommandArgs = async (scope: 'general' | 'nation', action: string, arg
|
||||
}
|
||||
};
|
||||
|
||||
const assertScenarioCommandAvailable = async (
|
||||
scope: 'general' | 'nation',
|
||||
action: string,
|
||||
worldState: WorldStateRow
|
||||
): Promise<void> => {
|
||||
try {
|
||||
await assertReservedTurnActionAvailable(scope, action, asRecord(worldState.config).const);
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: error instanceof Error ? error.message : 'Unavailable turn command.',
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const mutateReservedTurns = async <T>(mutation: () => Promise<T>): Promise<T> => {
|
||||
try {
|
||||
return await mutation();
|
||||
@@ -413,6 +430,7 @@ export const turnsRouter = router({
|
||||
const general = await getOwnedGeneral(ctx, input.generalId);
|
||||
const args = await parseCommandArgs('general', input.action, input.args);
|
||||
const worldState = await getReservationWorldState(ctx);
|
||||
await assertScenarioCommandAvailable('general', input.action, worldState);
|
||||
await assertReservedTurnPermission(worldState, general, 'general', input.action, args);
|
||||
|
||||
const snapshot = await mutateReservedTurns(() =>
|
||||
@@ -476,6 +494,7 @@ export const turnsRouter = router({
|
||||
);
|
||||
const worldState = await getReservationWorldState(ctx);
|
||||
for (const update of updates) {
|
||||
await assertScenarioCommandAvailable('general', update.action, worldState);
|
||||
await assertReservedTurnPermission(worldState, general, 'general', update.action, update.args);
|
||||
}
|
||||
const snapshot = await mutateReservedTurns(() =>
|
||||
@@ -515,6 +534,7 @@ export const turnsRouter = router({
|
||||
}
|
||||
const args = await parseCommandArgs('nation', input.action, input.args);
|
||||
const worldState = await getReservationWorldState(ctx);
|
||||
await assertScenarioCommandAvailable('nation', input.action, worldState);
|
||||
await assertReservedTurnPermission(worldState, general, 'nation', input.action, args);
|
||||
|
||||
const snapshot = await mutateReservedTurns(() =>
|
||||
@@ -628,6 +648,7 @@ export const turnsRouter = router({
|
||||
);
|
||||
const worldState = await getReservationWorldState(ctx);
|
||||
for (const update of updates) {
|
||||
await assertScenarioCommandAvailable('nation', update.action, worldState);
|
||||
await assertReservedTurnPermission(worldState, general, 'nation', update.action, update.args);
|
||||
}
|
||||
const snapshot = await mutateReservedTurns(() =>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import {
|
||||
isGeneralTurnCommandKey,
|
||||
isNationTurnCommandKey,
|
||||
loadGeneralTurnCommandSpecs,
|
||||
loadNationTurnCommandSpecs,
|
||||
type GeneralTurnCommandSpec,
|
||||
@@ -9,7 +11,7 @@ import type { ItemModule } from '@sammo-ts/logic/items/types.js';
|
||||
import { resolveLegacyPurchasableItemKeys } from '@sammo-ts/logic/rewards/legacyUniqueItemPool.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { loadTurnCommandProfile } from '@sammo-ts/game-engine/turn/turnCommandProfile.js';
|
||||
import { loadScenarioTurnCommandProfile } from '@sammo-ts/game-engine/turn/turnCommandProfile.js';
|
||||
|
||||
export type TurnCommandOptionValue = string | number;
|
||||
|
||||
@@ -345,22 +347,35 @@ export const buildTurnCommandInputFields = (
|
||||
return Object.entries(properties).map(([key, schema]) => buildField(key, schema, required.has(key)));
|
||||
};
|
||||
|
||||
export const loadTurnCommandSpecs = async () => {
|
||||
const profile = await loadTurnCommandProfile();
|
||||
export const loadTurnCommandSpecs = async (scenarioConst?: unknown) => {
|
||||
const resolution = await loadScenarioTurnCommandProfile({ scenarioConst });
|
||||
const profile = resolution.profile;
|
||||
const [general, nation] = await Promise.all([
|
||||
loadGeneralTurnCommandSpecs(profile.general),
|
||||
loadNationTurnCommandSpecs(profile.nation),
|
||||
]);
|
||||
return { general, nation };
|
||||
return {
|
||||
general,
|
||||
nation,
|
||||
generalGroups: resolution.generalGroups,
|
||||
nationGroups: resolution.nationGroups,
|
||||
};
|
||||
};
|
||||
|
||||
export const parseReservedTurnArgs = async (
|
||||
export const parseRegisteredTurnArgs = async (
|
||||
scope: 'general' | 'nation',
|
||||
action: string,
|
||||
rawArgs: unknown
|
||||
): Promise<Record<string, unknown>> => {
|
||||
const specs = await loadTurnCommandSpecs();
|
||||
const spec = specs[scope].find((entry) => entry.key === action);
|
||||
const specs =
|
||||
scope === 'general'
|
||||
? isGeneralTurnCommandKey(action)
|
||||
? await loadGeneralTurnCommandSpecs([action])
|
||||
: []
|
||||
: isNationTurnCommandKey(action)
|
||||
? await loadNationTurnCommandSpecs([action])
|
||||
: [];
|
||||
const spec = specs[0];
|
||||
if (!spec) {
|
||||
throw new Error(`Unknown ${scope} turn command: ${action}`);
|
||||
}
|
||||
@@ -369,3 +384,24 @@ export const parseReservedTurnArgs = async (
|
||||
}
|
||||
return spec.argsSchema.parse(rawArgs);
|
||||
};
|
||||
|
||||
export const assertReservedTurnActionAvailable = async (
|
||||
scope: 'general' | 'nation',
|
||||
action: string,
|
||||
scenarioConst?: unknown
|
||||
): Promise<void> => {
|
||||
const specs = await loadTurnCommandSpecs(scenarioConst);
|
||||
if (!specs[scope].some((entry) => entry.key === action)) {
|
||||
throw new Error(`Unknown ${scope} turn command: ${action}`);
|
||||
}
|
||||
};
|
||||
|
||||
export const parseReservedTurnArgs = async (
|
||||
scope: 'general' | 'nation',
|
||||
action: string,
|
||||
rawArgs: unknown,
|
||||
scenarioConst?: unknown
|
||||
): Promise<Record<string, unknown>> => {
|
||||
await assertReservedTurnActionAvailable(scope, action, scenarioConst);
|
||||
return parseRegisteredTurnArgs(scope, action, rawArgs);
|
||||
};
|
||||
|
||||
@@ -142,15 +142,6 @@ const REF_GENERAL_COMMAND_GROUPS = [
|
||||
commands: ReadonlyArray<GeneralTurnCommandKey>;
|
||||
}>;
|
||||
|
||||
const REF_GENERAL_CATEGORY_ORDER = new Map<string, number>(
|
||||
REF_GENERAL_COMMAND_GROUPS.map(({ category }, index) => [category, index] as const)
|
||||
);
|
||||
const REF_GENERAL_COMMAND_POSITION = new Map<string, { category: string; index: number }>(
|
||||
REF_GENERAL_COMMAND_GROUPS.flatMap(({ category, commands }) =>
|
||||
commands.map((command, index) => [command, { category, index }] as const)
|
||||
)
|
||||
);
|
||||
|
||||
const REF_NATION_COMMAND_GROUPS = [
|
||||
{
|
||||
category: '휴식',
|
||||
@@ -190,15 +181,6 @@ const REF_NATION_COMMAND_GROUPS = [
|
||||
commands: ReadonlyArray<NationTurnCommandKey>;
|
||||
}>;
|
||||
|
||||
const REF_NATION_CATEGORY_ORDER = new Map<string, number>(
|
||||
REF_NATION_COMMAND_GROUPS.map(({ category }, index) => [category, index] as const)
|
||||
);
|
||||
const REF_NATION_COMMAND_POSITION = new Map<string, { category: string; index: number }>(
|
||||
REF_NATION_COMMAND_GROUPS.flatMap(({ category, commands }) =>
|
||||
commands.map((command, index) => [command, { category, index }] as const)
|
||||
)
|
||||
);
|
||||
|
||||
const INPUT_REQUIREMENT_KINDS = new Set<RequirementKey['kind']>([
|
||||
'destGeneral',
|
||||
'destCity',
|
||||
@@ -746,34 +728,22 @@ const buildGroups = (entries: CommandEntry[], ctx: ConstraintContext, view: Stat
|
||||
}));
|
||||
};
|
||||
|
||||
const projectRefGeneralCommandGroups = (entries: CommandEntry[]): CommandEntry[] =>
|
||||
entries
|
||||
.map((entry, profileIndex) => {
|
||||
const refPosition = REF_GENERAL_COMMAND_POSITION.get(entry.definition.key as GeneralTurnCommandKey);
|
||||
return {
|
||||
entry: refPosition ? { ...entry, category: refPosition.category } : entry,
|
||||
categoryIndex:
|
||||
REF_GENERAL_CATEGORY_ORDER.get(refPosition?.category ?? entry.category) ?? Number.MAX_SAFE_INTEGER,
|
||||
commandIndex: refPosition?.index ?? profileIndex,
|
||||
profileIndex,
|
||||
};
|
||||
})
|
||||
.sort(
|
||||
(left, right) =>
|
||||
left.categoryIndex - right.categoryIndex ||
|
||||
left.commandIndex - right.commandIndex ||
|
||||
left.profileIndex - right.profileIndex
|
||||
)
|
||||
.map(({ entry }) => entry);
|
||||
type CommandGroupLayout = ReadonlyArray<{ category: string; commands: ReadonlyArray<string> }>;
|
||||
|
||||
const projectRefNationCommandGroups = (entries: CommandEntry[]): CommandEntry[] =>
|
||||
entries
|
||||
const projectCommandGroups = (entries: CommandEntry[], layout: CommandGroupLayout): CommandEntry[] => {
|
||||
const categoryOrder = new Map<string, number>(layout.map(({ category }, index) => [category, index] as const));
|
||||
const commandPosition = new Map<string, { category: string; index: number }>(
|
||||
layout.flatMap(({ category, commands }) =>
|
||||
commands.map((command, index) => [command, { category, index }] as const)
|
||||
)
|
||||
);
|
||||
|
||||
return entries
|
||||
.map((entry, profileIndex) => {
|
||||
const refPosition = REF_NATION_COMMAND_POSITION.get(entry.definition.key as NationTurnCommandKey);
|
||||
const refPosition = commandPosition.get(entry.definition.key);
|
||||
return {
|
||||
entry: refPosition ? { ...entry, category: refPosition.category } : entry,
|
||||
categoryIndex:
|
||||
REF_NATION_CATEGORY_ORDER.get(refPosition?.category ?? entry.category) ?? Number.MAX_SAFE_INTEGER,
|
||||
categoryIndex: categoryOrder.get(refPosition?.category ?? entry.category) ?? Number.MAX_SAFE_INTEGER,
|
||||
commandIndex: refPosition?.index ?? profileIndex,
|
||||
profileIndex,
|
||||
};
|
||||
@@ -785,6 +755,7 @@ const projectRefNationCommandGroups = (entries: CommandEntry[]): CommandEntry[]
|
||||
left.profileIndex - right.profileIndex
|
||||
)
|
||||
.map(({ entry }) => entry);
|
||||
};
|
||||
|
||||
export const buildTurnCommandTable = async (options: {
|
||||
worldState: WorldStateRow;
|
||||
@@ -814,7 +785,13 @@ export const buildTurnCommandTable = async (options: {
|
||||
};
|
||||
|
||||
const env = buildCommandEnv(options.worldState);
|
||||
const { general: generalSpecs, nation: nationSpecs } = await loadTurnCommandSpecs();
|
||||
const scenarioConst = asRecord(options.worldState.config).const;
|
||||
const {
|
||||
general: generalSpecs,
|
||||
nation: nationSpecs,
|
||||
generalGroups,
|
||||
nationGroups,
|
||||
} = await loadTurnCommandSpecs(scenarioConst);
|
||||
const generalEntries = buildEntries(env, generalSpecs, {
|
||||
foundingAvailable:
|
||||
options.realNationCount === undefined
|
||||
@@ -824,8 +801,12 @@ export const buildTurnCommandTable = async (options: {
|
||||
const nationEntries = buildEntries(env, nationSpecs);
|
||||
|
||||
return {
|
||||
general: buildGroups(projectRefGeneralCommandGroups(generalEntries), ctx, view),
|
||||
nation: buildGroups(projectRefNationCommandGroups(nationEntries), ctx, view),
|
||||
general: buildGroups(
|
||||
projectCommandGroups(generalEntries, generalGroups ?? REF_GENERAL_COMMAND_GROUPS),
|
||||
ctx,
|
||||
view
|
||||
),
|
||||
nation: buildGroups(projectCommandGroups(nationEntries, nationGroups ?? REF_NATION_COMMAND_GROUPS), ctx, view),
|
||||
inputOptions: options.inputOptions ?? {
|
||||
cities: [],
|
||||
nations: [],
|
||||
@@ -850,7 +831,8 @@ export const evaluateReservedTurnPermission = async (options: {
|
||||
action: string;
|
||||
args: Record<string, unknown>;
|
||||
}): Promise<ConstraintResult> => {
|
||||
const specs = await loadTurnCommandSpecs();
|
||||
const scenarioConst = asRecord(options.worldState.config).const;
|
||||
const specs = await loadTurnCommandSpecs(scenarioConst);
|
||||
const spec = specs[options.scope].find((entry) => entry.key === options.action);
|
||||
if (!spec) {
|
||||
throw new Error(`Unknown ${options.scope} turn command: ${options.action}`);
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
loadGeneralTurnCommandSpecs,
|
||||
loadNationTurnCommandSpecs,
|
||||
} from '@sammo-ts/logic';
|
||||
import { loadScenarioDefinitionById } from '@sammo-ts/game-engine/scenario/scenarioLoader.js';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
@@ -98,6 +99,52 @@ describe('turn command argument input', () => {
|
||||
await expect(parseReservedTurnArgs('general', 'che_포상', {})).rejects.toThrow('Unknown general turn command');
|
||||
});
|
||||
|
||||
it('accepts and rejects reserved commands from the real 904/905/910/912 world config', async () => {
|
||||
const scenarioConsts = Object.fromEntries(
|
||||
await Promise.all(
|
||||
[904, 905, 910, 912].map(async (scenarioId) => [
|
||||
scenarioId,
|
||||
(await loadScenarioDefinitionById(scenarioId)).config.const,
|
||||
])
|
||||
)
|
||||
) as Record<number, Record<string, unknown>>;
|
||||
|
||||
await expect(parseReservedTurnArgs('general', 'che_거병', {}, scenarioConsts[904])).rejects.toThrow(
|
||||
'Unknown general turn command: che_거병'
|
||||
);
|
||||
await expect(
|
||||
parseReservedTurnArgs('nation', 'che_선전포고', { destNationId: 2 }, scenarioConsts[904])
|
||||
).rejects.toThrow('Unknown nation turn command: che_선전포고');
|
||||
|
||||
await expect(
|
||||
parseReservedTurnArgs(
|
||||
'general',
|
||||
'che_무작위건국',
|
||||
{ nationName: '신국', nationType: 'che_도적', colorType: 1 },
|
||||
scenarioConsts[905]
|
||||
)
|
||||
).resolves.toEqual({ nationName: '신국', nationType: 'che_도적', colorType: 1 });
|
||||
await expect(parseReservedTurnArgs('nation', 'che_무작위수도이전', {}, scenarioConsts[905])).resolves.toEqual(
|
||||
{}
|
||||
);
|
||||
await expect(parseReservedTurnArgs('general', 'cr_맹훈련', {}, scenarioConsts[905])).rejects.toThrow(
|
||||
'Unknown general turn command: cr_맹훈련'
|
||||
);
|
||||
|
||||
await expect(parseReservedTurnArgs('general', 'cr_맹훈련', {}, scenarioConsts[910])).resolves.toEqual({});
|
||||
await expect(
|
||||
parseReservedTurnArgs('nation', 'cr_인구이동', { destCityId: 7, amount: 1234 }, scenarioConsts[910])
|
||||
).resolves.toEqual({ destCityId: 7, amount: 1234 });
|
||||
await expect(parseReservedTurnArgs('nation', 'che_무작위수도이전', {}, scenarioConsts[910])).rejects.toThrow(
|
||||
'Unknown nation turn command: che_무작위수도이전'
|
||||
);
|
||||
|
||||
await expect(parseReservedTurnArgs('nation', 'event_대검병연구', {}, scenarioConsts[912])).resolves.toEqual({});
|
||||
await expect(parseReservedTurnArgs('nation', 'cr_인구이동', {}, scenarioConsts[912])).rejects.toThrow(
|
||||
'Unknown nation turn command: cr_인구이동'
|
||||
);
|
||||
});
|
||||
|
||||
it('limits equipment trade options to the Ref default items when a scenario omits allItems', () => {
|
||||
const items = buildEquipmentTradeItemOptions({
|
||||
configConst: {},
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type { CityRow, GeneralRow, NationRow, WorldStateRow } from '../src/context.js';
|
||||
import { loadScenarioDefinitionById } from '@sammo-ts/game-engine/scenario/scenarioLoader.js';
|
||||
import type { GeneralActionModule, MapDefinition, UnitSetDefinition } from '@sammo-ts/logic';
|
||||
import { buildRecruitmentCommandInfo, buildTurnCommandTable } from '../src/turns/commandTable.js';
|
||||
|
||||
const buildWorldState = (joinMode = 'full'): WorldStateRow =>
|
||||
const buildWorldState = (joinMode = 'full', constOverrides: Record<string, unknown> = {}): WorldStateRow =>
|
||||
({
|
||||
id: 1,
|
||||
scenarioCode: 'default',
|
||||
@@ -17,6 +18,7 @@ const buildWorldState = (joinMode = 'full'): WorldStateRow =>
|
||||
baseGold: 1000,
|
||||
baseRice: 1000,
|
||||
develCost: 100,
|
||||
...constOverrides,
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
@@ -190,6 +192,89 @@ describe('buildTurnCommandTable', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('projects scenario-specific command categories instead of the default profile', async () => {
|
||||
const table = await buildTurnCommandTable({
|
||||
worldState: buildWorldState('full', {
|
||||
availableGeneralCommand: {
|
||||
개인: ['휴식'],
|
||||
내정: ['che_물자조달'],
|
||||
군사: ['cr_맹훈련'],
|
||||
},
|
||||
availableChiefCommand: {
|
||||
휴식: ['휴식'],
|
||||
특수: ['cr_인구이동'],
|
||||
연구: ['event_대검병연구', 'event_화륜차연구'],
|
||||
},
|
||||
}),
|
||||
general: buildGeneral(),
|
||||
city: buildCity(),
|
||||
nation: buildNation(),
|
||||
nationGenerals: null,
|
||||
});
|
||||
|
||||
expect(table.general.map(({ category }) => category)).toEqual(['개인', '내정', '군사']);
|
||||
expect(table.general.flatMap(({ values }) => values.map(({ key }) => key))).toEqual([
|
||||
'휴식',
|
||||
'che_물자조달',
|
||||
'cr_맹훈련',
|
||||
]);
|
||||
expect(table.nation.map(({ category }) => category)).toEqual(['휴식', '특수', '연구']);
|
||||
expect(table.nation.flatMap(({ values }) => values.map(({ key }) => key))).toEqual([
|
||||
'휴식',
|
||||
'cr_인구이동',
|
||||
'event_대검병연구',
|
||||
'event_화륜차연구',
|
||||
]);
|
||||
});
|
||||
|
||||
it('projects the real 904/905/910/912 world command profiles into the API table', async () => {
|
||||
const buildScenarioTable = async (scenarioId: number) => {
|
||||
const scenario = await loadScenarioDefinitionById(scenarioId);
|
||||
return buildTurnCommandTable({
|
||||
worldState: buildWorldState('full', scenario.config.const),
|
||||
general: buildGeneral(),
|
||||
city: buildCity(),
|
||||
nation: buildNation(),
|
||||
nationGenerals: null,
|
||||
});
|
||||
};
|
||||
const commandCategory = (groups: Awaited<ReturnType<typeof buildScenarioTable>>['general'], key: string) =>
|
||||
groups.find((group) => group.values.some((command) => command.key === key))?.category;
|
||||
const nationCommandCategory = (groups: Awaited<ReturnType<typeof buildScenarioTable>>['nation'], key: string) =>
|
||||
groups.find((group) => group.values.some((command) => command.key === key))?.category;
|
||||
const [scenario904, scenario905, scenario910, scenario912] = await Promise.all(
|
||||
[904, 905, 910, 912].map(buildScenarioTable)
|
||||
);
|
||||
|
||||
expect(commandCategory(scenario904.general, 'che_물자조달')).toBe('내정');
|
||||
expect(commandCategory(scenario904.general, 'che_거병')).toBeUndefined();
|
||||
expect(nationCommandCategory(scenario904.nation, 'che_피장파장')).toBe('기타');
|
||||
expect(nationCommandCategory(scenario904.nation, 'che_선전포고')).toBeUndefined();
|
||||
expect(nationCommandCategory(scenario904.nation, 'che_부대탈퇴지시')).toBeUndefined();
|
||||
|
||||
expect(commandCategory(scenario905.general, 'che_무작위건국')).toBe('국가');
|
||||
expect(nationCommandCategory(scenario905.nation, 'che_무작위수도이전')).toBe('기타');
|
||||
|
||||
expect(commandCategory(scenario910.general, 'cr_맹훈련')).toBe('군사');
|
||||
expect(commandCategory(scenario910.general, 'cr_건국')).toBe('국가');
|
||||
expect(nationCommandCategory(scenario910.nation, 'cr_인구이동')).toBe('특수');
|
||||
|
||||
expect(nationCommandCategory(scenario912.nation, 'event_대검병연구')).toBe('연구');
|
||||
expect(
|
||||
scenario912.nation.find((group) => group.category === '연구')?.values.map((command) => command.key)
|
||||
).toEqual([
|
||||
'event_대검병연구',
|
||||
'event_극병연구',
|
||||
'event_화시병연구',
|
||||
'event_원융노병연구',
|
||||
'event_산저병연구',
|
||||
'event_음귀병연구',
|
||||
'event_무희연구',
|
||||
'event_상병연구',
|
||||
'event_화륜차연구',
|
||||
]);
|
||||
});
|
||||
|
||||
it('keeps every default general and chief argument command inside the shared frontend field contract', async () => {
|
||||
const table = await buildTurnCommandTable({
|
||||
worldState: buildWorldState(),
|
||||
|
||||
Reference in New Issue
Block a user