fix: 선택 불가 국가 성향을 입력 목록에서 제외

Ref의 availableNationType 13개를 공용 목록으로 정의하고 건국, NPC/AI 건국, 전투 시뮬레이터 입력과 검증에 적용한다.
This commit is contained in:
2026-08-17 10:39:36 +00:00
parent bc356c0c84
commit 0b8add7f13
12 changed files with 171 additions and 25 deletions
@@ -58,7 +58,7 @@ const simulatorOptions = {
{ id: 200, name: '궁병', armType: 2 },
],
},
nationTypes: [{ key: 'che_중립', name: '중립', info: '특별한 효과 없음' }],
nationTypes: [{ key: 'che_도적', name: '도적', info: '금 수입 증가, 쌀 수입 감소' }],
eventDomesticTraits: [{ key: 'che_event_신산', name: '신산', info: '계략 강화' }],
warTraits: [{ key: 'che_필살', name: '필살', info: '필살 확률 증가' }],
personalities: [{ key: 'che_대담', name: '대담', info: '공격적인 성격' }],
@@ -352,6 +352,10 @@ test('operates independent/game presets, imports my general, and renders battle
await page.setViewportSize({ width: 1280, height: 900 });
await gotoSimulator(page);
const nationTypeSelects = page.locator('[data-parity-id="attacker-nation"] select').first();
await expect(nationTypeSelects).toHaveValue('che_도적');
await expect(nationTypeSelects.locator('option[value="che_중립"]')).toHaveCount(0);
const notice = page.getByLabel('시뮬레이터 데이터 안내');
const noticeRect = await notice.boundingBox();
expect(noticeRect?.width).toBeLessThan(100);
+56 -1
View File
@@ -54,7 +54,7 @@ const inputOptions = {
],
crewTypes: [{ value: 1100, label: '보병' }],
armTypes: [{ value: 1, label: '보병' }],
nationTypes: [{ value: 'che_중립', label: '중립' }],
nationTypes: [{ value: 'che_도적', label: '도적', description: '금 수입 증가, 쌀 수입 감소' }],
colors: [{ value: 0, label: '색상 1', color: '#ff0000' }],
items: { horse: [{ value: 'None', label: '판매/해제' }] },
recruitment: {
@@ -567,6 +567,61 @@ test('renders and accepts every Ref strategy command at mobile width', async ({
await picker.screenshot({ path: test.info().outputPath('all-strategy-commands-mobile.png') });
});
test('defaults founding to a Ref-selectable nation trait without exposing the neutral storage trait', async ({
page,
}) => {
const foundingCommandTable = {
general: [
{
category: '국가',
values: [
{
key: 'che_건국',
name: '건국',
reqArg: true,
possible: true,
status: 'needsInput',
inputFields: [
{ key: 'nationName', label: '국가명', kind: 'text', required: true, min: 1, max: 18 },
{
key: 'nationType',
label: '국가 성향',
kind: 'select',
required: true,
optionSource: 'nationTypes',
},
{
key: 'colorType',
label: '국기 색상',
kind: 'select',
required: true,
optionSource: 'colors',
},
],
},
],
},
],
nation: [],
inputOptions,
};
await install(page, false, foundingCommandTable);
await page.setViewportSize({ width: 1200, height: 900 });
await page.goto('/');
await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click();
const picker = page.getByTestId('command-picker');
await picker.getByRole('button', { name: '국가', exact: true }).click();
await picker.getByRole('button', { name: '건국', exact: true }).click();
const nationType = picker.getByLabel('국가 성향');
await expect(nationType).toHaveValue('che_도적');
await expect(nationType.locator('option[value="che_중립"]')).toHaveCount(0);
await expect(nationType.locator('option')).toHaveText(['도적']);
await nationType.focus();
await expect(nationType).toBeFocused();
await picker.screenshot({ path: test.info().outputPath('founding-selectable-nation-trait-desktop-1200.png') });
});
test('reserves force move, retirement, and resignation from the user command picker', async ({ page }) => {
const specialCommandTable = {
general: [
@@ -254,6 +254,11 @@ const toExportedGeneral = (general: GeneralDraft): GeneralExport => ({
inheritBuff: { ...general.inheritBuff },
});
const resolveAvailableNationType = (candidate?: string | null): string => {
const available = options.value?.nationTypes ?? [];
return available.some((entry) => entry.key === candidate) ? (candidate as string) : (available[0]?.key ?? '');
};
const initializeDefaults = async () => {
loading.value = true;
error.value = null;
@@ -267,9 +272,9 @@ const initializeDefaults = async () => {
repeatCnt.value = 1;
seed.value = '';
const nationTypeDefault = context.nationTypes[0]?.key ?? 'che_중립';
attackerNation.type = me?.nation?.typeCode ?? nationTypeDefault;
defenderNation.type = me?.nation?.typeCode ?? nationTypeDefault;
const nationTypeDefault = resolveAvailableNationType(me?.nation?.typeCode);
attackerNation.type = nationTypeDefault;
defenderNation.type = nationTypeDefault;
attackerNation.level = me?.nation?.level ?? 0;
defenderNation.level = me?.nation?.level ?? 0;
@@ -302,10 +307,10 @@ const applyGameEnvironment = () => {
return;
}
const me = gameDefaults.value;
const nationTypeDefault = options.value.nationTypes[0]?.key ?? 'che_중립';
const nationTypeDefault = resolveAvailableNationType(me?.nation?.typeCode);
year.value = options.value.world.currentYear;
month.value = options.value.world.currentMonth;
attackerNation.type = me?.nation?.typeCode ?? nationTypeDefault;
attackerNation.type = nationTypeDefault;
defenderNation.type = attackerNation.type;
attackerNation.level = me?.nation?.level ?? 0;
defenderNation.level = attackerNation.level;
@@ -324,7 +329,7 @@ const applyIndependentEnvironment = () => {
if (!options.value) {
return;
}
const nationTypeDefault = options.value.nationTypes[0]?.key ?? 'che_중립';
const nationTypeDefault = resolveAvailableNationType();
year.value = options.value.world.startYear;
month.value = 1;
seed.value = '';
@@ -742,13 +747,13 @@ const importBattle = (data: BattleExport) => {
month.value = data.month;
repeatCnt.value = data.repeatCnt;
attackerNation.type = data.attackerNation.type;
attackerNation.type = resolveAvailableNationType(data.attackerNation.type);
attackerNation.level = data.attackerNation.level;
attackerNation.tech = Math.floor(data.attackerNation.tech / 1000);
attackerNation.isCapital = data.attackerNation.capital === 1;
attackerCity.level = data.attackerCity.level;
defenderNation.type = data.defenderNation.type;
defenderNation.type = resolveAvailableNationType(data.defenderNation.type);
defenderNation.level = data.defenderNation.level;
defenderNation.tech = Math.floor(data.defenderNation.tech / 1000);
defenderNation.isCapital = data.defenderNation.capital === 3;