국가 대상 명령 입력에 Ref 권유문·원조 한도·피장파장 전략 대기 복원

- 임관·장수대상임관: 정제된 임관 권유문 목록(크게/작게 보기, 940px 기준 2열)과
  임관 대상 국가별 가능 여부(AllowJoinDestNation)를 명령표에 싣는다.
- 물자원조: 작위별 원조 한도표를 보이고 현재 작위를 굵은 밑줄로 표시한다.
- 피장파장: 전략 선택지를 원본 key 대신 전략 이름으로 보이고, 재사용 대기 중인
  전략은 `(불가, N턴)` 붉은색으로 표시한다. 선포·전쟁 국가만 대상 가능으로 둔다.
- 이호경식·급습: Ref 외교 조건으로 대상 국가 가능 여부를 계산한다.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-23 15:24:31 +00:00
co-authored by Claude Opus 5.5
parent 4a50c354fe
commit f74c3322b7
12 changed files with 893 additions and 17 deletions
+18
View File
@@ -53,6 +53,24 @@ describe('turn command argument input', () => {
{ key: 'destNationId', kind: 'select', optionSource: 'nations' },
{ key: 'amountList', kind: 'numberTuple' },
]);
// Ref che_피장파장.vue: '전략 :' select에 자신을 뺀 전략 이름을 보인다.
expect(fields.find((entry) => entry.key === 'che_피장파장')?.fields).toMatchObject([
{ key: 'destNationId', kind: 'select', optionSource: 'nations' },
{
key: 'commandType',
label: '전략',
kind: 'select',
options: [
{ value: 'che_필사즉생', label: '필사즉생' },
{ value: 'che_백성동원', label: '백성동원' },
{ value: 'che_수몰', label: '수몰' },
{ value: 'che_허보', label: '허보' },
{ value: 'che_의병모집', label: '의병모집' },
{ value: 'che_이호경식', label: '이호경식' },
{ value: 'che_급습', label: '급습' },
],
},
]);
expect(
nationFields
+83
View File
@@ -5,6 +5,7 @@ import {
buildRefGeneralTargetOptions,
buildRefNationTargetOptions,
type GeneralTargetSource,
type NationTargetSource,
} from '../src/turns/commandTargets.js';
const general = (overrides: Partial<GeneralTargetSource>): GeneralTargetSource => ({
@@ -323,6 +324,88 @@ describe('Ref nation target guidance', () => {
expect(result.nationTargets.che_불가침파기제의?.[0]?.description).toContain('불가침 12턴');
expect(result.nationTargets.che_불가침파기제의?.at(-1)).toMatchObject({ value: 4, availableNow: false });
});
it('marks counter-strategy targets like Ref exportJSVars notAvailable', () => {
const withDeclaration = [
...nations,
{ ...nations[3]!, id: 5, name: '선포국', diplomacyState: 1, diplomacyTerm: 12 },
{ ...nations[3]!, id: 6, name: '신규선포국', diplomacyState: 1, diplomacyTerm: 11 },
];
const result = buildRefNationTargetOptions({ actorNationId: 1, nations: withDeclaration });
const available = (action: string) =>
result.nationTargets[action]?.filter((entry) => entry.availableNow).map((entry) => entry.value);
// 피장파장·이호경식: AllowDiplomacyBetweenStatus([0, 1])
expect(available('che_피장파장')).toEqual([4, 5, 6]);
expect(available('che_이호경식')).toEqual([4, 5, 6]);
// 급습: AllowDiplomacyWithTerm(1, 12)
expect(available('che_급습')).toEqual([5]);
expect(result.nationTargets.che_급습?.find((entry) => entry.value === 6)?.description).toContain(
'선포 12개월 이상인 상대국에만 가능합니다.'
);
expect(result.nationTargets.che_피장파장?.find((entry) => entry.value === 1)).toMatchObject({
availableNow: false,
});
const exhausted = buildRefNationTargetOptions({
actorNationId: 1,
nations: withDeclaration,
strategyAvailable: false,
});
expect(exhausted.nationTargets.che_피장파장?.every((entry) => entry.availableNow === false)).toBe(true);
expect(exhausted.nationTargets.che_피장파장?.find((entry) => entry.value === 4)?.description).toContain(
'사용할 수 있는 전략이 없습니다.'
);
expect(exhausted.nationTargets.che_이호경식?.some((entry) => entry.availableNow)).toBe(true);
});
it('keeps the Ref nation order for appointment targets and exports purified scout messages', () => {
const joinNations: NationTargetSource[] = [
{ ...nations[0]!, scoutMessage: '<p>어서 오시오</p>' },
{ ...nations[1]!, blockScout: true },
{ ...nations[2]!, name: 'ⓤ태수국' },
{ ...nations[3]!, name: 'ⓞ이민족' },
{ ...nations[3]!, id: 5, name: '초반만원국', generalCount: 3, gennum: 10 },
];
const join = { actorNpcState: 0, relYear: 1, openingPartYear: 3, initialNationGenLimit: 10 };
const result = buildRefNationTargetOptions({ actorNationId: 0, nations: joinNations, join });
expect(result.nationTargets.che_임관?.map((entry) => [entry.value, entry.availableNow])).toEqual([
[1, true],
[2, false],
[3, false],
[4, false],
[5, false],
]);
expect(result.nationTargets.che_임관?.map((entry) => entry.description?.split(' · ')[0])).toEqual([
'현재 임관 가능',
'임관이 금지되어 있습니다.',
'유저장은 태수국에 임관할 수 없습니다.',
'이민족 국가에 임관할 수 없습니다.',
'임관이 제한되고 있습니다.',
]);
// 초반 제한이 끝나면 인원 제한은 없고, NPC(9)는 이민족 국가에 임관할 수 있다.
const later = buildRefNationTargetOptions({
actorNationId: 0,
nations: joinNations,
join: { ...join, actorNpcState: 9, relYear: 3 },
});
expect(later.nationTargets.che_임관?.map((entry) => entry.availableNow)).toEqual([
true,
false,
true,
true,
true,
]);
expect(result.nationScoutMessages).toEqual(
joinNations.map((entry) => ({
nationId: entry.id,
name: entry.name,
color: entry.color,
message: entry.scoutMessage ?? '',
}))
);
// join 정보가 없으면 임관은 기존 공통 국가 목록을 그대로 쓴다.
expect(buildRefNationTargetOptions({ actorNationId: 0, nations }).nationTargets.che_임관).toBeUndefined();
});
});
describe('Ref amount presets', () => {