buildMinConstraints 구현
This commit is contained in:
@@ -184,6 +184,8 @@ const buildCommandEnv = (worldState: WorldStateRow): CommandEnv => {
|
||||
};
|
||||
|
||||
const buildConstraintEnv = (worldState: WorldStateRow): Record<string, unknown> => {
|
||||
const config = asRecord(worldState.config);
|
||||
const constValues = asRecord(config.const);
|
||||
const meta = asRecord(worldState.meta);
|
||||
const scenarioMeta = asRecord(meta.scenarioMeta);
|
||||
const startYear = typeof scenarioMeta.startYear === 'number' ? scenarioMeta.startYear : undefined;
|
||||
@@ -196,6 +198,7 @@ const buildConstraintEnv = (worldState: WorldStateRow): Record<string, unknown>
|
||||
month: worldState.currentMonth,
|
||||
startYear,
|
||||
relYear,
|
||||
openingPartYear: resolveNumber(constValues, ['openingPartYear'], 0),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -352,7 +355,10 @@ const evaluateDefinition = (
|
||||
reqArg: boolean,
|
||||
args: unknown
|
||||
): AvailabilityCore => {
|
||||
const constraints = definition.buildConstraints(ctx, args);
|
||||
const constraints =
|
||||
ctx.mode === 'precheck' && definition.buildMinConstraints
|
||||
? definition.buildMinConstraints(ctx, args)
|
||||
: definition.buildConstraints(ctx, args);
|
||||
return evaluateAvailability(constraints, ctx, view, reqArg);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import type { CityRow, GeneralRow, NationRow, WorldStateRow } from '../src/context.js';
|
||||
import { buildTurnCommandTable } from '../src/turns/commandTable.js';
|
||||
|
||||
const buildWorldState = (): WorldStateRow =>
|
||||
({
|
||||
id: 1,
|
||||
scenarioCode: 'default',
|
||||
currentYear: 3,
|
||||
currentMonth: 1,
|
||||
tickSeconds: 600,
|
||||
config: {
|
||||
const: {
|
||||
baseGold: 1000,
|
||||
baseRice: 1000,
|
||||
develCost: 100,
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
scenarioMeta: {
|
||||
startYear: 1,
|
||||
},
|
||||
},
|
||||
updatedAt: new Date('2026-01-01T00:00:00Z'),
|
||||
}) as unknown as WorldStateRow;
|
||||
|
||||
const buildGeneral = (): GeneralRow =>
|
||||
({
|
||||
id: 1,
|
||||
name: 'TestGeneral',
|
||||
nationId: 1,
|
||||
cityId: 1,
|
||||
troopId: 0,
|
||||
leadership: 70,
|
||||
strength: 60,
|
||||
intel: 60,
|
||||
experience: 0,
|
||||
dedication: 0,
|
||||
officerLevel: 6,
|
||||
personalCode: null,
|
||||
specialCode: null,
|
||||
special2Code: null,
|
||||
horseCode: null,
|
||||
weaponCode: null,
|
||||
bookCode: null,
|
||||
itemCode: null,
|
||||
injury: 0,
|
||||
gold: 0,
|
||||
rice: 0,
|
||||
crew: 100,
|
||||
crewTypeId: 1100,
|
||||
train: 0,
|
||||
atmos: 0,
|
||||
age: 25,
|
||||
npcState: 0,
|
||||
meta: {},
|
||||
}) as unknown as GeneralRow;
|
||||
|
||||
const buildCity = (): CityRow =>
|
||||
({
|
||||
id: 1,
|
||||
name: 'TestCity',
|
||||
nationId: 1,
|
||||
level: 5,
|
||||
population: 100000,
|
||||
populationMax: 200000,
|
||||
agriculture: 1000,
|
||||
agricultureMax: 2000,
|
||||
commerce: 1000,
|
||||
commerceMax: 2000,
|
||||
security: 1000,
|
||||
securityMax: 2000,
|
||||
supplyState: 1,
|
||||
frontState: 0,
|
||||
defence: 0,
|
||||
defenceMax: 0,
|
||||
wall: 0,
|
||||
wallMax: 0,
|
||||
meta: {},
|
||||
trust: 50,
|
||||
trade: 0,
|
||||
region: 0,
|
||||
}) as unknown as CityRow;
|
||||
|
||||
const buildNation = (): NationRow =>
|
||||
({
|
||||
id: 1,
|
||||
name: 'TestNation',
|
||||
color: '#000000',
|
||||
capitalCityId: 1,
|
||||
gold: 0,
|
||||
rice: 0,
|
||||
level: 1,
|
||||
typeCode: 'default',
|
||||
tech: 0,
|
||||
meta: {},
|
||||
}) as unknown as NationRow;
|
||||
|
||||
describe('buildTurnCommandTable', () => {
|
||||
it('uses min-condition constraints for availability', async () => {
|
||||
const table = await buildTurnCommandTable({
|
||||
worldState: buildWorldState(),
|
||||
general: buildGeneral(),
|
||||
city: buildCity(),
|
||||
nation: buildNation(),
|
||||
nationGenerals: null,
|
||||
});
|
||||
|
||||
const nationCommand = table.nation
|
||||
.flatMap((group) => group.values)
|
||||
.find((command) => command.key === 'che_포상');
|
||||
|
||||
expect(nationCommand).toBeDefined();
|
||||
expect(nationCommand?.possible).toBe(true);
|
||||
expect(nationCommand?.status).not.toBe('blocked');
|
||||
});
|
||||
});
|
||||
@@ -7,9 +7,12 @@ export interface GeneralActionDefinition<
|
||||
Args = unknown,
|
||||
Context extends GeneralActionResolveContext<TriggerState> = GeneralActionResolveContext<TriggerState>,
|
||||
> {
|
||||
// TODO: legacy permissionConstraints(예약 권한) 모델링 필요.
|
||||
key: string;
|
||||
name: string;
|
||||
parseArgs(raw: unknown): Args | null;
|
||||
// 커맨드 입력 단계에서 최소 조건만 평가할 때 사용한다.
|
||||
buildMinConstraints?(ctx: ConstraintContext, args: Args): Constraint[];
|
||||
buildConstraints(ctx: ConstraintContext, args: Args): Constraint[];
|
||||
resolve(context: Context, args: Args): GeneralActionOutcome<TriggerState>;
|
||||
}
|
||||
|
||||
@@ -164,6 +164,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: ForcedMoveArgs): Constraint[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: ForcedMoveArgs): Constraint[] {
|
||||
return [
|
||||
existsDestCity(),
|
||||
|
||||
@@ -77,6 +77,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: FoundingArgs): Constraint[] {
|
||||
return [beOpeningPart(), beWanderingNation()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: FoundingArgs): Constraint[] {
|
||||
return [
|
||||
beOpeningPart(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||
import { occupiedCity, reqGeneralGold, reqGeneralRice } from '@sammo-ts/logic/constraints/presets.js';
|
||||
import { occupiedCity, reqGeneralGold, reqGeneralRice, suppliedCity } 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 { LogFormat } from '@sammo-ts/logic/logging/types.js';
|
||||
@@ -37,8 +37,12 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: TradeArgs): Constraint[] {
|
||||
return [occupiedCity(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: TradeArgs): Constraint[] {
|
||||
const constraints: Constraint[] = [occupiedCity()];
|
||||
const constraints: Constraint[] = [occupiedCity(), suppliedCity()];
|
||||
if (args.buyRice) {
|
||||
constraints.push(reqGeneralGold(() => 1));
|
||||
} else {
|
||||
|
||||
@@ -150,6 +150,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: EmployArgs): Constraint[] {
|
||||
return [notBeNeutral(), occupiedCity(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: EmployArgs): Constraint[] {
|
||||
const develCost = this.env.develCost ?? 100;
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext, StateView } from '@sammo-ts/logic/constraints/types.js';
|
||||
import { notBeNeutral, reqGeneralGold } from '@sammo-ts/logic/constraints/presets.js';
|
||||
import {
|
||||
notBeNeutral,
|
||||
notWanderingNation,
|
||||
occupiedCity,
|
||||
reqGeneralCrew,
|
||||
reqGeneralGold,
|
||||
} 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 type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||
@@ -36,9 +42,13 @@ export class ActionDefinition<
|
||||
return {};
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: BoostMoraleArgs): Constraint[] {
|
||||
return [notBeNeutral(), notWanderingNation(), occupiedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: BoostMoraleArgs): Constraint[] {
|
||||
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
||||
return [notBeNeutral(), reqGeneralGold(getRequiredGold)];
|
||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), reqGeneralCrew(), reqGeneralGold(getRequiredGold)];
|
||||
}
|
||||
|
||||
resolve(
|
||||
|
||||
@@ -160,6 +160,15 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(ctx: ConstraintContext, _args: MoveArgs): Constraint[] {
|
||||
return [
|
||||
reqGeneralGold(() => {
|
||||
const develCost = ctx.env.develCost as number;
|
||||
return develCost ?? 0;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: MoveArgs): Constraint[] {
|
||||
const constraints = [
|
||||
existsDestCity(),
|
||||
|
||||
@@ -42,6 +42,10 @@ export class ActionDefinition<
|
||||
return { destNationId };
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: AppointmentArgs): Constraint[] {
|
||||
return [beNeutral()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: AppointmentArgs): Constraint[] {
|
||||
return [beNeutral(), existsDestNation()];
|
||||
}
|
||||
|
||||
@@ -419,6 +419,16 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: RecruitArgs): Constraint[] {
|
||||
const minPopBase = this.env.minAvailableRecruitPop ?? DEFAULT_MIN_POP;
|
||||
return [
|
||||
notBeNeutral(),
|
||||
occupiedCity(),
|
||||
reqCityCapacity('population', '주민', minPopBase + MIN_CREW),
|
||||
reqCityTrust(20),
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: RecruitArgs): Constraint[] {
|
||||
const requirements: RequirementKey[] = [
|
||||
{ kind: 'arg', key: 'crewType' },
|
||||
|
||||
@@ -131,6 +131,12 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(ctx: ConstraintContext, _args: SpyArgs): Constraint[] {
|
||||
const env = ctx.env;
|
||||
const cost = ((env.develCost as number) ?? 100) * 3;
|
||||
return [notBeNeutral(), reqGeneralGold(() => cost), reqGeneralRice(() => cost)];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: SpyArgs): Constraint[] {
|
||||
const env = ctx.env;
|
||||
const cost = ((env.develCost as number) ?? 100) * 3;
|
||||
|
||||
@@ -260,6 +260,19 @@ export class ActionDefinition<
|
||||
return { destCityId };
|
||||
}
|
||||
|
||||
buildMinConstraints(ctx: ConstraintContext, _args: DispatchArgs): Constraint[] {
|
||||
const relYear = typeof ctx.env.relYear === 'number' ? ctx.env.relYear : 0;
|
||||
const openingPartYear = typeof ctx.env.openingPartYear === 'number' ? ctx.env.openingPartYear : 0;
|
||||
return [
|
||||
notOpeningPart(relYear + 2, openingPartYear),
|
||||
notBeNeutral(),
|
||||
occupiedCity(),
|
||||
suppliedCity(),
|
||||
reqGeneralCrew(),
|
||||
reqGeneralRice(getRequiredRice),
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: DispatchArgs): Constraint[] {
|
||||
const relYear = typeof _ctx.env.relYear === 'number' ? _ctx.env.relYear : 0;
|
||||
const openingPartYear = typeof _ctx.env.openingPartYear === 'number' ? _ctx.env.openingPartYear : 0;
|
||||
|
||||
@@ -3,8 +3,10 @@ import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/
|
||||
import {
|
||||
notBeNeutral,
|
||||
notWanderingNation,
|
||||
occupiedCity,
|
||||
reqGeneralGold,
|
||||
reqGeneralRice,
|
||||
suppliedCity,
|
||||
} from '@sammo-ts/logic/constraints/presets.js';
|
||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||
import type {
|
||||
@@ -96,11 +98,15 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: DonateArgs): Constraint[] {
|
||||
return [notBeNeutral(), occupiedCity(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: DonateArgs): Constraint[] {
|
||||
if (args.isGold) {
|
||||
return [notBeNeutral(), notWanderingNation(), reqGeneralGold(() => args.amount)];
|
||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), suppliedCity(), reqGeneralGold(() => args.amount)];
|
||||
}
|
||||
return [notBeNeutral(), notWanderingNation(), reqGeneralRice(() => args.amount)];
|
||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), suppliedCity(), reqGeneralRice(() => args.amount)];
|
||||
}
|
||||
|
||||
resolve(context: GeneralActionResolveContext<TriggerState>, args: DonateArgs): GeneralActionOutcome<TriggerState> {
|
||||
|
||||
@@ -362,6 +362,17 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: FireAttackArgs): Constraint[] {
|
||||
const { gold, rice } = this.command.getCost();
|
||||
return [
|
||||
notBeNeutral(),
|
||||
occupiedCity(),
|
||||
suppliedCity(),
|
||||
reqGeneralGold(() => gold),
|
||||
reqGeneralRice(() => rice),
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: FireAttackArgs): Constraint[] {
|
||||
void _ctx;
|
||||
void _args;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext, StateView } from '@sammo-ts/logic/constraints/types.js';
|
||||
import { notBeNeutral, reqGeneralGold } from '@sammo-ts/logic/constraints/presets.js';
|
||||
import { notBeNeutral, notWanderingNation, occupiedCity, reqGeneralCrew, reqGeneralGold } 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 type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||
@@ -36,9 +36,13 @@ export class ActionDefinition<
|
||||
return {};
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: TrainingArgs): Constraint[] {
|
||||
return [notBeNeutral(), notWanderingNation(), occupiedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: TrainingArgs): Constraint[] {
|
||||
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
||||
return [notBeNeutral(), reqGeneralGold(getRequiredGold)];
|
||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), reqGeneralCrew(), reqGeneralGold(getRequiredGold)];
|
||||
}
|
||||
|
||||
resolve(
|
||||
|
||||
@@ -43,6 +43,10 @@ export class ActionDefinition<
|
||||
return {};
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: ReduceCityArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), suppliedCity()];
|
||||
}
|
||||
|
||||
private getRecoverAmount(): number {
|
||||
return this.env.develCost * COST_COEF + DEFAULT_COST / 2;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,25 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: ChangeNationNameArgs): Constraint[] {
|
||||
return [
|
||||
occupiedCity(),
|
||||
beChief(),
|
||||
suppliedCity(),
|
||||
{
|
||||
name: 'canChangeNationName',
|
||||
requires: (ctx) => [{ kind: 'nation', id: ctx.nationId! }],
|
||||
test: (_ctx: ConstraintContext, view: StateView) => {
|
||||
const nation = view.get({ kind: 'nation', id: _ctx.nationId! }) as Nation | undefined;
|
||||
const canChangeRaw = nation?.meta[`can_${ACTION_NAME}`];
|
||||
const canChange = (typeof canChangeRaw === 'number' ? canChangeRaw : 0) !== 0;
|
||||
if (!canChange) return { kind: 'deny', reason: '더이상 변경이 불가능합니다.' };
|
||||
return { kind: 'allow' };
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: ChangeNationNameArgs): Constraint[] {
|
||||
return [
|
||||
occupiedCity(),
|
||||
|
||||
@@ -174,6 +174,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: RaidArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), availableStrategicCommand()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: RaidArgs): Constraint[] {
|
||||
void _ctx;
|
||||
void _args;
|
||||
|
||||
@@ -4,6 +4,8 @@ import {
|
||||
beChief,
|
||||
existsDestGeneral,
|
||||
friendlyDestGeneral,
|
||||
notBeNeutral,
|
||||
notOpeningPart,
|
||||
occupiedCity,
|
||||
suppliedCity,
|
||||
} from '@sammo-ts/logic/constraints/presets.js';
|
||||
@@ -60,10 +62,20 @@ export class ActionDefinition<
|
||||
};
|
||||
}
|
||||
|
||||
buildMinConstraints(ctx: ConstraintContext, _args: SeizureArgs): Constraint[] {
|
||||
const relYear = typeof ctx.env.relYear === 'number' ? ctx.env.relYear : 0;
|
||||
const openingPartYear = typeof ctx.env.openingPartYear === 'number' ? ctx.env.openingPartYear : 0;
|
||||
return [notBeNeutral(), occupiedCity(), beChief(), notOpeningPart(relYear, openingPartYear), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: SeizureArgs): Constraint[] {
|
||||
const relYear = typeof _ctx.env.relYear === 'number' ? _ctx.env.relYear : 0;
|
||||
const openingPartYear = typeof _ctx.env.openingPartYear === 'number' ? _ctx.env.openingPartYear : 0;
|
||||
return [
|
||||
notBeNeutral(),
|
||||
occupiedCity(),
|
||||
beChief(),
|
||||
notOpeningPart(relYear, openingPartYear),
|
||||
suppliedCity(),
|
||||
existsDestGeneral(),
|
||||
friendlyDestGeneral(),
|
||||
|
||||
@@ -51,6 +51,25 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: MaterialAidArgs): Constraint[] {
|
||||
return [
|
||||
occupiedCity(),
|
||||
beChief(),
|
||||
suppliedCity(),
|
||||
{
|
||||
name: 'nationSurlimit',
|
||||
requires: (ctx) => [{ kind: 'nation', id: ctx.nationId! }],
|
||||
test: (_ctx: ConstraintContext, view: StateView) => {
|
||||
const nation = view.get({ kind: 'nation', id: _ctx.nationId! }) as Nation | undefined;
|
||||
const surlimitRaw = nation?.meta.surlimit;
|
||||
const surlimit = typeof surlimitRaw === 'number' ? surlimitRaw : 0;
|
||||
if (surlimit > 0) return { kind: 'deny', reason: '외교제한중입니다.' };
|
||||
return { kind: 'allow' };
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: MaterialAidArgs): Constraint[] {
|
||||
const [goldAmount, riceAmount] = args.amountList;
|
||||
return [
|
||||
|
||||
@@ -152,6 +152,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: AssignmentArgs): Constraint[] {
|
||||
return [beChief(), notBeNeutral(), occupiedCity(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: AssignmentArgs): Constraint[] {
|
||||
void _args;
|
||||
if (ctx.destGeneralId === ctx.actorId) {
|
||||
|
||||
@@ -152,6 +152,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: MobilizePeopleArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), availableStrategicCommand()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: MobilizePeopleArgs): Constraint[] {
|
||||
void _ctx;
|
||||
void _args;
|
||||
|
||||
@@ -48,6 +48,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: TroopKickArgs): Constraint[] {
|
||||
return [notBeNeutral(), beChief()];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, _args: TroopKickArgs): Constraint[] {
|
||||
if (ctx.destGeneralId !== undefined && ctx.destGeneralId === ctx.actorId) {
|
||||
return [alwaysFail('본인입니다')];
|
||||
|
||||
@@ -97,6 +97,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: NonAggressionProposalArgs): Constraint[] {
|
||||
return [beChief(), notBeNeutral()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: NonAggressionProposalArgs): Constraint[] {
|
||||
return [
|
||||
beChief(),
|
||||
|
||||
@@ -40,6 +40,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: NonAggressionCancelProposalArgs): Constraint[] {
|
||||
return [beChief(), notBeNeutral(), occupiedCity(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: NonAggressionCancelProposalArgs): Constraint[] {
|
||||
return [
|
||||
beChief(),
|
||||
|
||||
@@ -53,6 +53,24 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: DeclareWarArgs): Constraint[] {
|
||||
const relYear = typeof _ctx.env.relYear === 'number' ? _ctx.env.relYear : 0;
|
||||
return [
|
||||
notBeNeutral(),
|
||||
occupiedCity(),
|
||||
suppliedCity(),
|
||||
beChief(),
|
||||
{
|
||||
name: 'declareWarYearLimit',
|
||||
requires: () => [],
|
||||
test: () => {
|
||||
if (relYear >= 1) return { kind: 'allow' };
|
||||
return { kind: 'deny', reason: '초반제한 해제 2년전부터 가능합니다.' };
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: DeclareWarArgs): Constraint[] {
|
||||
const relYear = typeof _ctx.env.relYear === 'number' ? _ctx.env.relYear : 0;
|
||||
|
||||
|
||||
@@ -177,6 +177,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: FloodArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), availableStrategicCommand()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: FloodArgs): Constraint[] {
|
||||
void _ctx;
|
||||
void _args;
|
||||
|
||||
@@ -181,6 +181,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: DegradeRelationsArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), availableStrategicCommand()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: DegradeRelationsArgs): Constraint[] {
|
||||
void _ctx;
|
||||
void _args;
|
||||
|
||||
@@ -40,6 +40,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: StopWarProposalArgs): Constraint[] {
|
||||
return [beChief(), notBeNeutral(), occupiedCity(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: StopWarProposalArgs): Constraint[] {
|
||||
return [
|
||||
beChief(),
|
||||
|
||||
@@ -48,6 +48,10 @@ export class ActionDefinition<
|
||||
return {};
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: ExpandCityArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), suppliedCity()];
|
||||
}
|
||||
|
||||
private getCost(): number {
|
||||
return this.env.develCost * COST_COEF + DEFAULT_COST;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: MoveCapitalArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: MoveCapitalArgs): Constraint[] {
|
||||
const develcost = this.env.develCost;
|
||||
|
||||
|
||||
@@ -116,6 +116,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: ScorchedEarthArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), suppliedCity(), requireNoDiplomacyLimit()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: ScorchedEarthArgs): Constraint[] {
|
||||
void _ctx;
|
||||
return [
|
||||
|
||||
@@ -180,6 +180,10 @@ export class ActionDefinition<
|
||||
};
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: AwardArgs): Constraint[] {
|
||||
return [notBeNeutral(), occupiedCity(), beChief(), suppliedCity()];
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, args: AwardArgs): Constraint[] {
|
||||
const requirements: RequirementKey[] = [];
|
||||
if (ctx.cityId !== undefined) {
|
||||
|
||||
@@ -211,6 +211,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: CounterStrategyArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: CounterStrategyArgs): Constraint[] {
|
||||
void _ctx;
|
||||
void _args;
|
||||
|
||||
@@ -184,6 +184,10 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: DeceptionArgs): Constraint[] {
|
||||
return [occupiedCity(), beChief(), availableStrategicCommand()];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, _args: DeceptionArgs): Constraint[] {
|
||||
void _ctx;
|
||||
void _args;
|
||||
|
||||
@@ -65,6 +65,15 @@ export class ActionDefinition<
|
||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||
}
|
||||
|
||||
buildMinConstraints(_ctx: ConstraintContext, _args: PopulationMoveArgs): Constraint[] {
|
||||
return [
|
||||
occupiedCity(),
|
||||
beChief(),
|
||||
suppliedCity(),
|
||||
reqCityCapacity('population', '주민', MIN_AVAILABLE_RECRUIT_POP + 100),
|
||||
];
|
||||
}
|
||||
|
||||
buildConstraints(_ctx: ConstraintContext, args: PopulationMoveArgs): Constraint[] {
|
||||
const cost = calcCost(this.env.develCost, args.amount);
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user