fix nation diplomacy proposal parity

This commit is contained in:
2026-07-26 14:18:56 +00:00
parent e46ef382b0
commit 980781f990
6 changed files with 408 additions and 54 deletions
@@ -20,15 +20,9 @@ import { z } from 'zod';
import { parseArgsWithSchema } from '../parseArgs.js';
const ARGS_SCHEMA = z.object({
destNationId: z.preprocess(
(value) => (typeof value === 'number' ? Math.floor(value) : value),
z.number().int().positive()
),
year: z.preprocess((value) => (typeof value === 'number' ? Math.floor(value) : value), z.number().int().min(0)),
month: z.preprocess(
(value) => (typeof value === 'number' ? Math.floor(value) : value),
z.number().int().min(1).max(12)
),
destNationId: z.number().int().positive(),
year: z.number().int().min(0),
month: z.number().int().min(1).max(12),
});
export type NonAggressionProposalArgs = z.infer<typeof ARGS_SCHEMA>;
@@ -52,12 +46,14 @@ const reqMinimumTreatyTerm = (minMonths: number): Constraint => ({
{ kind: 'arg', key: 'month' },
{ kind: 'env', key: 'year' },
{ kind: 'env', key: 'month' },
{ kind: 'env', key: 'startYear' },
],
test: (ctx) => {
const yearValue = typeof ctx.args.year === 'number' ? ctx.args.year : null;
const monthValue = typeof ctx.args.month === 'number' ? ctx.args.month : null;
const envYearValue = typeof ctx.env.year === 'number' ? ctx.env.year : null;
const envMonthValue = typeof ctx.env.month === 'number' ? ctx.env.month : null;
const startYearValue = typeof ctx.env.startYear === 'number' ? ctx.env.startYear : null;
const missing = [];
if (yearValue === null) {
@@ -72,17 +68,27 @@ const reqMinimumTreatyTerm = (minMonths: number): Constraint => ({
if (envMonthValue === null) {
missing.push({ kind: 'env', key: 'month' } as const);
}
if (startYearValue === null) {
missing.push({ kind: 'env', key: 'startYear' } as const);
}
if (
missing.length > 0 ||
yearValue === null ||
monthValue === null ||
envYearValue === null ||
envMonthValue === null
envMonthValue === null ||
startYearValue === null
) {
return unknownOrDeny(ctx, missing, '기한 정보가 없습니다.');
}
if (yearValue < startYearValue) {
return {
kind: 'deny',
reason: '시작 연도보다 이전의 기한은 지정할 수 없습니다.',
};
}
const currentMonth = resolveMonthIndex(envYearValue, envMonthValue);
const targetMonth = resolveMonthIndex(yearValue, monthValue);
if (targetMonth < currentMonth + minMonths) {
@@ -137,7 +143,7 @@ export class ActionDefinition<
return { effects: [createLogEffect('국가 정보가 없습니다.')] };
}
const destNationName = destNation.name;
const josaRo = JosaUtil.pick(destNationName, '로');
const josaRo = JosaUtil.pick(nation.name, '로');
const josaWa = JosaUtil.pick(nation.name, '와');
const validUntil = new Date(context.messageTime.getTime() + context.messageValidMinutes * 60_000);
return {
@@ -20,10 +20,7 @@ import { z } from 'zod';
import { parseArgsWithSchema } from '../parseArgs.js';
const ARGS_SCHEMA = z.object({
destNationId: z.preprocess(
(value) => (typeof value === 'number' ? Math.floor(value) : value),
z.number().int().positive()
),
destNationId: z.number().int().positive(),
});
export type NonAggressionCancelProposalArgs = z.infer<typeof ARGS_SCHEMA>;
@@ -15,7 +15,7 @@ import type {
GeneralActionOutcome,
GeneralActionResolveContext,
} from '@sammo-ts/logic/actions/engine.js';
import { createDiplomacyPatchEffect, createLogEffect } from '@sammo-ts/logic/actions/engine.js';
import { createDiplomacyPatchEffect, createLogEffect, createMessageEffect } from '@sammo-ts/logic/actions/engine.js';
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
@@ -25,14 +25,18 @@ import { z } from 'zod';
import { parseArgsWithSchema } from '../parseArgs.js';
const ARGS_SCHEMA = z.object({
destNationId: z.preprocess(
(value) => (typeof value === 'number' ? Math.floor(value) : value),
z.number().int().positive()
),
destNationId: z.number().int().positive(),
});
export type DeclareWarArgs = z.infer<typeof ARGS_SCHEMA>;
// DeclareWarResolveContext is not used anymore as it was replaced by inline type in ActionDefinition
interface DeclareWarResolveContext<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> extends GeneralActionResolveContext<TriggerState> {
destNation: Nation;
currentYear: number;
currentMonth: number;
messageTime: Date;
}
const ACTION_NAME = '선전포고';
// legacy 규칙: 선전포고 상태는 24턴 유지.
@@ -41,11 +45,7 @@ const DECLARE_TERM = 24;
export class ActionDefinition<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> implements GeneralActionDefinition<
TriggerState,
DeclareWarArgs,
GeneralActionResolveContext<TriggerState> & { destNation: Nation }
> {
> implements GeneralActionDefinition<TriggerState, DeclareWarArgs, DeclareWarResolveContext<TriggerState>> {
public readonly key = 'che_선전포고';
public readonly name = ACTION_NAME;
@@ -98,10 +98,7 @@ export class ActionDefinition<
];
}
resolve(
context: GeneralActionResolveContext<TriggerState> & { destNation: Nation },
args: DeclareWarArgs
): GeneralActionOutcome<TriggerState> {
resolve(context: DeclareWarResolveContext<TriggerState>, args: DeclareWarArgs): GeneralActionOutcome<TriggerState> {
const nationId = context.nation?.id;
if (nationId === undefined || nationId <= 0 || !context.destNation) {
return {
@@ -157,27 +154,46 @@ export class ActionDefinition<
format: LogFormat.YEAR_MONTH,
}),
// Global Action Log
createLogEffect(`<Y>${generalName}</>${josaYiGeneral} <D><b>${destNationName}</b></>에 <M>선전 포고</> 하였습니다.`, {
scope: LogScope.SYSTEM,
category: LogCategory.ACTION,
format: LogFormat.PLAIN,
}),
// Global History Log
createLogEffect(`<R><b>【선포】</b></><D><b>${nationName}</b></>${josaYiNation} <D><b>${destNationName}</b></>에 선전 포고 하였습니다.`, {
scope: LogScope.SYSTEM,
category: LogCategory.HISTORY,
format: LogFormat.YEAR_MONTH,
}),
// National Message (국메)
createLogEffect(
`【국메】<Y>${generalName}</>${josaYiGeneral} <Y>${destNationName}</>에 <R>${ACTION_NAME}</>하였습니다!`,
`<Y>${generalName}</>${josaYiGeneral} <D><b>${destNationName}</b></>에 <M>선전 포고</> 하였습니다.`,
{
scope: LogScope.NATION,
nationId: nationId,
category: LogCategory.ACTION,
format: LogFormat.PLAIN,
scope: LogScope.SYSTEM,
category: LogCategory.SUMMARY,
format: LogFormat.MONTH,
}
),
// Global History Log
createLogEffect(
`<R><b>【선포】</b></><D><b>${nationName}</b></>${josaYiNation} <D><b>${destNationName}</b></>에 선전 포고 하였습니다.`,
{
scope: LogScope.SYSTEM,
category: LogCategory.HISTORY,
format: LogFormat.YEAR_MONTH,
}
),
createMessageEffect({
msgType: 'national',
src: {
generalId: context.general.id,
generalName,
nationId,
nationName,
color: context.nation?.color ?? '',
icon: '',
},
dest: {
generalId: 0,
generalName: '',
nationId: args.destNationId,
nationName: destNationName,
color: context.destNation.color,
icon: '',
},
text: `【외교】${context.currentYear}${context.currentMonth}월:${nationName}에서 ${destNationName}에 선전포고`,
time: context.messageTime,
validUntil: new Date('9999-12-31T00:00:00.000Z'),
option: {},
}),
];
return { effects };
@@ -201,6 +217,9 @@ export const actionContextBuilder: ActionContextBuilder<DeclareWarArgs> = (base,
return {
...base,
destNation,
currentYear: options.world.currentYear,
currentMonth: options.world.currentMonth,
messageTime: base.general.turnTime,
};
};
@@ -20,10 +20,7 @@ import { z } from 'zod';
import { parseArgsWithSchema } from '../parseArgs.js';
const ARGS_SCHEMA = z.object({
destNationId: z.preprocess(
(value) => (typeof value === 'number' ? Math.floor(value) : value),
z.number().int().positive()
),
destNationId: z.number().int().positive(),
});
export type StopWarProposalArgs = z.infer<typeof ARGS_SCHEMA>;