Complete instant diplomacy response parity

This commit is contained in:
2026-07-26 03:10:41 +00:00
parent b3f4734e80
commit 9885bf6905
23 changed files with 2072 additions and 305 deletions
@@ -1,4 +1,4 @@
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
import type { General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
import {
beChief,
@@ -7,15 +7,15 @@ import {
existsDestNation,
notBeNeutral,
occupiedCity,
reqDestNationValue,
suppliedCity,
} from '@sammo-ts/logic/constraints/presets.js';
import { allow, unknownOrDeny } from '@sammo-ts/logic/constraints/helpers.js';
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
import { createDiplomacyPatchEffect, createLogEffect } from '@sammo-ts/logic/actions/engine.js';
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
import { JosaUtil } from '@sammo-ts/common';
import {
destGeneralBelongsToDestNation,
resolveInstantDiplomacyResponse,
} from '@sammo-ts/logic/diplomacy/instantResponse.js';
export interface NonAggressionAcceptArgs {
destNationId: number;
@@ -29,11 +29,11 @@ export interface NonAggressionAcceptContext<
> extends GeneralActionResolveContext<TriggerState> {
currentYear: number;
currentMonth: number;
destNation: Nation;
destGeneral: General<TriggerState>;
}
const ACTION_NAME = '불가침 수락';
const DIPLOMACY_NON_AGGRESSION = 7;
const parseNationId = (raw: unknown): number | null => {
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
return null;
@@ -147,7 +147,7 @@ export class ActionDefinition<
suppliedCity(),
existsDestNation(),
existsDestGeneral(),
reqDestNationValue('level', '국가규모', '>', 0, '상대국 정보가 없습니다.'),
destGeneralBelongsToDestNation(),
reqFutureTreatyTerm(),
disallowDiplomacyBetweenStatus({
0: '아국과 이미 교전중입니다.',
@@ -160,44 +160,29 @@ export class ActionDefinition<
context: NonAggressionAcceptContext<TriggerState>,
args: NonAggressionAcceptArgs
): GeneralActionOutcome<TriggerState> {
const nationId = context.nation?.id;
if (nationId === undefined || nationId <= 0) {
const nation = context.nation;
if (!nation) {
return {
effects: [
createLogEffect(`${ACTION_NAME}을 준비했지만 국가 정보가 없습니다.`, {
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.MONTH,
}),
],
effects: [],
};
}
const currentMonth = resolveMonthIndex(context.currentYear, context.currentMonth);
const targetMonth = args.year * 12 + args.month;
const term = Math.max(0, targetMonth - currentMonth);
const destNationName = String(args.destNationId);
const josaWa = JosaUtil.pick(destNationName, '와');
const resolution = resolveInstantDiplomacyResponse<TriggerState>(
{
actor: context.general,
actorNation: nation,
proposer: context.destGeneral,
proposerNation: context.destNation,
currentYear: context.currentYear,
currentMonth: context.currentMonth,
},
{
action: 'noAggression',
treatyYear: args.year,
treatyMonth: args.month,
}
);
return {
effects: [
createDiplomacyPatchEffect(nationId, args.destNationId, {
state: DIPLOMACY_NON_AGGRESSION,
term,
}),
createDiplomacyPatchEffect(args.destNationId, nationId, {
state: DIPLOMACY_NON_AGGRESSION,
term,
}),
createLogEffect(
`<D><b>${destNationName}</b></>${josaWa} <C>${args.year}</>년 <C>${args.month}</>월까지 불가침에 성공했습니다.`,
{
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.MONTH,
}
),
],
effects: resolution.effects,
};
}
}
@@ -1,4 +1,4 @@
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
import type { General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
import {
allowDiplomacyBetweenStatus,
@@ -6,21 +6,29 @@ import {
existsDestGeneral,
existsDestNation,
notBeNeutral,
reqDestNationValue,
} from '@sammo-ts/logic/constraints/presets.js';
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
import { createDiplomacyPatchEffect, createLogEffect } from '@sammo-ts/logic/actions/engine.js';
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
import { JosaUtil } from '@sammo-ts/common';
import {
destGeneralBelongsToDestNation,
resolveInstantDiplomacyResponse,
} from '@sammo-ts/logic/diplomacy/instantResponse.js';
export interface NonAggressionCancelAcceptArgs {
destNationId: number;
destGeneralId: number;
}
export interface NonAggressionCancelAcceptContext<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> extends GeneralActionResolveContext<TriggerState> {
destNation: Nation;
destGeneral: General<TriggerState>;
currentYear: number;
currentMonth: number;
}
const ACTION_NAME = '불가침 파기 수락';
const DIPLOMACY_NEUTRAL = 2;
const DIPLOMACY_NON_AGGRESSION = 7;
const parseNationId = (raw: unknown): number | null => {
@@ -40,7 +48,11 @@ const parseGeneralId = (raw: unknown): number | null => {
// 불가침 파기 수락은 메시지와 연결되는 즉시 국가 커맨드로 사용한다.
export class ActionDefinition<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> implements GeneralActionDefinition<TriggerState, NonAggressionCancelAcceptArgs> {
> implements GeneralActionDefinition<
TriggerState,
NonAggressionCancelAcceptArgs,
NonAggressionCancelAcceptContext<TriggerState>
> {
public readonly key = 'che_불가침파기수락';
public readonly name = ACTION_NAME;
@@ -60,47 +72,32 @@ export class ActionDefinition<
notBeNeutral(),
existsDestNation(),
existsDestGeneral(),
reqDestNationValue('level', '국가규모', '>', 0, '상대국 정보가 없습니다.'),
destGeneralBelongsToDestNation(),
allowDiplomacyBetweenStatus([DIPLOMACY_NON_AGGRESSION], '불가침 중인 상대국에게만 가능합니다.'),
];
}
resolve(
context: GeneralActionResolveContext<TriggerState>,
args: NonAggressionCancelAcceptArgs
context: NonAggressionCancelAcceptContext<TriggerState>,
_args: NonAggressionCancelAcceptArgs
): GeneralActionOutcome<TriggerState> {
const nationId = context.nation?.id;
if (nationId === undefined || nationId <= 0) {
return {
effects: [
createLogEffect(`${ACTION_NAME}을 준비했지만 국가 정보가 없습니다.`, {
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.MONTH,
}),
],
};
const nation = context.nation;
if (!nation) {
return { effects: [] };
}
const destNationName = String(args.destNationId);
const josaWa = JosaUtil.pick(destNationName, '와');
const resolution = resolveInstantDiplomacyResponse<TriggerState>(
{
actor: context.general,
actorNation: nation,
proposer: context.destGeneral,
proposerNation: context.destNation,
currentYear: context.currentYear,
currentMonth: context.currentMonth,
},
{ action: 'cancelNA' }
);
return {
effects: [
createDiplomacyPatchEffect(nationId, args.destNationId, {
state: DIPLOMACY_NEUTRAL,
term: 0,
}),
createDiplomacyPatchEffect(args.destNationId, nationId, {
state: DIPLOMACY_NEUTRAL,
term: 0,
}),
createLogEffect(`<D><b>${destNationName}</b></>${josaWa}의 불가침을 파기했습니다.`, {
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.MONTH,
}),
],
effects: resolution.effects,
};
}
}
@@ -1,4 +1,4 @@
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
import type { General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
import {
allowDiplomacyBetweenStatus,
@@ -6,22 +6,29 @@ import {
existsDestGeneral,
existsDestNation,
notBeNeutral,
reqDestNationValue,
} from '@sammo-ts/logic/constraints/presets.js';
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
import { createDiplomacyPatchEffect, createLogEffect } from '@sammo-ts/logic/actions/engine.js';
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic/logging/types.js';
import { JosaUtil } from '@sammo-ts/common';
import {
destGeneralBelongsToDestNation,
resolveInstantDiplomacyResponse,
} from '@sammo-ts/logic/diplomacy/instantResponse.js';
export interface StopWarAcceptArgs {
destNationId: number;
destGeneralId: number;
}
const ACTION_NAME = '종전 수락';
const DIPLOMACY_NEUTRAL = 2;
export interface StopWarAcceptContext<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> extends GeneralActionResolveContext<TriggerState> {
destNation: Nation;
destGeneral: General<TriggerState>;
currentYear: number;
currentMonth: number;
}
const ACTION_NAME = '종전 수락';
const parseNationId = (raw: unknown): number | null => {
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
return null;
@@ -39,7 +46,7 @@ const parseGeneralId = (raw: unknown): number | null => {
// 종전 수락은 메시지와 연결되는 즉시 국가 커맨드로 사용한다.
export class ActionDefinition<
TriggerState extends GeneralTriggerState = GeneralTriggerState,
> implements GeneralActionDefinition<TriggerState, StopWarAcceptArgs> {
> implements GeneralActionDefinition<TriggerState, StopWarAcceptArgs, StopWarAcceptContext<TriggerState>> {
public readonly key = 'che_종전수락';
public readonly name = ACTION_NAME;
@@ -59,97 +66,29 @@ export class ActionDefinition<
notBeNeutral(),
existsDestNation(),
existsDestGeneral(),
reqDestNationValue('level', '국가규모', '>', 0, '상대국 정보가 없습니다.'),
destGeneralBelongsToDestNation(),
allowDiplomacyBetweenStatus([0, 1], '상대국과 선포, 전쟁중이지 않습니다.'),
];
}
resolve(
context: GeneralActionResolveContext<TriggerState>,
args: StopWarAcceptArgs
): GeneralActionOutcome<TriggerState> {
const nationId = context.nation?.id;
if (nationId === undefined || nationId <= 0) {
return {
effects: [
createLogEffect(`${ACTION_NAME}을 준비했지만 국가 정보가 없습니다.`, {
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.MONTH,
}),
],
};
resolve(context: StopWarAcceptContext<TriggerState>, _args: StopWarAcceptArgs): GeneralActionOutcome<TriggerState> {
const nation = context.nation;
if (!nation) {
return { effects: [] };
}
const nationName = context.nation?.name ?? `국가${nationId}`;
const destNationName =
(context as { destNation?: { name?: string } }).destNation?.name ?? `국가${args.destNationId}`;
const generalName = context.general.name;
const josaYiGeneral = JosaUtil.pick(generalName, '이');
const josaYiNation = JosaUtil.pick(nationName, '이');
const josaWa = JosaUtil.pick(destNationName, '와');
const resolution = resolveInstantDiplomacyResponse<TriggerState>(
{
actor: context.general,
actorNation: nation,
proposer: context.destGeneral,
proposerNation: context.destNation,
currentYear: context.currentYear,
currentMonth: context.currentMonth,
},
{ action: 'stopWar' }
);
return {
effects: [
createDiplomacyPatchEffect(nationId, args.destNationId, {
state: DIPLOMACY_NEUTRAL,
term: 0,
}),
createDiplomacyPatchEffect(args.destNationId, nationId, {
state: DIPLOMACY_NEUTRAL,
term: 0,
}),
createLogEffect(`<D><b>${destNationName}</b></>${josaWa} 종전에 합의했습니다.`, {
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.PLAIN,
}),
createLogEffect(`<D><b>${destNationName}</b></>${josaWa} 종전 수락`, {
scope: LogScope.GENERAL,
category: LogCategory.HISTORY,
format: LogFormat.YEAR_MONTH,
}),
createLogEffect(
`<Y>${generalName}</>${josaYiGeneral} <D><b>${destNationName}</b></>${josaWa} <M>종전 합의</> 하였습니다.`,
{
scope: LogScope.SYSTEM,
category: LogCategory.ACTION,
format: LogFormat.PLAIN,
}
),
createLogEffect(
`<Y><b>【종전】</b></><D><b>${nationName}</b></>${josaYiNation} <D><b>${destNationName}</b></>${josaWa} <M>종전 합의</> 하였습니다.`,
{
scope: LogScope.SYSTEM,
category: LogCategory.HISTORY,
format: LogFormat.YEAR_MONTH,
}
),
createLogEffect(`<D><b>${destNationName}</b></>${josaWa} 종전`, {
scope: LogScope.NATION,
nationId,
category: LogCategory.HISTORY,
format: LogFormat.YEAR_MONTH,
}),
createLogEffect(`<D><b>${nationName}</b></>${josaWa} 종전에 성공했습니다.`, {
scope: LogScope.GENERAL,
generalId: args.destGeneralId,
category: LogCategory.ACTION,
format: LogFormat.PLAIN,
}),
createLogEffect(`<D><b>${nationName}</b></>${josaWa} 종전 성공`, {
scope: LogScope.GENERAL,
generalId: args.destGeneralId,
category: LogCategory.HISTORY,
format: LogFormat.YEAR_MONTH,
}),
createLogEffect(`<D><b>${nationName}</b></>${josaWa} 종전`, {
scope: LogScope.NATION,
nationId: args.destNationId,
category: LogCategory.HISTORY,
format: LogFormat.YEAR_MONTH,
}),
],
effects: resolution.effects,
};
}
}
@@ -6,8 +6,10 @@ export {
export {
ActionDefinition as NonAggressionCancelAcceptActionDefinition,
type NonAggressionCancelAcceptArgs,
type NonAggressionCancelAcceptContext,
} from './che_불가침파기수락.js';
export {
ActionDefinition as StopWarAcceptActionDefinition,
type StopWarAcceptArgs,
type StopWarAcceptContext,
} from './che_종전수락.js';