feat: 사용자 군주 자동 선전포고를 분리한다

This commit is contained in:
2026-08-24 08:14:53 +00:00
parent 1e79611089
commit 4efc9a7679
10 changed files with 54 additions and 6 deletions
+2
View File
@@ -56,6 +56,7 @@ const zGeneralSettings = z.object({
use_treatment: z.number().int().optional(), use_treatment: z.number().int().optional(),
use_auto_nation_turn: z.number().int().optional(), use_auto_nation_turn: z.number().int().optional(),
use_auto_nation_diplomacy: z.number().int().min(0).max(1).optional(), use_auto_nation_diplomacy: z.number().int().min(0).max(1).optional(),
use_auto_nation_war: z.number().int().min(0).max(1).optional(),
use_auto_nation_promotion: z.number().int().min(0).max(1).optional(), use_auto_nation_promotion: z.number().int().min(0).max(1).optional(),
use_auto_nation_finance: z.number().int().min(0).max(1).optional(), use_auto_nation_finance: z.number().int().min(0).max(1).optional(),
use_auto_nation_capital: z.number().int().min(0).max(1).optional(), use_auto_nation_capital: z.number().int().min(0).max(1).optional(),
@@ -218,6 +219,7 @@ const resolveUserSettings = (meta: Record<string, unknown>) => {
// Ref가 NPC 군주에게만 수행하던 국가 운영은 사용자 군주에게 opt-in이다. // Ref가 NPC 군주에게만 수행하던 국가 운영은 사용자 군주에게 opt-in이다.
// 누락된 값은 신규 게임과 기존 장수 모두 안전한 기본값(사용 안함)으로 해석한다. // 누락된 값은 신규 게임과 기존 장수 모두 안전한 기본값(사용 안함)으로 해석한다.
use_auto_nation_diplomacy: readNumber(readSetting('use_auto_nation_diplomacy'), 0), use_auto_nation_diplomacy: readNumber(readSetting('use_auto_nation_diplomacy'), 0),
use_auto_nation_war: readNumber(readSetting('use_auto_nation_war'), 0),
use_auto_nation_promotion: readNumber(readSetting('use_auto_nation_promotion'), 0), use_auto_nation_promotion: readNumber(readSetting('use_auto_nation_promotion'), 0),
use_auto_nation_finance: readNumber(readSetting('use_auto_nation_finance'), 0), use_auto_nation_finance: readNumber(readSetting('use_auto_nation_finance'), 0),
use_auto_nation_capital: readNumber(readSetting('use_auto_nation_capital'), 0), use_auto_nation_capital: readNumber(readSetting('use_auto_nation_capital'), 0),
@@ -585,6 +585,7 @@ describe('in-game my information ownership', () => {
use_treatment: 21, use_treatment: 21,
use_auto_nation_turn: 1, use_auto_nation_turn: 1,
use_auto_nation_diplomacy: 0, use_auto_nation_diplomacy: 0,
use_auto_nation_war: 0,
use_auto_nation_promotion: 0, use_auto_nation_promotion: 0,
use_auto_nation_finance: 0, use_auto_nation_finance: 0,
use_auto_nation_capital: 0, use_auto_nation_capital: 0,
@@ -600,6 +601,16 @@ describe('in-game my information ownership', () => {
expect(fixture.db.general.update).not.toHaveBeenCalled(); expect(fixture.db.general.update).not.toHaveBeenCalled();
}); });
it('rejects an invalid automatic war setting before dispatching it to ENGINE', async () => {
const requestCommand = vi.fn(async () => ({ type: 'setMySetting', ok: true, generalId: 7 }));
const fixture = createContext({ requestCommand });
await expect(
appRouter.createCaller(fixture.context).general.setMySetting({ use_auto_nation_war: 2 })
).rejects.toMatchObject({ code: 'BAD_REQUEST' });
expect(requestCommand).not.toHaveBeenCalled();
});
it('sends settings directly to ENGINE without creating an API input event', async () => { it('sends settings directly to ENGINE without creating an API input event', async () => {
const transaction = vi.fn(async () => { const transaction = vi.fn(async () => {
throw new Error('API transaction must not run'); throw new Error('API transaction must not run');
+3 -2
View File
@@ -61,10 +61,11 @@ export const AVAILABLE_INSTANT_TURN: Record<string, boolean> = {
NPC전방발령: true, NPC전방발령: true,
}; };
export type UserRulerAutomationFeature = 'diplomacy' | 'promotion' | 'finance' | 'capital'; export type UserRulerAutomationFeature = 'diplomacy' | 'war' | 'promotion' | 'finance' | 'capital';
const USER_RULER_AUTOMATION_META_KEY = { const USER_RULER_AUTOMATION_META_KEY = {
diplomacy: 'use_auto_nation_diplomacy', diplomacy: 'use_auto_nation_diplomacy',
war: 'use_auto_nation_war',
promotion: 'use_auto_nation_promotion', promotion: 'use_auto_nation_promotion',
finance: 'use_auto_nation_finance', finance: 'use_auto_nation_finance',
capital: 'use_auto_nation_capital', capital: 'use_auto_nation_capital',
@@ -72,7 +73,7 @@ const USER_RULER_AUTOMATION_META_KEY = {
const USER_RULER_ACTION_FEATURE: Readonly<Record<string, UserRulerAutomationFeature>> = { const USER_RULER_ACTION_FEATURE: Readonly<Record<string, UserRulerAutomationFeature>> = {
: 'diplomacy', : 'diplomacy',
: 'diplomacy', : 'war',
: 'capital', : 'capital',
}; };
@@ -137,6 +137,7 @@ const zSetMySetting = z.object({
use_treatment: z.number().int().optional(), use_treatment: z.number().int().optional(),
use_auto_nation_turn: z.number().int().optional(), use_auto_nation_turn: z.number().int().optional(),
use_auto_nation_diplomacy: z.number().int().optional(), use_auto_nation_diplomacy: z.number().int().optional(),
use_auto_nation_war: z.number().int().optional(),
use_auto_nation_promotion: z.number().int().optional(), use_auto_nation_promotion: z.number().int().optional(),
use_auto_nation_finance: z.number().int().optional(), use_auto_nation_finance: z.number().int().optional(),
use_auto_nation_capital: z.number().int().optional(), use_auto_nation_capital: z.number().int().optional(),
@@ -1779,6 +1779,7 @@ async function handleSetMySetting(
} }
for (const key of [ for (const key of [
'use_auto_nation_diplomacy', 'use_auto_nation_diplomacy',
'use_auto_nation_war',
'use_auto_nation_promotion', 'use_auto_nation_promotion',
'use_auto_nation_finance', 'use_auto_nation_finance',
'use_auto_nation_capital', 'use_auto_nation_capital',
@@ -718,6 +718,7 @@ describe('legacy NPC user-chief promotion parity', () => {
it('keeps user-ruler duties individually disabled until each setting is enabled', () => { it('keeps user-ruler duties individually disabled until each setting is enabled', () => {
const ruler = makePromotionGeneral({ id: 1, officerLevel: 12, npcState: 0, meta: { killturn: 0 } }); const ruler = makePromotionGeneral({ id: 1, officerLevel: 12, npcState: 0, meta: { killturn: 0 } });
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(false); expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(false);
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(false);
expect(canUseAutomatedNationAction(ruler, '천도')).toBe(false); expect(canUseAutomatedNationAction(ruler, '천도')).toBe(false);
expect(canUseRulerAutomation(ruler, 'finance')).toBe(false); expect(canUseRulerAutomation(ruler, 'finance')).toBe(false);
@@ -728,9 +729,17 @@ describe('legacy NPC user-chief promotion parity', () => {
use_auto_nation_finance: 1, use_auto_nation_finance: 1,
}; };
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(true); expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(true);
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(true); expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(false);
expect(canUseAutomatedNationAction(ruler, '천도')).toBe(true); expect(canUseAutomatedNationAction(ruler, '천도')).toBe(true);
expect(canUseRulerAutomation(ruler, 'finance')).toBe(true); expect(canUseRulerAutomation(ruler, 'finance')).toBe(true);
ruler.meta = {
...ruler.meta,
use_auto_nation_diplomacy: 0,
use_auto_nation_war: 1,
};
expect(canUseAutomatedNationAction(ruler, '불가침제의')).toBe(false);
expect(canUseAutomatedNationAction(ruler, '선전포고')).toBe(true);
}); });
it('honors the existing automatic nation-turn master switch for user chiefs only', () => { it('honors the existing automatic nation-turn master switch for user chiefs only', () => {
@@ -223,6 +223,7 @@ describe('my information world commands', () => {
use_treatment: 200, use_treatment: 200,
use_auto_nation_turn: 0, use_auto_nation_turn: 0,
use_auto_nation_diplomacy: 1, use_auto_nation_diplomacy: 1,
use_auto_nation_war: 1,
use_auto_nation_promotion: 1, use_auto_nation_promotion: 1,
use_auto_nation_finance: 1, use_auto_nation_finance: 1,
use_auto_nation_capital: 1, use_auto_nation_capital: 1,
@@ -239,6 +240,7 @@ describe('my information world commands', () => {
use_treatment: 100, use_treatment: 100,
use_auto_nation_turn: 0, use_auto_nation_turn: 0,
use_auto_nation_diplomacy: 1, use_auto_nation_diplomacy: 1,
use_auto_nation_war: 1,
use_auto_nation_promotion: 1, use_auto_nation_promotion: 1,
use_auto_nation_finance: 1, use_auto_nation_finance: 1,
use_auto_nation_capital: 1, use_auto_nation_capital: 1,
+11 -2
View File
@@ -276,6 +276,7 @@ const myGeneral = (state: FixtureState) => ({
use_treatment: 21, use_treatment: 21,
use_auto_nation_turn: 1, use_auto_nation_turn: 1,
use_auto_nation_diplomacy: 0, use_auto_nation_diplomacy: 0,
use_auto_nation_war: 0,
use_auto_nation_promotion: 0, use_auto_nation_promotion: 0,
use_auto_nation_finance: 0, use_auto_nation_finance: 0,
use_auto_nation_capital: 0, use_auto_nation_capital: 0,
@@ -1342,11 +1343,18 @@ test('내 정보&설정 keeps desktop density and becomes a 390px horizontal-ide
]); ]);
const rulerAutomation = page.locator('.ruler-automation-settings'); const rulerAutomation = page.locator('.ruler-automation-settings');
await expect(rulerAutomation).toBeVisible(); await expect(rulerAutomation).toBeVisible();
const diplomacyAutomation = page.getByRole('checkbox', { name: '자동 외교 (불가침 제의·선전포고)' }); const diplomacyAutomation = page.getByRole('checkbox', { name: '자동 외교 (불가침 제의)' });
const warAutomation = page.getByRole('checkbox', { name: '자동 선전포고' });
const promotionAutomation = page.getByRole('checkbox', { name: '자동 수뇌 임명' }); const promotionAutomation = page.getByRole('checkbox', { name: '자동 수뇌 임명' });
const financeAutomation = page.getByRole('checkbox', { name: '자동 세율·지급률 조정' }); const financeAutomation = page.getByRole('checkbox', { name: '자동 세율·지급률 조정' });
const capitalAutomation = page.getByRole('checkbox', { name: '자동 천도' }); const capitalAutomation = page.getByRole('checkbox', { name: '자동 천도' });
for (const checkbox of [diplomacyAutomation, promotionAutomation, financeAutomation, capitalAutomation]) { for (const checkbox of [
diplomacyAutomation,
warAutomation,
promotionAutomation,
financeAutomation,
capitalAutomation,
]) {
await expect(checkbox).not.toBeChecked(); await expect(checkbox).not.toBeChecked();
await checkbox.check(); await checkbox.check();
} }
@@ -1410,6 +1418,7 @@ test('내 정보&설정 keeps desktop density and becomes a 390px horizontal-ide
expect(state.settingMutations[0]).not.toHaveProperty('generalId'); expect(state.settingMutations[0]).not.toHaveProperty('generalId');
expect(state.settingMutations[0]).toMatchObject({ expect(state.settingMutations[0]).toMatchObject({
use_auto_nation_diplomacy: 1, use_auto_nation_diplomacy: 1,
use_auto_nation_war: 1,
use_auto_nation_promotion: 1, use_auto_nation_promotion: 1,
use_auto_nation_finance: 1, use_auto_nation_finance: 1,
use_auto_nation_capital: 1, use_auto_nation_capital: 1,
+12 -1
View File
@@ -33,6 +33,7 @@ type SettingForm = {
use_treatment: number; use_treatment: number;
use_auto_nation_turn: number; use_auto_nation_turn: number;
use_auto_nation_diplomacy: number; use_auto_nation_diplomacy: number;
use_auto_nation_war: number;
use_auto_nation_promotion: number; use_auto_nation_promotion: number;
use_auto_nation_finance: number; use_auto_nation_finance: number;
use_auto_nation_capital: number; use_auto_nation_capital: number;
@@ -69,6 +70,7 @@ const form = reactive<SettingForm>({
use_treatment: 10, use_treatment: 10,
use_auto_nation_turn: 1, use_auto_nation_turn: 1,
use_auto_nation_diplomacy: 0, use_auto_nation_diplomacy: 0,
use_auto_nation_war: 0,
use_auto_nation_promotion: 0, use_auto_nation_promotion: 0,
use_auto_nation_finance: 0, use_auto_nation_finance: 0,
use_auto_nation_capital: 0, use_auto_nation_capital: 0,
@@ -432,7 +434,16 @@ onMounted(() => {
:true-value="1" :true-value="1"
:false-value="0" :false-value="0"
/> />
자동 외교 (불가침 제의·선전포고) 자동 외교 (불가침 제의)
</label>
<label>
<input
v-model="form.use_auto_nation_war"
type="checkbox"
:true-value="1"
:false-value="0"
/>
자동 선전포고
</label> </label>
<label> <label>
<input <input
+1
View File
@@ -164,6 +164,7 @@ export type TurnDaemonCommand =
use_treatment?: number; use_treatment?: number;
use_auto_nation_turn?: number; use_auto_nation_turn?: number;
use_auto_nation_diplomacy?: number; use_auto_nation_diplomacy?: number;
use_auto_nation_war?: number;
use_auto_nation_promotion?: number; use_auto_nation_promotion?: number;
use_auto_nation_finance?: number; use_auto_nation_finance?: number;
use_auto_nation_capital?: number; use_auto_nation_capital?: number;