코드
This commit is contained in:
@@ -1,11 +1,6 @@
|
||||
import type { GeneralTriggerState } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||
import {
|
||||
mustBeNPC,
|
||||
reqGeneralGold,
|
||||
unknownOrDeny,
|
||||
existsDestCity,
|
||||
} from '@sammo-ts/logic/constraints/presets.js';
|
||||
import { mustBeNPC, reqGeneralGold, unknownOrDeny, existsDestCity } from '@sammo-ts/logic/constraints/presets.js';
|
||||
import type { GeneralActionDefinition } from '@sammo-ts/logic/actions/definition.js';
|
||||
import type {
|
||||
GeneralActionOutcome,
|
||||
@@ -25,17 +20,16 @@ export interface NPCSelfArgs {
|
||||
destCityId?: number;
|
||||
}
|
||||
|
||||
export type NPCSelfResolveContext<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> = GeneralActionResolveContext<TriggerState> & {
|
||||
map?: MapDefinition;
|
||||
};
|
||||
export type NPCSelfResolveContext<TriggerState extends GeneralTriggerState = GeneralTriggerState> =
|
||||
GeneralActionResolveContext<TriggerState> & {
|
||||
map?: MapDefinition;
|
||||
};
|
||||
|
||||
const ACTION_NAME = 'NPC능동';
|
||||
const ACTION_KEY = 'che_NPC능동';
|
||||
|
||||
export class ActionResolver<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||
> implements GeneralActionResolver<TriggerState, NPCSelfArgs> {
|
||||
readonly key = ACTION_KEY;
|
||||
|
||||
@@ -85,7 +79,7 @@ export class ActionResolver<
|
||||
}
|
||||
|
||||
export class ActionDefinition<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||
> implements GeneralActionDefinition<TriggerState, NPCSelfArgs, NPCSelfResolveContext<TriggerState>> {
|
||||
public readonly key = ACTION_KEY;
|
||||
public readonly name = ACTION_NAME;
|
||||
@@ -111,9 +105,7 @@ export class ActionDefinition<
|
||||
}
|
||||
|
||||
buildConstraints(ctx: ConstraintContext, args: NPCSelfArgs): Constraint[] {
|
||||
const constraints = [
|
||||
mustBeNPC()
|
||||
];
|
||||
const constraints = [mustBeNPC()];
|
||||
|
||||
if (args.optionText === '순간이동') {
|
||||
constraints.push(existsDestCity());
|
||||
|
||||
@@ -28,7 +28,7 @@ export interface ForcedMoveArgs {
|
||||
}
|
||||
|
||||
export interface ForcedMoveResolveContext<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||
> extends GeneralActionResolveContext<TriggerState> {
|
||||
moveGenerals?: General<TriggerState>[]; // For roaming move
|
||||
map?: MapDefinition;
|
||||
@@ -39,7 +39,7 @@ const ACTION_NAME = '강행';
|
||||
const ACTION_KEY = 'che_강행';
|
||||
|
||||
export class ActionResolver<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||
> implements GeneralActionResolver<TriggerState, ForcedMoveArgs> {
|
||||
readonly key = ACTION_KEY;
|
||||
|
||||
@@ -97,7 +97,8 @@ export class ActionResolver<
|
||||
const nextTrain = Math.max(20, general.train - 5);
|
||||
const nextAtmos = Math.max(20, general.atmos - 5);
|
||||
const nextExp = general.experience + 100;
|
||||
const nextLeadershipExp = (typeof general.meta.leadership_exp === 'number' ? general.meta.leadership_exp : 0) + 1;
|
||||
const nextLeadershipExp =
|
||||
(typeof general.meta.leadership_exp === 'number' ? general.meta.leadership_exp : 0) + 1;
|
||||
|
||||
effects.push(
|
||||
createGeneralPatchEffect(
|
||||
@@ -128,14 +129,14 @@ export class ActionResolver<
|
||||
// Currently addLog attaches provided logs to turnLog (which is for the actor).
|
||||
// To log for OTHERS, we might need specific effect or handle it differently.
|
||||
// For now, I will omit logs for others or use a special effect if available.
|
||||
// The legacy TS porting pattern for "others" logs isn't fully standardized yet in shared snippets.
|
||||
// The legacy TS porting pattern for "others" logs isn't fully standardized yet in shared snippets.
|
||||
// Assuming createGeneralPatchEffect handles state. Logs for others might be missing in this iteration unless I find `createLogEffect`.
|
||||
|
||||
effects.push(
|
||||
createGeneralPatchEffect(
|
||||
{
|
||||
...target,
|
||||
cityId: destCityId
|
||||
cityId: destCityId,
|
||||
},
|
||||
target.id
|
||||
)
|
||||
@@ -148,7 +149,7 @@ export class ActionResolver<
|
||||
}
|
||||
|
||||
export class ActionDefinition<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState,
|
||||
> implements GeneralActionDefinition<TriggerState, ForcedMoveArgs, ForcedMoveResolveContext<TriggerState>> {
|
||||
public readonly key = ACTION_KEY;
|
||||
public readonly name = ACTION_NAME;
|
||||
@@ -173,7 +174,7 @@ export class ActionDefinition<
|
||||
const cost = ctx.env.develCost as number;
|
||||
return (cost ?? 0) * 5;
|
||||
}),
|
||||
reqGeneralRice(() => 0) // Legacy checks cost[1] which is 0, but included constraint.
|
||||
reqGeneralRice(() => 0), // Legacy checks cost[1] which is 0, but included constraint.
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import type { City, General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||
import {
|
||||
@@ -23,7 +22,7 @@ import type { TurnCommandEnv } from '@sammo-ts/logic/actions/turn/commandEnv.js'
|
||||
import type { ActionContextBuilder } from '@sammo-ts/logic/actions/turn/actionContext.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
export interface ReturnArgs { }
|
||||
export interface ReturnArgs {}
|
||||
|
||||
const ACTION_NAME = '귀환';
|
||||
const ACTION_KEY = 'che_귀환';
|
||||
@@ -71,7 +70,7 @@ export class ActionResolver<
|
||||
throw new Error('Destination city not found (No capital?).');
|
||||
}
|
||||
|
||||
// We need city name for log.
|
||||
// We need city name for log.
|
||||
// If we don't have city object in context, we can't log name easily unless we fetch it.
|
||||
// We should add `getWorldCity(id)` to context or something.
|
||||
// Or assume resolving involves looking up city.
|
||||
@@ -83,8 +82,8 @@ export class ActionResolver<
|
||||
// `GeneralActionResolveContext` usually doesn't have `worldRef`.
|
||||
// We must rely on `contextBuilder` to populate necessary data.
|
||||
|
||||
// Let's iterate `nationCities` if provided.
|
||||
// Or use `ActionContextBuilder` to fetch city by ID if we knew it?
|
||||
// Let's iterate `nationCities` if provided.
|
||||
// Or use `ActionContextBuilder` to fetch city by ID if we knew it?
|
||||
// We don't know ID until we check logic.
|
||||
|
||||
// In `ActionContextBuilder`, we can just load all cities of the nation?
|
||||
@@ -94,7 +93,7 @@ export class ActionResolver<
|
||||
let foundDestCity: City | undefined;
|
||||
|
||||
if (context.nationCities) {
|
||||
foundDestCity = context.nationCities.find(c => c.id === destCityId);
|
||||
foundDestCity = context.nationCities.find((c) => c.id === destCityId);
|
||||
if (foundDestCity) {
|
||||
destCityName = foundDestCity.name;
|
||||
}
|
||||
@@ -108,7 +107,7 @@ export class ActionResolver<
|
||||
|
||||
context.addLog(`<G><b>${destCityName}</b></>${josaRo} 귀환했습니다.`, {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH, // Or HM if we want HH:MM suffix?
|
||||
format: LogFormat.MONTH, // Or HM if we want HH:MM suffix?
|
||||
// Legacy uses explicit date at end.
|
||||
// We can just use standard MONTH format for now.
|
||||
});
|
||||
@@ -116,27 +115,32 @@ export class ActionResolver<
|
||||
const exp = 70;
|
||||
const ded = 100;
|
||||
|
||||
effects.push(createGeneralPatchEffect({
|
||||
...general,
|
||||
cityId: destCityId,
|
||||
experience: general.experience + exp,
|
||||
dedication: general.dedication + ded,
|
||||
stats: {
|
||||
...general.stats,
|
||||
leadership: general.stats.leadership, // Update not needed unless verified
|
||||
},
|
||||
// leadership_exp + 1 in legacy?
|
||||
// "increaseVar('leadership_exp', 1)"
|
||||
// General entity doesn't show leadership_exp in Interface?
|
||||
// Checking `entities.ts`... `stats` is Leadership/Strength/Intel.
|
||||
// `experience` is total exp?
|
||||
// If `leadership_exp` is missing in Entity, we must use meta or omit.
|
||||
// Default to meta if needed.
|
||||
meta: {
|
||||
...general.meta,
|
||||
leadership_exp: (readMetaNumberFromUnknown(general.meta, 'leadership_exp') ?? 0) + 1
|
||||
}
|
||||
}, general.id));
|
||||
effects.push(
|
||||
createGeneralPatchEffect(
|
||||
{
|
||||
...general,
|
||||
cityId: destCityId,
|
||||
experience: general.experience + exp,
|
||||
dedication: general.dedication + ded,
|
||||
stats: {
|
||||
...general.stats,
|
||||
leadership: general.stats.leadership, // Update not needed unless verified
|
||||
},
|
||||
// leadership_exp + 1 in legacy?
|
||||
// "increaseVar('leadership_exp', 1)"
|
||||
// General entity doesn't show leadership_exp in Interface?
|
||||
// Checking `entities.ts`... `stats` is Leadership/Strength/Intel.
|
||||
// `experience` is total exp?
|
||||
// If `leadership_exp` is missing in Entity, we must use meta or omit.
|
||||
// Default to meta if needed.
|
||||
meta: {
|
||||
...general.meta,
|
||||
leadership_exp: (readMetaNumberFromUnknown(general.meta, 'leadership_exp') ?? 0) + 1,
|
||||
},
|
||||
},
|
||||
general.id
|
||||
)
|
||||
);
|
||||
|
||||
return { effects };
|
||||
}
|
||||
@@ -173,9 +177,9 @@ export class ActionDefinition<
|
||||
export const actionContextBuilder: ActionContextBuilder = (base, options) => {
|
||||
return {
|
||||
...base,
|
||||
// We effectively need all cities to find the destination name if it's not the capital.
|
||||
// We effectively need all cities to find the destination name if it's not the capital.
|
||||
// Or at least nation cities.
|
||||
nationCities: options.worldRef?.listCities().filter(c => c.nationId === base.nation?.id) ?? [],
|
||||
nationCities: options.worldRef?.listCities().filter((c) => c.nationId === base.nation?.id) ?? [],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import type { General, GeneralTriggerState, Nation } from '@sammo-ts/logic/domain/entities.js';
|
||||
import type { Constraint, ConstraintContext } from '@sammo-ts/logic/constraints/types.js';
|
||||
import {
|
||||
@@ -45,7 +44,10 @@ export class ActionResolver<
|
||||
> implements GeneralActionResolver<TriggerState, AcceptScoutArgs> {
|
||||
readonly key = ACTION_KEY;
|
||||
|
||||
resolve(context: AcceptScoutResolveContext<TriggerState>, _args: AcceptScoutArgs): GeneralActionOutcome<TriggerState> {
|
||||
resolve(
|
||||
context: AcceptScoutResolveContext<TriggerState>,
|
||||
_args: AcceptScoutArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
const general = context.general;
|
||||
const currentNation = context.nation;
|
||||
const destNation = context.destNation;
|
||||
@@ -64,7 +66,8 @@ export class ActionResolver<
|
||||
const josaYi = JosaUtil.pick(generalName, '이');
|
||||
|
||||
// Self Log
|
||||
context.addLog(`<D>${destNationName}</>${josaRo} 망명하여 수도로 이동합니다.`, { // Text says "Move to Capital", but logic might move to recruiter city.
|
||||
context.addLog(`<D>${destNationName}</>${josaRo} 망명하여 수도로 이동합니다.`, {
|
||||
// Text says "Move to Capital", but logic might move to recruiter city.
|
||||
// Legacy log says "수도로 이동합니다", but implementation moves to destGeneral city if present!
|
||||
// We should match implementation or text? Text is just flavor.
|
||||
category: LogCategory.ACTION,
|
||||
@@ -89,10 +92,15 @@ export class ActionResolver<
|
||||
});
|
||||
|
||||
// 2. Recruiter Rewards
|
||||
effects.push(createGeneralPatchEffect({
|
||||
experience: destGeneral.experience + 100,
|
||||
dedication: destGeneral.dedication + 100,
|
||||
}, destGeneral.id));
|
||||
effects.push(
|
||||
createGeneralPatchEffect(
|
||||
{
|
||||
experience: destGeneral.experience + 100,
|
||||
dedication: destGeneral.dedication + 100,
|
||||
},
|
||||
destGeneral.id
|
||||
)
|
||||
);
|
||||
|
||||
// 3. Betrayal Logic
|
||||
// If currentNation exists (and > 0), handle betrayal return logic.
|
||||
@@ -107,7 +115,7 @@ export class ActionResolver<
|
||||
let newExp = general.experience;
|
||||
let newDed = general.dedication;
|
||||
|
||||
const betrayCount = (readMetaNumberFromUnknown(general.meta, 'betray') ?? 0);
|
||||
const betrayCount = readMetaNumberFromUnknown(general.meta, 'betray') ?? 0;
|
||||
let newBetray = betrayCount;
|
||||
|
||||
if (currentNation && currentNation.id !== 0) {
|
||||
@@ -125,16 +133,22 @@ export class ActionResolver<
|
||||
}
|
||||
|
||||
if (returnGold > 0 || returnRice > 0) {
|
||||
effects.push(createNationPatchEffect({
|
||||
gold: currentNation.gold + returnGold,
|
||||
rice: currentNation.rice + returnRice
|
||||
}, currentNation.id));
|
||||
effects.push(
|
||||
createNationPatchEffect(
|
||||
{
|
||||
gold: currentNation.gold + returnGold,
|
||||
rice: currentNation.rice + returnRice,
|
||||
},
|
||||
currentNation.id
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Penalty
|
||||
// 10% * betray count deduction
|
||||
const penaltyFactor = 1 - (0.1 * betrayCount);
|
||||
if (penaltyFactor < 0) { // Should not be less than 0? capped at ?
|
||||
const penaltyFactor = 1 - 0.1 * betrayCount;
|
||||
if (penaltyFactor < 0) {
|
||||
// Should not be less than 0? capped at ?
|
||||
// Legacy: (1 - 0.1 * betray).
|
||||
}
|
||||
// Apply penalty
|
||||
@@ -152,26 +166,31 @@ export class ActionResolver<
|
||||
// If recruiter is not valid city?
|
||||
if (!targetCityId) targetCityId = destNation.capitalCityId!;
|
||||
|
||||
effects.push(createGeneralPatchEffect({
|
||||
nationId: destNation.id,
|
||||
cityId: targetCityId,
|
||||
experience: newExp,
|
||||
dedication: newDed,
|
||||
gold: newGold,
|
||||
rice: newRice,
|
||||
officerLevel: 1, // Reset rank
|
||||
// officer_city: 0 via meta
|
||||
crew: general.crew, // Keep crew? Legacy implies checking troop leader.
|
||||
// If troop leader, disband troop.
|
||||
// TS entity `troopId`.
|
||||
troopId: 0, // Quit troop
|
||||
meta: {
|
||||
...general.meta,
|
||||
officer_city: 0,
|
||||
betray: newBetray,
|
||||
// killturn logic?
|
||||
}
|
||||
}, general.id));
|
||||
effects.push(
|
||||
createGeneralPatchEffect(
|
||||
{
|
||||
nationId: destNation.id,
|
||||
cityId: targetCityId,
|
||||
experience: newExp,
|
||||
dedication: newDed,
|
||||
gold: newGold,
|
||||
rice: newRice,
|
||||
officerLevel: 1, // Reset rank
|
||||
// officer_city: 0 via meta
|
||||
crew: general.crew, // Keep crew? Legacy implies checking troop leader.
|
||||
// If troop leader, disband troop.
|
||||
// TS entity `troopId`.
|
||||
troopId: 0, // Quit troop
|
||||
meta: {
|
||||
...general.meta,
|
||||
officer_city: 0,
|
||||
betray: newBetray,
|
||||
// killturn logic?
|
||||
},
|
||||
},
|
||||
general.id
|
||||
)
|
||||
);
|
||||
|
||||
// 5. Update Nations Gen Count (Visual only? or real count)
|
||||
// Legacy updates `gennum`.
|
||||
@@ -214,7 +233,10 @@ export class ActionDefinition<
|
||||
];
|
||||
}
|
||||
|
||||
resolve(context: AcceptScoutResolveContext<TriggerState>, args: AcceptScoutArgs): GeneralActionOutcome<TriggerState> {
|
||||
resolve(
|
||||
context: AcceptScoutResolveContext<TriggerState>,
|
||||
args: AcceptScoutArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
return this.resolver.resolve(context, args);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user