merge: 기본 사령턴 목록과 분류를 Ref에 맞춤
This commit is contained in:
@@ -10,6 +10,7 @@ import type {
|
||||
GeneralTurnCommandKey,
|
||||
MapDefinition,
|
||||
Nation,
|
||||
NationTurnCommandKey,
|
||||
NationTurnCommandSpec,
|
||||
RequirementKey,
|
||||
StateView,
|
||||
@@ -150,6 +151,54 @@ const REF_GENERAL_COMMAND_POSITION = new Map<string, { category: string; index:
|
||||
)
|
||||
);
|
||||
|
||||
const REF_NATION_COMMAND_GROUPS = [
|
||||
{
|
||||
category: '휴식',
|
||||
commands: ['휴식'],
|
||||
},
|
||||
{
|
||||
category: '인사',
|
||||
commands: ['che_발령', 'che_포상', 'che_몰수', 'che_부대탈퇴지시'],
|
||||
},
|
||||
{
|
||||
category: '외교',
|
||||
commands: ['che_물자원조', 'che_불가침제의', 'che_선전포고', 'che_종전제의', 'che_불가침파기제의'],
|
||||
},
|
||||
{
|
||||
category: '특수',
|
||||
commands: ['che_초토화', 'che_천도', 'che_증축', 'che_감축'],
|
||||
},
|
||||
{
|
||||
category: '전략',
|
||||
commands: [
|
||||
'che_필사즉생',
|
||||
'che_백성동원',
|
||||
'che_수몰',
|
||||
'che_허보',
|
||||
'che_의병모집',
|
||||
'che_이호경식',
|
||||
'che_급습',
|
||||
'che_피장파장',
|
||||
],
|
||||
},
|
||||
{
|
||||
category: '기타',
|
||||
commands: ['che_국기변경', 'che_국호변경'],
|
||||
},
|
||||
] as const satisfies ReadonlyArray<{
|
||||
category: string;
|
||||
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',
|
||||
@@ -665,6 +714,26 @@ const projectRefGeneralCommandGroups = (entries: CommandEntry[]): CommandEntry[]
|
||||
)
|
||||
.map(({ entry }) => entry);
|
||||
|
||||
const projectRefNationCommandGroups = (entries: CommandEntry[]): CommandEntry[] =>
|
||||
entries
|
||||
.map((entry, profileIndex) => {
|
||||
const refPosition = REF_NATION_COMMAND_POSITION.get(entry.definition.key as NationTurnCommandKey);
|
||||
return {
|
||||
entry: refPosition ? { ...entry, category: refPosition.category } : entry,
|
||||
categoryIndex:
|
||||
REF_NATION_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);
|
||||
|
||||
export const buildTurnCommandTable = async (options: {
|
||||
worldState: WorldStateRow;
|
||||
general: GeneralRow;
|
||||
@@ -697,7 +766,7 @@ export const buildTurnCommandTable = async (options: {
|
||||
|
||||
return {
|
||||
general: buildGroups(projectRefGeneralCommandGroups(generalEntries), ctx, view),
|
||||
nation: buildGroups(nationEntries, ctx, view),
|
||||
nation: buildGroups(projectRefNationCommandGroups(nationEntries), ctx, view),
|
||||
inputOptions: options.inputOptions ?? {
|
||||
cities: [],
|
||||
nations: [],
|
||||
|
||||
@@ -100,7 +100,7 @@ const buildNation = (): NationRow =>
|
||||
}) as unknown as NationRow;
|
||||
|
||||
describe('buildTurnCommandTable', () => {
|
||||
it('projects the main reserved-turn categories and command order from Ref', async () => {
|
||||
it('projects the general and chief reserved-turn categories and command order from Ref', async () => {
|
||||
const table = await buildTurnCommandTable({
|
||||
worldState: buildWorldState(),
|
||||
general: buildGeneral(),
|
||||
@@ -140,6 +140,36 @@ describe('buildTurnCommandTable', () => {
|
||||
계략: ['che_선동', 'che_탈취', 'che_파괴', 'che_화계'],
|
||||
국가: ['che_증여', 'che_헌납', 'che_물자조달', 'che_하야', 'che_거병', 'che_건국', 'che_선양', 'che_해산'],
|
||||
});
|
||||
expect(table.nation.map(({ category }) => category)).toEqual(['휴식', '인사', '외교', '특수', '전략', '기타']);
|
||||
expect(
|
||||
Object.fromEntries(table.nation.map(({ category, values }) => [category, values.map(({ key }) => key)]))
|
||||
).toEqual({
|
||||
휴식: ['휴식'],
|
||||
인사: ['che_발령', 'che_포상', 'che_몰수', 'che_부대탈퇴지시'],
|
||||
외교: ['che_물자원조', 'che_불가침제의', 'che_선전포고', 'che_종전제의', 'che_불가침파기제의'],
|
||||
특수: ['che_초토화', 'che_천도', 'che_증축', 'che_감축'],
|
||||
전략: [
|
||||
'che_필사즉생',
|
||||
'che_백성동원',
|
||||
'che_수몰',
|
||||
'che_허보',
|
||||
'che_의병모집',
|
||||
'che_이호경식',
|
||||
'che_급습',
|
||||
'che_피장파장',
|
||||
],
|
||||
기타: ['che_국기변경', 'che_국호변경'],
|
||||
});
|
||||
expect(
|
||||
Object.fromEntries(table.nation.map(({ category, values }) => [category, values.map(({ name }) => name)]))
|
||||
).toEqual({
|
||||
휴식: ['휴식'],
|
||||
인사: ['발령', '포상', '몰수', '부대 탈퇴 지시'],
|
||||
외교: ['원조', '불가침 제의', '선전포고', '종전 제의', '불가침 파기 제의'],
|
||||
특수: ['초토화', '천도', '증축', '감축'],
|
||||
전략: ['필사즉생', '백성동원', '수몰', '허보', '의병모집', '이호경식', '급습', '피장파장'],
|
||||
기타: ['국기변경', '국호변경'],
|
||||
});
|
||||
});
|
||||
|
||||
it('projects the Ref availability boundaries for force move, retirement, and resignation', async () => {
|
||||
|
||||
@@ -35,7 +35,32 @@ const GENERAL_REF_EDITOR_ACTIONS = [
|
||||
] as const;
|
||||
const GENERAL_REF_STRATEGY_ACTIONS = ['che_선동', 'che_탈취', 'che_파괴', 'che_화계'] as const;
|
||||
const GENERAL_REF_STRATEGY_ACTION_SET = new Set<string>(GENERAL_REF_STRATEGY_ACTIONS);
|
||||
const NATION_REF_EDITOR_ACTIONS = ['che_포상', 'che_발령', 'che_증축', 'che_필사즉생'] as const;
|
||||
const NATION_REF_EDITOR_ACTIONS = [
|
||||
'휴식',
|
||||
'che_발령',
|
||||
'che_포상',
|
||||
'che_몰수',
|
||||
'che_부대탈퇴지시',
|
||||
'che_물자원조',
|
||||
'che_불가침제의',
|
||||
'che_선전포고',
|
||||
'che_종전제의',
|
||||
'che_불가침파기제의',
|
||||
'che_초토화',
|
||||
'che_천도',
|
||||
'che_증축',
|
||||
'che_감축',
|
||||
'che_필사즉생',
|
||||
'che_백성동원',
|
||||
'che_수몰',
|
||||
'che_허보',
|
||||
'che_의병모집',
|
||||
'che_이호경식',
|
||||
'che_급습',
|
||||
'che_피장파장',
|
||||
'che_국기변경',
|
||||
'che_국호변경',
|
||||
] as const;
|
||||
|
||||
describe('default turn command profile AI coverage', () => {
|
||||
it('loads every action selected directly by the general and nation AI', async () => {
|
||||
@@ -52,6 +77,6 @@ describe('default turn command profile AI coverage', () => {
|
||||
expect(profile.general.filter((action) => GENERAL_REF_STRATEGY_ACTION_SET.has(action))).toEqual(
|
||||
GENERAL_REF_STRATEGY_ACTIONS
|
||||
);
|
||||
expect(profile.nation).toEqual(expect.arrayContaining([...NATION_REF_EDITOR_ACTIONS]));
|
||||
expect(profile.nation).toEqual(NATION_REF_EDITOR_ACTIONS);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -231,6 +231,66 @@ const commandTable = {
|
||||
],
|
||||
inputOptions,
|
||||
};
|
||||
const buildSimpleCommand = (key: string, name: string) => ({
|
||||
key,
|
||||
name,
|
||||
reqArg: false,
|
||||
possible: true,
|
||||
status: 'available',
|
||||
inputFields: [],
|
||||
});
|
||||
const refChiefCommandTable = {
|
||||
general: [],
|
||||
nation: [
|
||||
{ category: '휴식', values: [buildSimpleCommand('휴식', '휴식')] },
|
||||
{
|
||||
category: '인사',
|
||||
values: [
|
||||
buildSimpleCommand('che_발령', '발령'),
|
||||
buildSimpleCommand('che_포상', '포상'),
|
||||
buildSimpleCommand('che_몰수', '몰수'),
|
||||
buildSimpleCommand('che_부대탈퇴지시', '부대 탈퇴 지시'),
|
||||
],
|
||||
},
|
||||
{
|
||||
category: '외교',
|
||||
values: [
|
||||
buildSimpleCommand('che_물자원조', '원조'),
|
||||
buildSimpleCommand('che_불가침제의', '불가침 제의'),
|
||||
buildSimpleCommand('che_선전포고', '선전포고'),
|
||||
buildSimpleCommand('che_종전제의', '종전 제의'),
|
||||
buildSimpleCommand('che_불가침파기제의', '불가침 파기 제의'),
|
||||
],
|
||||
},
|
||||
{
|
||||
category: '특수',
|
||||
values: [
|
||||
buildSimpleCommand('che_초토화', '초토화'),
|
||||
buildSimpleCommand('che_천도', '천도'),
|
||||
buildSimpleCommand('che_증축', '증축'),
|
||||
buildSimpleCommand('che_감축', '감축'),
|
||||
],
|
||||
},
|
||||
{
|
||||
category: '전략',
|
||||
values: [
|
||||
buildSimpleCommand('che_필사즉생', '필사즉생'),
|
||||
buildSimpleCommand('che_백성동원', '백성동원'),
|
||||
buildSimpleCommand('che_수몰', '수몰'),
|
||||
buildSimpleCommand('che_허보', '허보'),
|
||||
buildSimpleCommand('che_의병모집', '의병모집'),
|
||||
buildSimpleCommand('che_이호경식', '이호경식'),
|
||||
buildSimpleCommand('che_급습', '급습'),
|
||||
buildSimpleCommand('che_피장파장', '피장파장'),
|
||||
],
|
||||
},
|
||||
{
|
||||
category: '기타',
|
||||
values: [buildSimpleCommand('che_국기변경', '국기변경'), buildSimpleCommand('che_국호변경', '국호변경')],
|
||||
},
|
||||
],
|
||||
inputOptions,
|
||||
};
|
||||
const generalContext = {
|
||||
general: {
|
||||
id: 1,
|
||||
@@ -618,6 +678,51 @@ test('reserves force move, retirement, and resignation from the user command pic
|
||||
await picker.screenshot({ path: test.info().outputPath('special-user-commands-mobile-500.png') });
|
||||
});
|
||||
|
||||
test('shows every Ref chief command in the exact category and command order', async ({ page }) => {
|
||||
await install(page, false, refChiefCommandTable);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
await page.goto('/che/chief-center');
|
||||
|
||||
const editor = page.locator('[data-command-scope="nation"]');
|
||||
await editor.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
|
||||
const picker = page.getByTestId('command-picker');
|
||||
const expected = {
|
||||
휴식: ['휴식'],
|
||||
인사: ['발령', '포상', '몰수', '부대 탈퇴 지시'],
|
||||
외교: ['원조', '불가침 제의', '선전포고', '종전 제의', '불가침 파기 제의'],
|
||||
특수: ['초토화', '천도', '증축', '감축'],
|
||||
전략: ['필사즉생', '백성동원', '수몰', '허보', '의병모집', '이호경식', '급습', '피장파장'],
|
||||
기타: ['국기변경', '국호변경'],
|
||||
} as const;
|
||||
|
||||
await expect(picker.locator('.category-btn')).toHaveText(Object.keys(expected));
|
||||
for (const [category, commands] of Object.entries(expected)) {
|
||||
await picker.locator('.category-btn').filter({ hasText: category }).click();
|
||||
await expect(picker.locator('.command-grid .command-item')).toHaveText([...commands]);
|
||||
}
|
||||
const rename = picker.getByRole('button', { name: '국호변경', exact: true });
|
||||
await rename.hover();
|
||||
await rename.focus();
|
||||
await expect(rename).toBeFocused();
|
||||
await picker.screenshot({ path: test.info().outputPath('ref-chief-command-list-desktop-1200.png') });
|
||||
|
||||
await picker.getByRole('button', { name: '명령 입력 닫기', exact: true }).click();
|
||||
await page.setViewportSize({ width: 500, height: 900 });
|
||||
const mobileEditor = page.locator('[data-command-scope="nation"]:visible');
|
||||
await mobileEditor.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
|
||||
const mobilePicker = page.locator('[data-testid="command-picker"]:visible');
|
||||
await expect(mobilePicker.locator('.category-btn')).toHaveText(Object.keys(expected));
|
||||
const mobileGeometry = await mobilePicker.evaluate((element) => ({
|
||||
width: element.getBoundingClientRect().width,
|
||||
horizontalOverflow: element.scrollWidth - element.clientWidth,
|
||||
categoryColumns: getComputedStyle(element.querySelector<HTMLElement>('.category-list')!).gridTemplateColumns,
|
||||
}));
|
||||
expect(mobileGeometry.width).toBeLessThanOrEqual(500);
|
||||
expect(mobileGeometry.horizontalOverflow).toBeLessThanOrEqual(0);
|
||||
expect(mobileGeometry.categoryColumns.split(' ')).toHaveLength(3);
|
||||
await mobilePicker.screenshot({ path: test.info().outputPath('ref-chief-command-list-mobile-500.png') });
|
||||
});
|
||||
|
||||
test('enters general and nation command arguments and sends exact values', async ({ page }) => {
|
||||
const requests = await install(page);
|
||||
await page.setViewportSize({ width: 1200, height: 900 });
|
||||
|
||||
@@ -46,21 +46,28 @@
|
||||
],
|
||||
"nation": [
|
||||
"휴식",
|
||||
"che_발령",
|
||||
"che_포상",
|
||||
"che_몰수",
|
||||
"che_부대탈퇴지시",
|
||||
"che_발령",
|
||||
"che_물자원조",
|
||||
"che_불가침제의",
|
||||
"che_선전포고",
|
||||
"che_종전제의",
|
||||
"che_불가침파기제의",
|
||||
"che_초토화",
|
||||
"che_천도",
|
||||
"che_증축",
|
||||
"che_선전포고",
|
||||
"che_불가침제의",
|
||||
"che_불가침파기제의",
|
||||
"che_의병모집",
|
||||
"che_허보",
|
||||
"che_감축",
|
||||
"che_필사즉생",
|
||||
"che_백성동원",
|
||||
"che_이호경식",
|
||||
"che_수몰",
|
||||
"che_급습"
|
||||
"che_허보",
|
||||
"che_의병모집",
|
||||
"che_이호경식",
|
||||
"che_급습",
|
||||
"che_피장파장",
|
||||
"che_국기변경",
|
||||
"che_국호변경"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user