fix nation personnel command parity
This commit is contained in:
@@ -1,13 +1,6 @@
|
||||
import type {
|
||||
City,
|
||||
General,
|
||||
GeneralMeta,
|
||||
GeneralTriggerState,
|
||||
TriggerValue,
|
||||
} from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { City, General, GeneralMeta, GeneralTriggerState, TriggerValue } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||
import {
|
||||
denyWithReason,
|
||||
beChief,
|
||||
existsDestGeneral,
|
||||
friendlyDestGeneral,
|
||||
@@ -32,11 +25,11 @@ import { resolveTurnTermMinutes } from '@sammo-ts/logic/actions/turn/actionConte
|
||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||
import type { NationTurnCommandSpec } from './index.js';
|
||||
import { z } from 'zod';
|
||||
import { parseArgsWithSchema } from '../parseArgs.js';
|
||||
import { normalizeLegacyIntegerArg, parseArgsWithSchema } from '../parseArgs.js';
|
||||
|
||||
const ARGS_SCHEMA = z.object({
|
||||
destGeneralId: z.number(),
|
||||
destCityId: z.number(),
|
||||
destGeneralId: z.preprocess(normalizeLegacyIntegerArg, z.number().int()),
|
||||
destCityId: z.preprocess(normalizeLegacyIntegerArg, z.number().int()),
|
||||
});
|
||||
export type AssignmentArgs = z.infer<typeof ARGS_SCHEMA>;
|
||||
|
||||
@@ -101,6 +94,18 @@ export class ActionResolver<
|
||||
void _args;
|
||||
const destGeneral = context.destGeneral;
|
||||
const destCity = context.destCity;
|
||||
if (destGeneral.id === context.general.id) {
|
||||
return {
|
||||
completed: false,
|
||||
effects: [
|
||||
createLogEffect(`본인입니다 <Y>${destGeneral.name}</> ${ACTION_NAME} 실패.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
const cityName = this.env.formatCityName ? this.env.formatCityName(destCity) : destCity.name;
|
||||
const cityJosa = JosaUtil.pick(cityName, '로');
|
||||
const generalJosa = JosaUtil.pick(destGeneral.name, '을');
|
||||
@@ -158,11 +163,7 @@ export class ActionDefinition<
|
||||
return [beChief(), notBeNeutral(), occupiedCity(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: AssignmentArgs): Constraint[] {
|
||||
void _args;
|
||||
if (ctx.destGeneralId === ctx.actorId) {
|
||||
return [denyWithReason('본인입니다')];
|
||||
}
|
||||
buildConstraints(_ctx: ConstraintContext, _args: AssignmentArgs): Constraint[] {
|
||||
return [
|
||||
beChief(),
|
||||
notBeNeutral(),
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import type { General, GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||
import {
|
||||
denyWithReason,
|
||||
beChief,
|
||||
existsDestGeneral,
|
||||
friendlyDestGeneral,
|
||||
notBeNeutral,
|
||||
} from '@sammo-ts/logic/constraints/presets.js';
|
||||
import { beChief, existsDestGeneral, friendlyDestGeneral, notBeNeutral } from '@sammo-ts/logic/constraints/presets.js';
|
||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||
import type {
|
||||
GeneralActionEffect,
|
||||
@@ -23,10 +17,7 @@ import { z } from 'zod';
|
||||
import { parseArgsWithSchema } from '../parseArgs.js';
|
||||
|
||||
const ARGS_SCHEMA = z.object({
|
||||
destGeneralId: z.preprocess(
|
||||
(value) => (typeof value === 'number' ? Math.floor(value) : value),
|
||||
z.number().int().positive()
|
||||
),
|
||||
destGeneralId: z.number().int().positive(),
|
||||
});
|
||||
export type TroopKickArgs = z.infer<typeof ARGS_SCHEMA>;
|
||||
|
||||
@@ -52,10 +43,7 @@ export class ActionDefinition<
|
||||
return [notBeNeutral(), beChief()];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: TroopKickArgs): Constraint[] {
|
||||
if (ctx.destGeneralId !== undefined && ctx.destGeneralId === ctx.actorId) {
|
||||
return [denyWithReason('본인입니다')];
|
||||
}
|
||||
buildConstraints(_ctx: ConstraintContext, _args: TroopKickArgs): Constraint[] {
|
||||
return [notBeNeutral(), beChief(), existsDestGeneral(), friendlyDestGeneral()];
|
||||
}
|
||||
|
||||
@@ -66,6 +54,19 @@ export class ActionDefinition<
|
||||
const josaUn = JosaUtil.pick(destGeneralName, '은');
|
||||
const effects: Array<GeneralActionEffect<TriggerState>> = [];
|
||||
|
||||
if (destGeneral.id === general.id) {
|
||||
return {
|
||||
completed: false,
|
||||
effects: [
|
||||
createLogEffect(`본인입니다 ${ACTION_NAME} 실패.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (destGeneral.troopId === 0) {
|
||||
context.addLog(`<Y>${destGeneralName}</>${josaUn} 부대원이 아닙니다.`);
|
||||
return { effects };
|
||||
|
||||
@@ -5,6 +5,8 @@ import { ActionDefinition as DeclareWarAction } from '../../../src/actions/turn/
|
||||
import { ActionDefinition as NonAggressionProposalAction } from '../../../src/actions/turn/nation/che_불가침제의.js';
|
||||
import { ActionDefinition as StopWarProposalAction } from '../../../src/actions/turn/nation/che_종전제의.js';
|
||||
import { ActionDefinition as NonAggressionCancelProposalAction } from '../../../src/actions/turn/nation/che_불가침파기제의.js';
|
||||
import { ActionDefinition as TroopKickAction } from '../../../src/actions/turn/nation/che_부대탈퇴지시.js';
|
||||
import { ActionDefinition as AssignmentAction } from '../../../src/actions/turn/nation/che_발령.js';
|
||||
import { ActionDefinition as MoveCapitalAction } from '../../../src/actions/turn/nation/che_천도.js';
|
||||
import {
|
||||
ActionDefinition as ChangeNationNameAction,
|
||||
@@ -266,6 +268,29 @@ describe('Nation Actions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('personnel command argument boundaries', () => {
|
||||
it('keeps troop-kick target IDs strict', () => {
|
||||
const definition = new TroopKickAction();
|
||||
|
||||
expect(definition.parseArgs({ destGeneralId: 3 })).toEqual({ destGeneralId: 3 });
|
||||
expect(definition.parseArgs({ destGeneralId: '3' })).toBeNull();
|
||||
expect(definition.parseArgs({ destGeneralId: 3.9 })).toBeNull();
|
||||
});
|
||||
|
||||
it('uses PHP weak integer coercion for assignment IDs', () => {
|
||||
const definition = new AssignmentAction({});
|
||||
|
||||
expect(definition.parseArgs({ destGeneralId: '3', destCityId: '70' })).toEqual({
|
||||
destGeneralId: 3,
|
||||
destCityId: 70,
|
||||
});
|
||||
expect(definition.parseArgs({ destGeneralId: 3.9, destCityId: 70.9 })).toEqual({
|
||||
destGeneralId: 3,
|
||||
destCityId: 70,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('che_필사즉생 (Last Stand)', () => {
|
||||
it('applies the legacy three-turn gains and global delay', () => {
|
||||
const nation = buildNation(1);
|
||||
|
||||
Reference in New Issue
Block a user