feat: Add new general actions and constraints for game mechanics

- Introduced new general turn commands: 'che_거병', 'che_임관', 'che_건국', 'che_훈련', 'che_사기진작', 'che_요양', 'che_출병', 'che_주민선정', 'che_농지개간', 'che_기술연구', 'che_치안강화', 'che_수비강화', 'che_성벽보수'.
- Implemented action definitions for each new command, including constraints and resolution logic.
- Added new constraints: 'beNeutral', 'remainCityCapacityByMax', 'existsDestNation'.
- Enhanced city development actions for '농지개간', '성벽보수', and '수비강화'.
- Created a new action for declaring war: 'che_선전포고' with appropriate constraints.
- Updated existing action definitions to accommodate new game mechanics and ensure proper functionality.
This commit is contained in:
2026-01-01 11:49:15 +00:00
parent 8c9f61700e
commit cce57d0fea
20 changed files with 1344 additions and 2 deletions
@@ -0,0 +1,74 @@
import type { GeneralTriggerState } from '../../../domain/entities.js';
import type { Constraint, ConstraintContext } from '../../../constraints/types.js';
import {
beChief,
existsDestNation,
notBeNeutral,
occupiedCity,
suppliedCity,
} from '../../../constraints/presets.js';
import type { GeneralActionDefinition } from '../../definition.js';
import type {
GeneralActionOutcome,
GeneralActionResolveContext,
} from '../../engine.js';
import { createLogEffect } from '../../engine.js';
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
export interface DeclareWarArgs {
destNationId: number;
}
const ACTION_NAME = '선전포고';
const parseNationId = (raw: unknown): number | null => {
if (typeof raw !== 'number' || !Number.isFinite(raw)) {
return null;
}
return raw > 0 ? Math.floor(raw) : null;
};
export class ActionDefinition<
TriggerState extends GeneralTriggerState = GeneralTriggerState
> implements GeneralActionDefinition<TriggerState, DeclareWarArgs> {
public readonly key = 'che_선전포고';
public readonly name = ACTION_NAME;
parseArgs(raw: unknown): DeclareWarArgs | null {
const data = raw as { destNationId?: unknown };
const destNationId = parseNationId(data?.destNationId);
if (destNationId === null) {
return null;
}
return { destNationId };
}
buildConstraints(_ctx: ConstraintContext, _args: DeclareWarArgs): Constraint[] {
return [
notBeNeutral(),
occupiedCity(),
suppliedCity(),
beChief(),
existsDestNation(),
];
}
resolve(
_context: GeneralActionResolveContext<TriggerState>,
args: DeclareWarArgs
): GeneralActionOutcome<TriggerState> {
void _context;
return {
effects: [
createLogEffect(
`${ACTION_NAME}을 준비했습니다. (국가 ${args.destNationId})`,
{
scope: LogScope.GENERAL,
category: LogCategory.ACTION,
format: LogFormat.MONTH,
}
),
],
};
}
}
@@ -1,13 +1,19 @@
export type NationTurnCommandKey = '휴식' | 'che_포상' | 'che_발령';
export type NationTurnCommandKey =
| '휴식'
| 'che_포상'
| 'che_발령'
| 'che_선전포고';
import type * as NationRestModule from './휴식.js';
import type * as AwardModule from './che_포상.js';
import type * as AssignmentModule from './che_발령.js';
import type * as DeclarationModule from './che_선전포고.js';
export type NationTurnCommandModule =
| typeof NationRestModule
| typeof AwardModule
| typeof AssignmentModule;
| typeof AssignmentModule
| typeof DeclarationModule;
export type NationTurnCommandImporter = () => Promise<NationTurnCommandModule>;
@@ -18,6 +24,7 @@ const defaultImporters: Record<
휴식: async () => import('./휴식.js'),
che_포상: async () => import('./che_포상.js'),
che_발령: async () => import('./che_발령.js'),
che_선전포고: async () => import('./che_선전포고.js'),
};
export class NationTurnCommandLoader {
@@ -52,3 +59,6 @@ export {
ActionDefinition as AssignmentActionDefinition,
ActionResolver as AssignmentActionResolver,
} from './che_발령.js';
export {
ActionDefinition as DeclarationActionDefinition,
} from './che_선전포고.js';