feat: 여러 커맨드의 제약 조건 개선 및 새로운 제약 조건 추가
This commit is contained in:
@@ -5,7 +5,6 @@ import {
|
|||||||
nearCity,
|
nearCity,
|
||||||
reqGeneralGold,
|
reqGeneralGold,
|
||||||
reqGeneralRice,
|
reqGeneralRice,
|
||||||
existsDestCity,
|
|
||||||
} from '@sammo-ts/logic/constraints/presets.js';
|
} from '@sammo-ts/logic/constraints/presets.js';
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
import type {
|
import type {
|
||||||
@@ -173,7 +172,6 @@ export class ActionDefinition<
|
|||||||
|
|
||||||
buildConstraints(ctx: ConstraintContext, _args: ForcedMoveArgs): Constraint[] {
|
buildConstraints(ctx: ConstraintContext, _args: ForcedMoveArgs): Constraint[] {
|
||||||
return [
|
return [
|
||||||
existsDestCity(),
|
|
||||||
notSameDestCity(),
|
notSameDestCity(),
|
||||||
nearCity(3),
|
nearCity(3),
|
||||||
reqGeneralGold((_c, _v) => {
|
reqGeneralGold((_c, _v) => {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
|
import type { GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
|
||||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||||
import { beNeutral } from '@sammo-ts/logic/constraints/presets.js';
|
import { allowJoinAction, beNeutral, beOpeningPart, noPenalty } from '@sammo-ts/logic/constraints/presets.js';
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
||||||
import {
|
import {
|
||||||
@@ -33,7 +33,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: UprisingArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: UprisingArgs): Constraint[] {
|
||||||
return [beNeutral()];
|
return [beNeutral(), beOpeningPart(), allowJoinAction(), noPenalty('noFoundNation')];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||||
import { occupiedCity, reqGeneralGold, reqGeneralRice, suppliedCity } from '@sammo-ts/logic/constraints/presets.js';
|
import {
|
||||||
|
occupiedCity,
|
||||||
|
reqCityTrader,
|
||||||
|
reqGeneralGold,
|
||||||
|
reqGeneralRice,
|
||||||
|
suppliedCity,
|
||||||
|
} from '@sammo-ts/logic/constraints/presets.js';
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
||||||
import { LogFormat } from '@sammo-ts/logic/logging/types.js';
|
import { LogFormat } from '@sammo-ts/logic/logging/types.js';
|
||||||
@@ -39,11 +45,11 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildMinConstraints(_ctx: ConstraintContext, _args: TradeArgs): Constraint[] {
|
buildMinConstraints(_ctx: ConstraintContext, _args: TradeArgs): Constraint[] {
|
||||||
return [occupiedCity(), suppliedCity()];
|
return [reqCityTrader(), occupiedCity({ allowNeutral: true }), suppliedCity()];
|
||||||
}
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, args: TradeArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, args: TradeArgs): Constraint[] {
|
||||||
const constraints: Constraint[] = [occupiedCity(), suppliedCity()];
|
const constraints: Constraint[] = [reqCityTrader(), occupiedCity({ allowNeutral: true }), suppliedCity()];
|
||||||
if (args.buyRice) {
|
if (args.buyRice) {
|
||||||
constraints.push(reqGeneralGold(() => 1));
|
constraints.push(reqGeneralGold(() => 1));
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
notWanderingNation,
|
notWanderingNation,
|
||||||
occupiedCity,
|
occupiedCity,
|
||||||
reqGeneralGold,
|
reqGeneralGold,
|
||||||
|
reqGeneralRice,
|
||||||
suppliedCity,
|
suppliedCity,
|
||||||
} from '@sammo-ts/logic/constraints/presets.js';
|
} from '@sammo-ts/logic/constraints/presets.js';
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
@@ -52,7 +53,14 @@ export class ActionDefinition<
|
|||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: TechResearchArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: TechResearchArgs): Constraint[] {
|
||||||
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
||||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), suppliedCity(), reqGeneralGold(getRequiredGold)];
|
return [
|
||||||
|
notBeNeutral(),
|
||||||
|
notWanderingNation(),
|
||||||
|
occupiedCity(),
|
||||||
|
suppliedCity(),
|
||||||
|
reqGeneralGold(getRequiredGold),
|
||||||
|
reqGeneralRice(() => 0),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ const ACTION_NAME = '내정 특기 초기화';
|
|||||||
const hasSpecial = (value: string | null | undefined): boolean =>
|
const hasSpecial = (value: string | null | undefined): boolean =>
|
||||||
value !== null && value !== undefined && value !== 'None';
|
value !== null && value !== undefined && value !== 'None';
|
||||||
|
|
||||||
const reqDomesticSpecial = (): Constraint => ({
|
const reqGeneralValue = (): Constraint => ({
|
||||||
name: 'ReqGeneralDomesticSpecial',
|
name: 'reqGeneralValue',
|
||||||
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
test: (ctx, view) => {
|
test: (ctx, view) => {
|
||||||
const general = readGeneral(ctx, view);
|
const general = readGeneral(ctx, view);
|
||||||
@@ -42,8 +42,12 @@ export class ActionDefinition<
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildMinConstraints(_ctx: ConstraintContext, _args: ResetSpecialDomesticArgs): Constraint[] {
|
||||||
|
return [reqGeneralValue()];
|
||||||
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: ResetSpecialDomesticArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: ResetSpecialDomesticArgs): Constraint[] {
|
||||||
return [reqDomesticSpecial()];
|
return [reqGeneralValue()];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ const pickByWeight = <T extends string>(rng: DrillContext['rng'], weights: Recor
|
|||||||
return last ? last[0] : first[0];
|
return last ? last[0] : first[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
const reqGeneralStat = (key: 'train' | 'atmos', label: string, minValue: number): Constraint => ({
|
const reqGeneralValue = (key: 'train' | 'atmos', label: string, minValue: number): Constraint => ({
|
||||||
name: `ReqGeneral${label}`,
|
name: 'reqGeneralValue',
|
||||||
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
test: (ctx, view) => {
|
test: (ctx, view) => {
|
||||||
const general = readGeneral(ctx, view);
|
const general = readGeneral(ctx, view);
|
||||||
@@ -103,8 +103,8 @@ export class ActionDefinition<
|
|||||||
return [
|
return [
|
||||||
notBeNeutral(),
|
notBeNeutral(),
|
||||||
reqGeneralCrew(),
|
reqGeneralCrew(),
|
||||||
reqGeneralStat('train', '훈련', trainLow),
|
reqGeneralValue('train', '훈련', trainLow),
|
||||||
reqGeneralStat('atmos', '사기', atmosLow),
|
reqGeneralValue('atmos', '사기', atmosLow),
|
||||||
reqGeneralGold(getRequiredGold),
|
reqGeneralGold(getRequiredGold),
|
||||||
reqGeneralRice(getRequiredRice),
|
reqGeneralRice(getRequiredRice),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ import {
|
|||||||
notBeNeutral,
|
notBeNeutral,
|
||||||
notWanderingNation,
|
notWanderingNation,
|
||||||
occupiedCity,
|
occupiedCity,
|
||||||
|
reqGeneralAtmosMargin,
|
||||||
reqGeneralCrew,
|
reqGeneralCrew,
|
||||||
reqGeneralGold,
|
reqGeneralGold,
|
||||||
|
reqGeneralRice,
|
||||||
} from '@sammo-ts/logic/constraints/presets.js';
|
} from '@sammo-ts/logic/constraints/presets.js';
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
||||||
@@ -49,7 +51,19 @@ export class ActionDefinition<
|
|||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: BoostMoraleArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: BoostMoraleArgs): Constraint[] {
|
||||||
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
||||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), reqGeneralCrew(), reqGeneralGold(getRequiredGold)];
|
const maxAtmos =
|
||||||
|
this.env.maxAtmosByCommand && this.env.maxAtmosByCommand > 0
|
||||||
|
? this.env.maxAtmosByCommand
|
||||||
|
: DEFAULT_MAX_ATMOS;
|
||||||
|
return [
|
||||||
|
notBeNeutral(),
|
||||||
|
notWanderingNation(),
|
||||||
|
occupiedCity(),
|
||||||
|
reqGeneralCrew(),
|
||||||
|
reqGeneralGold(getRequiredGold),
|
||||||
|
reqGeneralRice(() => 0),
|
||||||
|
reqGeneralAtmosMargin(maxAtmos),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ export class ActionDefinition<
|
|||||||
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
return parseArgsWithSchema(ARGS_SCHEMA, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildMinConstraints(_ctx: ConstraintContext, _args: DexTransferArgs): Constraint[] {
|
||||||
|
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.develCost ?? 0;
|
||||||
|
const getRequiredRice = (_context: ConstraintContext, _view: StateView): number => this.env.develCost ?? 0;
|
||||||
|
return [notBeNeutral(), occupiedCity(), reqGeneralGold(getRequiredGold), reqGeneralRice(getRequiredRice)];
|
||||||
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: DexTransferArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: DexTransferArgs): Constraint[] {
|
||||||
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.develCost ?? 0;
|
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.develCost ?? 0;
|
||||||
const getRequiredRice = (_context: ConstraintContext, _view: StateView): number => this.env.develCost ?? 0;
|
const getRequiredRice = (_context: ConstraintContext, _view: StateView): number => this.env.develCost ?? 0;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||||
import type { Constraint, ConstraintContext, StateView } from '@sammo-ts/logic/constraints/types.js';
|
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||||
import { notBeNeutral, reqGeneralGold } from '@sammo-ts/logic/constraints/presets.js';
|
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
||||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||||
@@ -34,8 +33,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: RecoveryArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: RecoveryArgs): Constraint[] {
|
||||||
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
return [];
|
||||||
return [notBeNeutral(), reqGeneralGold(getRequiredGold)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ const ACTION_KEY = 'che_은퇴';
|
|||||||
|
|
||||||
const REQ_AGE = 60;
|
const REQ_AGE = 60;
|
||||||
|
|
||||||
const reqAge = (): Constraint => ({
|
const reqGeneralValue = (): Constraint => ({
|
||||||
name: 'ReqAge',
|
name: 'reqGeneralValue',
|
||||||
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
test: (ctx, view) => {
|
test: (ctx, view) => {
|
||||||
const generalKey: RequirementKey = { kind: 'general', id: ctx.actorId };
|
const generalKey: RequirementKey = { kind: 'general', id: ctx.actorId };
|
||||||
@@ -118,7 +118,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: RetireArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: RetireArgs): Constraint[] {
|
||||||
return [reqAge()];
|
return [reqGeneralValue()];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(context: GeneralActionResolveContext<TriggerState>, args: RetireArgs): GeneralActionOutcome<TriggerState> {
|
resolve(context: GeneralActionResolveContext<TriggerState>, args: RetireArgs): GeneralActionOutcome<TriggerState> {
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ const ACTION_NAME = '전투 특기 초기화';
|
|||||||
const hasSpecial = (value: string | null | undefined): boolean =>
|
const hasSpecial = (value: string | null | undefined): boolean =>
|
||||||
value !== null && value !== undefined && value !== 'None';
|
value !== null && value !== undefined && value !== 'None';
|
||||||
|
|
||||||
const reqWarSpecial = (): Constraint => ({
|
const reqGeneralValue = (): Constraint => ({
|
||||||
name: 'ReqGeneralWarSpecial',
|
name: 'reqGeneralValue',
|
||||||
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
test: (ctx, view) => {
|
test: (ctx, view) => {
|
||||||
const general = readGeneral(ctx, view);
|
const general = readGeneral(ctx, view);
|
||||||
@@ -43,8 +43,12 @@ export class ActionDefinition<
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildMinConstraints(_ctx: ConstraintContext, _args: ResetSpecialWarArgs): Constraint[] {
|
||||||
|
return [reqGeneralValue()];
|
||||||
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: ResetSpecialWarArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: ResetSpecialWarArgs): Constraint[] {
|
||||||
return [reqWarSpecial()];
|
return [reqGeneralValue()];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import {
|
|||||||
notBeNeutral,
|
notBeNeutral,
|
||||||
notWanderingNation,
|
notWanderingNation,
|
||||||
occupiedCity,
|
occupiedCity,
|
||||||
remainCityCapacityByMax,
|
remainCityCapacity,
|
||||||
|
reqGeneralGold,
|
||||||
reqGeneralRice,
|
reqGeneralRice,
|
||||||
suppliedCity,
|
suppliedCity,
|
||||||
} from '@sammo-ts/logic/constraints/presets.js';
|
} from '@sammo-ts/logic/constraints/presets.js';
|
||||||
@@ -76,7 +77,8 @@ export class ActionDefinition<
|
|||||||
notWanderingNation(),
|
notWanderingNation(),
|
||||||
occupiedCity(),
|
occupiedCity(),
|
||||||
suppliedCity(),
|
suppliedCity(),
|
||||||
remainCityCapacityByMax('population', 'populationMax', '인구'),
|
reqGeneralGold(() => 0),
|
||||||
|
remainCityCapacity('population', '인구'),
|
||||||
reqGeneralRice(getRequiredRice),
|
reqGeneralRice(getRequiredRice),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
notBeNeutral,
|
notBeNeutral,
|
||||||
notWanderingNation,
|
notWanderingNation,
|
||||||
occupiedCity,
|
occupiedCity,
|
||||||
|
reqGeneralGold,
|
||||||
reqGeneralRice,
|
reqGeneralRice,
|
||||||
suppliedCity,
|
suppliedCity,
|
||||||
} from '@sammo-ts/logic/constraints/presets.js';
|
} from '@sammo-ts/logic/constraints/presets.js';
|
||||||
@@ -134,6 +135,7 @@ export class ActionDefinition<
|
|||||||
notWanderingNation(),
|
notWanderingNation(),
|
||||||
occupiedCity(),
|
occupiedCity(),
|
||||||
suppliedCity(),
|
suppliedCity(),
|
||||||
|
reqGeneralGold(() => 0, requirements),
|
||||||
reqGeneralRice(getRiceCost, requirements),
|
reqGeneralRice(getRiceCost, requirements),
|
||||||
remainCityTrust(),
|
remainCityTrust(),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||||
import { notBeNeutral } from '@sammo-ts/logic/constraints/presets.js';
|
import { notBeNeutral, notLord } from '@sammo-ts/logic/constraints/presets.js';
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
import type {
|
import type {
|
||||||
GeneralActionOutcome,
|
GeneralActionOutcome,
|
||||||
@@ -106,7 +106,7 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: ResignArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: ResignArgs): Constraint[] {
|
||||||
return [notBeNeutral()];
|
return [notBeNeutral(), notLord()];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(context: GeneralActionResolveContext<TriggerState>, args: ResignArgs): GeneralActionOutcome<TriggerState> {
|
resolve(context: GeneralActionResolveContext<TriggerState>, args: ResignArgs): GeneralActionOutcome<TriggerState> {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
|||||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||||
import {
|
import {
|
||||||
notBeNeutral,
|
notBeNeutral,
|
||||||
notWanderingNation,
|
|
||||||
occupiedCity,
|
occupiedCity,
|
||||||
reqGeneralGold,
|
reqGeneralGold,
|
||||||
reqGeneralRice,
|
reqGeneralRice,
|
||||||
@@ -107,9 +106,9 @@ export class ActionDefinition<
|
|||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, args: DonateArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, args: DonateArgs): Constraint[] {
|
||||||
if (args.isGold) {
|
if (args.isGold) {
|
||||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), suppliedCity(), reqGeneralGold(() => args.amount)];
|
return [notBeNeutral(), occupiedCity(), suppliedCity(), reqGeneralGold(() => args.amount)];
|
||||||
}
|
}
|
||||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), suppliedCity(), reqGeneralRice(() => args.amount)];
|
return [notBeNeutral(), occupiedCity(), suppliedCity(), reqGeneralRice(() => args.amount)];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(context: GeneralActionResolveContext<TriggerState>, args: DonateArgs): GeneralActionOutcome<TriggerState> {
|
resolve(context: GeneralActionResolveContext<TriggerState>, args: DonateArgs): GeneralActionOutcome<TriggerState> {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import type {
|
|||||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||||
import {
|
import {
|
||||||
disallowDiplomacyBetweenStatus,
|
disallowDiplomacyBetweenStatus,
|
||||||
existsDestCity,
|
|
||||||
notBeNeutral,
|
notBeNeutral,
|
||||||
notNeutralDestCity,
|
notNeutralDestCity,
|
||||||
notOccupiedDestCity,
|
notOccupiedDestCity,
|
||||||
@@ -393,7 +392,6 @@ export class ActionDefinition<
|
|||||||
notBeNeutral(),
|
notBeNeutral(),
|
||||||
occupiedCity(),
|
occupiedCity(),
|
||||||
suppliedCity(),
|
suppliedCity(),
|
||||||
existsDestCity(),
|
|
||||||
notOccupiedDestCity(),
|
notOccupiedDestCity(),
|
||||||
notNeutralDestCity(),
|
notNeutralDestCity(),
|
||||||
reqGeneralGold(() => gold),
|
reqGeneralGold(() => gold),
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||||
import type { Constraint, ConstraintContext, StateView } from '@sammo-ts/logic/constraints/types.js';
|
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||||
import { notBeNeutral, notWanderingNation, occupiedCity, reqGeneralCrew, reqGeneralGold } from '@sammo-ts/logic/constraints/presets.js';
|
import {
|
||||||
|
notBeNeutral,
|
||||||
|
notWanderingNation,
|
||||||
|
occupiedCity,
|
||||||
|
reqGeneralCrew,
|
||||||
|
reqGeneralTrainMargin,
|
||||||
|
} from '@sammo-ts/logic/constraints/presets.js';
|
||||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||||
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
import type { GeneralActionOutcome, GeneralActionResolveContext } from '@sammo-ts/logic/actions/engine.js';
|
||||||
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js';
|
||||||
@@ -42,8 +48,11 @@ export class ActionDefinition<
|
|||||||
}
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: TrainingArgs): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: TrainingArgs): Constraint[] {
|
||||||
const getRequiredGold = (_context: ConstraintContext, _view: StateView): number => this.env.costGold ?? 0;
|
const maxTrain =
|
||||||
return [notBeNeutral(), notWanderingNation(), occupiedCity(), reqGeneralCrew(), reqGeneralGold(getRequiredGold)];
|
this.env.maxTrainByCommand && this.env.maxTrainByCommand > 0
|
||||||
|
? this.env.maxTrainByCommand
|
||||||
|
: DEFAULT_MAX_TRAIN;
|
||||||
|
return [notBeNeutral(), notWanderingNation(), occupiedCity(), reqGeneralCrew(), reqGeneralTrainMargin(maxTrain)];
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(
|
resolve(
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ export interface EventResearchConfig {
|
|||||||
category?: string;
|
category?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const requireNationAux = (auxKey: string, actionName: string): Constraint => ({
|
const reqNationAuxValue = (auxKey: string, actionName: string): Constraint => ({
|
||||||
name: 'requireNationAux',
|
name: 'reqNationAuxValue',
|
||||||
requires: (ctx) => (ctx.nationId !== undefined ? [{ kind: 'nation', id: ctx.nationId }] : []),
|
requires: (ctx) => (ctx.nationId !== undefined ? [{ kind: 'nation', id: ctx.nationId }] : []),
|
||||||
test: (ctx: ConstraintContext, view: StateView) => {
|
test: (ctx: ConstraintContext, view: StateView) => {
|
||||||
if (ctx.nationId === undefined) {
|
if (ctx.nationId === undefined) {
|
||||||
@@ -69,13 +69,25 @@ export const createEventResearchCommand = (config: EventResearchConfig): {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buildMinConstraints(_ctx: ConstraintContext, _args: Record<string, never>): Constraint[] {
|
||||||
|
void _ctx;
|
||||||
|
void _args;
|
||||||
|
return [
|
||||||
|
occupiedCity(),
|
||||||
|
beChief(),
|
||||||
|
reqNationAuxValue(config.auxKey, ACTION_NAME),
|
||||||
|
reqNationGold(() => this.env.baseGold + COST),
|
||||||
|
reqNationRice(() => this.env.baseRice + COST),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
buildConstraints(_ctx: ConstraintContext, _args: Record<string, never>): Constraint[] {
|
buildConstraints(_ctx: ConstraintContext, _args: Record<string, never>): Constraint[] {
|
||||||
void _ctx;
|
void _ctx;
|
||||||
void _args;
|
void _args;
|
||||||
return [
|
return [
|
||||||
occupiedCity(),
|
occupiedCity(),
|
||||||
beChief(),
|
beChief(),
|
||||||
requireNationAux(config.auxKey, ACTION_NAME),
|
reqNationAuxValue(config.auxKey, ACTION_NAME),
|
||||||
reqNationGold(() => this.env.baseGold + COST),
|
reqNationGold(() => this.env.baseGold + COST),
|
||||||
reqNationRice(() => this.env.baseRice + COST),
|
reqNationRice(() => this.env.baseRice + COST),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -109,6 +109,42 @@ export const suppliedCity = (): Constraint => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const reqCityTrader = (): Constraint => ({
|
||||||
|
name: 'reqCityTrader',
|
||||||
|
requires: (ctx) => {
|
||||||
|
const reqs: RequirementKey[] = [{ kind: 'general', id: ctx.actorId }];
|
||||||
|
if (ctx.cityId !== undefined) {
|
||||||
|
reqs.push({ kind: 'city', id: ctx.cityId });
|
||||||
|
}
|
||||||
|
return reqs;
|
||||||
|
},
|
||||||
|
test: (ctx, view) => {
|
||||||
|
const generalReq: RequirementKey = { kind: 'general', id: ctx.actorId };
|
||||||
|
if (!view.has(generalReq)) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
const general = view.get(generalReq) as General | null;
|
||||||
|
if (!general) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const city = readCity(view, ctx.cityId);
|
||||||
|
if (!city) {
|
||||||
|
if (ctx.cityId === undefined) {
|
||||||
|
return unknownOrDeny(ctx, [], '도시 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
const req: RequirementKey = { kind: 'city', id: ctx.cityId };
|
||||||
|
return unknownOrDeny(ctx, [req], '도시 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const trade = readMetaNumberFromUnknown(city.meta, 'trade');
|
||||||
|
if (trade !== null || general.npcState >= 2) {
|
||||||
|
return allow();
|
||||||
|
}
|
||||||
|
return { kind: 'deny', reason: '도시에 상인이 없습니다.' };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const suppliedDestCity = (): Constraint => ({
|
export const suppliedDestCity = (): Constraint => ({
|
||||||
name: 'suppliedDestCity',
|
name: 'suppliedDestCity',
|
||||||
requires: (ctx) =>
|
requires: (ctx) =>
|
||||||
|
|||||||
@@ -145,6 +145,98 @@ export const reqGeneralCrew = (): Constraint => ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const reqGeneralTrainMargin = (maxTrain: number): Constraint => ({
|
||||||
|
name: 'reqGeneralTrainMargin',
|
||||||
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
|
test: (ctx, view) => {
|
||||||
|
const generalReq: RequirementKey = { kind: 'general', id: ctx.actorId };
|
||||||
|
if (!view.has(generalReq)) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
const general = view.get(generalReq) as General | null;
|
||||||
|
if (!general) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
if (general.train < maxTrain) {
|
||||||
|
return allow();
|
||||||
|
}
|
||||||
|
return { kind: 'deny', reason: '병사들은 이미 정예병사들입니다.' };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const reqGeneralAtmosMargin = (maxAtmos: number): Constraint => ({
|
||||||
|
name: 'reqGeneralAtmosMargin',
|
||||||
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
|
test: (ctx, view) => {
|
||||||
|
const generalReq: RequirementKey = { kind: 'general', id: ctx.actorId };
|
||||||
|
if (!view.has(generalReq)) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
const general = view.get(generalReq) as General | null;
|
||||||
|
if (!general) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
if (general.atmos < maxAtmos) {
|
||||||
|
return allow();
|
||||||
|
}
|
||||||
|
return { kind: 'deny', reason: '이미 사기는 하늘을 찌를듯 합니다.' };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const allowJoinAction = (): Constraint => ({
|
||||||
|
name: 'allowJoinAction',
|
||||||
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
|
test: (ctx, view) => {
|
||||||
|
const generalReq: RequirementKey = { kind: 'general', id: ctx.actorId };
|
||||||
|
if (!view.has(generalReq)) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
const general = view.get(generalReq) as General | null;
|
||||||
|
if (!general) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const makelimit = typeof general.meta.makelimit === 'number' ? general.meta.makelimit : 0;
|
||||||
|
if (makelimit === 0) {
|
||||||
|
return allow();
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinActionLimit = typeof ctx.env.joinActionLimit === 'number' ? ctx.env.joinActionLimit : 12;
|
||||||
|
return { kind: 'deny', reason: `재야가 된지 ${joinActionLimit}턴이 지나야 합니다.` };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const noPenalty = (penaltyKey: string): Constraint => ({
|
||||||
|
name: 'noPenalty',
|
||||||
|
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
|
||||||
|
test: (ctx, view) => {
|
||||||
|
const generalReq: RequirementKey = { kind: 'general', id: ctx.actorId };
|
||||||
|
if (!view.has(generalReq)) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
const general = view.get(generalReq) as General | null;
|
||||||
|
if (!general) {
|
||||||
|
return unknownOrDeny(ctx, [generalReq], '장수 정보가 없습니다.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const penalty = general.meta.penalty;
|
||||||
|
if (!penalty || typeof penalty !== 'object' || Array.isArray(penalty)) {
|
||||||
|
return allow();
|
||||||
|
}
|
||||||
|
|
||||||
|
const penaltyMap = penalty as Record<string, unknown>;
|
||||||
|
if (!Object.prototype.hasOwnProperty.call(penaltyMap, penaltyKey)) {
|
||||||
|
return allow();
|
||||||
|
}
|
||||||
|
|
||||||
|
const reason = penaltyMap[penaltyKey];
|
||||||
|
return {
|
||||||
|
kind: 'deny',
|
||||||
|
reason: `징계 사유: ${typeof reason === 'string' ? reason : String(reason)}`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const reqGeneralCrewMargin = (
|
export const reqGeneralCrewMargin = (
|
||||||
getCrewTypeId: (ctx: ConstraintContext, view: StateView) => number | null,
|
getCrewTypeId: (ctx: ConstraintContext, view: StateView) => number | null,
|
||||||
requirements: RequirementKey[] = []
|
requirements: RequirementKey[] = []
|
||||||
|
|||||||
Reference in New Issue
Block a user