Refactor action effects to use direct state mutations and logging
- Removed the use of createGeneralPatchEffect and createLogEffect in various action definitions. - Replaced effects with direct mutations of the state for general and city entities. - Updated logging to use context.addLog for better clarity and consistency. - Ensured that all actions now return an empty effects array after processing.
This commit is contained in:
@@ -369,7 +369,7 @@ export const createReservedTurnHandler = async (options: {
|
||||
|
||||
const resolution = resolveGeneralAction(
|
||||
definition,
|
||||
actionContext as GeneralActionResolveContext,
|
||||
actionContext,
|
||||
{
|
||||
now: currentGeneral.turnTime,
|
||||
schedule: context.schedule,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { RandomGenerator } from '@sammo-ts/common';
|
||||
import { enablePatches, produceWithPatches, type Draft, castDraft } from 'immer';
|
||||
import { enablePatches, produceWithPatches, castDraft } from 'immer';
|
||||
import type {
|
||||
City,
|
||||
General,
|
||||
@@ -34,8 +34,16 @@ export interface GeneralActionResolveContext<
|
||||
rng: RandomGenerator;
|
||||
city?: City;
|
||||
nation?: Nation | null;
|
||||
addLog(
|
||||
message: string,
|
||||
options?: Partial<Omit<LogEntryDraft, 'text'>>
|
||||
): void;
|
||||
}
|
||||
|
||||
export type GeneralActionResolveInputContext<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> = Omit<GeneralActionResolveContext<TriggerState>, 'addLog'>;
|
||||
|
||||
export interface TurnScheduleContext {
|
||||
now: Date;
|
||||
schedule: TurnSchedule;
|
||||
@@ -130,106 +138,6 @@ export interface GeneralActionResolution {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Immer Draft에 Effect를 적용한다.
|
||||
* 기존 Effect 기반 코드를 유지하면서 Draft에 즉시 반영하기 위함.
|
||||
*/
|
||||
export const applyEffectToDraft = <
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
>(
|
||||
draft: Draft<WorldState<TriggerState>>,
|
||||
effect: GeneralActionEffect<TriggerState>,
|
||||
context: { generalId: GeneralId; cityId?: CityId; nationId?: NationId }
|
||||
): void => {
|
||||
const generalDraft = draft.general as any;
|
||||
switch (effect.type) {
|
||||
case 'general:patch':
|
||||
if (
|
||||
effect.targetId === undefined ||
|
||||
effect.targetId === context.generalId
|
||||
) {
|
||||
Object.assign(generalDraft, effect.patch);
|
||||
if (effect.patch.stats) {
|
||||
generalDraft.stats = {
|
||||
...generalDraft.stats,
|
||||
...effect.patch.stats,
|
||||
};
|
||||
}
|
||||
if (effect.patch.role) {
|
||||
generalDraft.role = {
|
||||
...generalDraft.role,
|
||||
...effect.patch.role,
|
||||
items: {
|
||||
...generalDraft.role.items,
|
||||
...(effect.patch.role.items ?? {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (effect.patch.triggerState) {
|
||||
generalDraft.triggerState = {
|
||||
...generalDraft.triggerState,
|
||||
...effect.patch.triggerState,
|
||||
flags: {
|
||||
...generalDraft.triggerState.flags,
|
||||
...(effect.patch.triggerState.flags ?? {}),
|
||||
},
|
||||
counters: {
|
||||
...generalDraft.triggerState.counters,
|
||||
...(effect.patch.triggerState.counters ?? {}),
|
||||
},
|
||||
modifiers: {
|
||||
...generalDraft.triggerState.modifiers,
|
||||
...(effect.patch.triggerState.modifiers ?? {}),
|
||||
},
|
||||
meta: {
|
||||
...generalDraft.triggerState.meta,
|
||||
...(effect.patch.triggerState.meta ?? {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
if (effect.patch.meta) {
|
||||
generalDraft.meta = {
|
||||
...generalDraft.meta,
|
||||
...effect.patch.meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'city:patch':
|
||||
if (
|
||||
draft.city &&
|
||||
(effect.targetId === undefined ||
|
||||
effect.targetId === context.cityId)
|
||||
) {
|
||||
Object.assign(draft.city, effect.patch);
|
||||
if (effect.patch.meta) {
|
||||
draft.city.meta = {
|
||||
...draft.city.meta,
|
||||
...effect.patch.meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'nation:patch':
|
||||
if (
|
||||
draft.nation &&
|
||||
(effect.targetId === undefined ||
|
||||
effect.targetId === context.nationId)
|
||||
) {
|
||||
Object.assign(draft.nation, effect.patch);
|
||||
if (effect.patch.meta) {
|
||||
draft.nation.meta = {
|
||||
...draft.nation.meta,
|
||||
...effect.patch.meta,
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
export const createGeneralPatchEffect = <
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
>(
|
||||
@@ -303,7 +211,7 @@ export const resolveGeneralAction = <
|
||||
Args = unknown
|
||||
>(
|
||||
resolver: GeneralActionResolver<TriggerState, Args>,
|
||||
context: GeneralActionResolveContext<TriggerState>,
|
||||
context: GeneralActionResolveInputContext<TriggerState>,
|
||||
scheduleContext: TurnScheduleContext,
|
||||
args: Args
|
||||
): GeneralActionResolution => {
|
||||
@@ -323,12 +231,56 @@ export const resolveGeneralAction = <
|
||||
nation: context.nation,
|
||||
} as WorldState<TriggerState>,
|
||||
(draft) => {
|
||||
const addLog = (
|
||||
message: string,
|
||||
options: Partial<Omit<LogEntryDraft, 'text'>> = {}
|
||||
) => {
|
||||
const entry: LogEntryDraft = {
|
||||
scope: options.scope ?? LogScope.GENERAL,
|
||||
category: options.category ?? LogCategory.ACTION,
|
||||
text: message,
|
||||
format: options.format ?? LogFormat.MONTH,
|
||||
...options,
|
||||
};
|
||||
|
||||
switch (entry.scope) {
|
||||
case LogScope.GENERAL:
|
||||
logs.push({
|
||||
...entry,
|
||||
generalId: entry.generalId ?? context.general.id,
|
||||
});
|
||||
break;
|
||||
case LogScope.NATION:
|
||||
if (entry.nationId !== undefined) {
|
||||
logs.push(entry);
|
||||
break;
|
||||
}
|
||||
if (context.nation?.id !== undefined) {
|
||||
logs.push({
|
||||
...entry,
|
||||
nationId: context.nation.id,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case LogScope.USER:
|
||||
if (entry.userId) {
|
||||
logs.push(entry);
|
||||
}
|
||||
break;
|
||||
case LogScope.SYSTEM:
|
||||
default:
|
||||
logs.push(entry);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const outcome = resolver.resolve(
|
||||
{
|
||||
...context,
|
||||
general: castDraft(draft.general),
|
||||
city: castDraft(draft.city),
|
||||
nation: castDraft(draft.nation),
|
||||
addLog,
|
||||
} as GeneralActionResolveContext<TriggerState>,
|
||||
args
|
||||
);
|
||||
@@ -336,38 +288,7 @@ export const resolveGeneralAction = <
|
||||
for (const effect of outcome.effects) {
|
||||
switch (effect.type) {
|
||||
case 'log':
|
||||
// 로그 대상이 비어 있으면 현재 장수/국가 기준으로 보정한다.
|
||||
switch (effect.entry.scope) {
|
||||
case LogScope.GENERAL:
|
||||
logs.push({
|
||||
...effect.entry,
|
||||
generalId:
|
||||
effect.entry.generalId ??
|
||||
context.general.id,
|
||||
});
|
||||
break;
|
||||
case LogScope.NATION:
|
||||
if (effect.entry.nationId !== undefined) {
|
||||
logs.push(effect.entry);
|
||||
break;
|
||||
}
|
||||
if (context.nation?.id !== undefined) {
|
||||
logs.push({
|
||||
...effect.entry,
|
||||
nationId: context.nation.id,
|
||||
});
|
||||
}
|
||||
break;
|
||||
case LogScope.USER:
|
||||
if (effect.entry.userId) {
|
||||
logs.push(effect.entry);
|
||||
}
|
||||
break;
|
||||
case LogScope.SYSTEM:
|
||||
default:
|
||||
logs.push(effect.entry);
|
||||
break;
|
||||
}
|
||||
addLog(effect.entry.text, effect.entry);
|
||||
break;
|
||||
case 'schedule:override':
|
||||
nextTurnAtOverride = effect.nextTurnAt;
|
||||
@@ -378,16 +299,7 @@ export const resolveGeneralAction = <
|
||||
case 'general:patch':
|
||||
case 'city:patch':
|
||||
case 'nation:patch':
|
||||
applyEffectToDraft(draft, effect, {
|
||||
generalId: context.general.id,
|
||||
...(context.city?.id !== undefined
|
||||
? { cityId: context.city.id }
|
||||
: {}),
|
||||
...(context.nation?.id !== undefined
|
||||
? { nationId: context.nation.id }
|
||||
: {}),
|
||||
});
|
||||
// 타겟이 다른 경우 patches에 추가 (applyEffectToDraft에서 처리되지 않은 경우)
|
||||
// 타겟이 다른 경우 patches에 추가
|
||||
if (
|
||||
effect.type === 'general:patch' &&
|
||||
effect.targetId !== undefined &&
|
||||
|
||||
@@ -6,8 +6,7 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createGeneralPatchEffect, createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import { LogCategory, LogFormat } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -35,24 +34,19 @@ export class ActionDefinition<
|
||||
_args: UprisingArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
const general = context.general;
|
||||
const meta = {
|
||||
...general.meta,
|
||||
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.meta = {
|
||||
...general.meta as object,
|
||||
uprising: true as TriggerValue,
|
||||
};
|
||||
|
||||
return {
|
||||
effects: [
|
||||
createGeneralPatchEffect<TriggerState>(
|
||||
{ meta } as Partial<typeof general>,
|
||||
general.id
|
||||
),
|
||||
createLogEffect(`${ACTION_NAME}을 준비했습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
context.addLog(`${ACTION_NAME}을 준비했습니다.`, {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,7 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createGeneralPatchEffect, createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import { LogCategory, LogFormat } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -35,24 +34,19 @@ export class ActionDefinition<
|
||||
_args: FoundingArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
const general = context.general;
|
||||
const meta = {
|
||||
...general.meta,
|
||||
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.meta = {
|
||||
...general.meta as object,
|
||||
founding: true as TriggerValue,
|
||||
};
|
||||
|
||||
return {
|
||||
effects: [
|
||||
createGeneralPatchEffect<TriggerState>(
|
||||
{ meta } as Partial<typeof general>,
|
||||
general.id
|
||||
),
|
||||
createLogEffect(`${ACTION_NAME}을 준비했습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
context.addLog(`${ACTION_NAME}을 준비했습니다.`, {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type {
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
Nation,
|
||||
} from '../../../domain/entities.js';
|
||||
@@ -20,12 +19,6 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import {
|
||||
createGeneralPatchEffect,
|
||||
createLogEffect,
|
||||
createNationPatchEffect,
|
||||
} from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -84,17 +77,10 @@ export class ActionDefinition<
|
||||
_args: TechResearchArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
const general = context.general;
|
||||
const nation = context.nation ?? null;
|
||||
const nation = context.nation;
|
||||
if (!nation) {
|
||||
return {
|
||||
effects: [
|
||||
createLogEffect('국가 정보를 찾지 못했습니다.', {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
context.addLog('국가 정보를 찾지 못했습니다.');
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const delta = this.env.techDelta ?? DEFAULT_TECH_DELTA;
|
||||
@@ -107,26 +93,14 @@ export class ActionDefinition<
|
||||
const nextTech = Math.min(currentTech + delta, maxTech);
|
||||
const applied = nextTech - currentTech;
|
||||
const costGold = this.env.costGold ?? 0;
|
||||
const generalPatch: Partial<General<TriggerState>> = {
|
||||
gold: Math.max(0, general.gold - costGold),
|
||||
};
|
||||
|
||||
return {
|
||||
effects: [
|
||||
createNationPatchEffect(
|
||||
{
|
||||
meta: { ...nation.meta, tech: nextTech },
|
||||
} as Partial<Nation>,
|
||||
nation.id
|
||||
),
|
||||
createGeneralPatchEffect<TriggerState>(generalPatch, general.id),
|
||||
createLogEffect(`${ACTION_NAME}로 기술이 ${applied} 상승했습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
// 직접 수정 (Immer Draft)
|
||||
nation.meta = { ...nation.meta, tech: nextTech };
|
||||
general.gold = Math.max(0, general.gold - costGold);
|
||||
|
||||
context.addLog(`${ACTION_NAME}로 기술이 ${applied} 상승했습니다.`);
|
||||
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type {
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
} from '../../../domain/entities.js';
|
||||
import type {
|
||||
@@ -13,8 +12,6 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createGeneralPatchEffect, createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -74,21 +71,14 @@ export class ActionDefinition<
|
||||
const nextAtmos = clamp(general.atmos + delta, 0, maxAtmos);
|
||||
const applied = nextAtmos - general.atmos;
|
||||
const costGold = this.env.costGold ?? 0;
|
||||
const patch: Partial<General<TriggerState>> = {
|
||||
atmos: nextAtmos,
|
||||
gold: Math.max(0, general.gold - costGold),
|
||||
};
|
||||
|
||||
return {
|
||||
effects: [
|
||||
createGeneralPatchEffect<TriggerState>(patch, general.id),
|
||||
createLogEffect(`${ACTION_NAME}로 사기가 ${applied} 증가했습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.atmos = nextAtmos;
|
||||
general.gold = Math.max(0, general.gold - costGold);
|
||||
|
||||
context.addLog(`${ACTION_NAME}로 사기가 ${applied} 증가했습니다.`);
|
||||
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,12 +30,6 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolver,
|
||||
GeneralActionResolveContext,
|
||||
GeneralActionEffect,
|
||||
} from '../../engine.js';
|
||||
import {
|
||||
createCityPatchEffect,
|
||||
createGeneralPatchEffect,
|
||||
createLogEffect,
|
||||
} from '../../engine.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
@@ -335,36 +329,24 @@ export class ActionResolver<
|
||||
context.rng
|
||||
);
|
||||
|
||||
const updatedCommerce = clamp(
|
||||
// 직접 수정 (Immer Draft)
|
||||
city.commerce = clamp(
|
||||
city.commerce + result.score,
|
||||
0,
|
||||
city.commerceMax
|
||||
);
|
||||
|
||||
const nextGold = Math.max(0, general.gold - result.costGold);
|
||||
const nextRice = Math.max(0, general.rice - result.costRice);
|
||||
const nextExperience = general.experience + result.exp;
|
||||
const nextDedication = general.dedication + result.dedication;
|
||||
general.gold = Math.max(0, general.gold - result.costGold);
|
||||
general.rice = Math.max(0, general.rice - result.costRice);
|
||||
general.experience += result.exp;
|
||||
general.dedication += result.dedication;
|
||||
|
||||
const metaWithStatExp = addMetaNumber(general.meta, STAT_EXP_KEY, 1);
|
||||
const metaUpdated =
|
||||
general.meta =
|
||||
result.pick === 'success'
|
||||
? { ...metaWithStatExp, max_domestic_critical: result.score }
|
||||
: { ...metaWithStatExp, max_domestic_critical: 0 };
|
||||
|
||||
const effects: Array<GeneralActionEffect<TriggerState>> = [
|
||||
createCityPatchEffect({
|
||||
[CITY_KEY]: updatedCommerce,
|
||||
} as Partial<City>),
|
||||
createGeneralPatchEffect({
|
||||
gold: nextGold,
|
||||
rice: nextRice,
|
||||
experience: nextExperience,
|
||||
dedication: nextDedication,
|
||||
meta: metaUpdated,
|
||||
}),
|
||||
];
|
||||
|
||||
const pickLabel =
|
||||
result.pick === 'success'
|
||||
? '성공'
|
||||
@@ -372,9 +354,9 @@ export class ActionResolver<
|
||||
? '실패'
|
||||
: '완료';
|
||||
const logMessage = `${ACTION_NAME} ${pickLabel}: +${Math.round(result.score)}`;
|
||||
effects.push(createLogEffect(logMessage));
|
||||
context.addLog(logMessage);
|
||||
|
||||
return { effects };
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type {
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
} from '../../../domain/entities.js';
|
||||
import type {
|
||||
@@ -13,8 +12,6 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createGeneralPatchEffect, createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -62,21 +59,14 @@ export class ActionDefinition<
|
||||
const nextInjury = Math.max(0, general.injury - delta);
|
||||
const applied = general.injury - nextInjury;
|
||||
const costGold = this.env.costGold ?? 0;
|
||||
const patch: Partial<General<TriggerState>> = {
|
||||
injury: nextInjury,
|
||||
gold: Math.max(0, general.gold - costGold),
|
||||
};
|
||||
|
||||
return {
|
||||
effects: [
|
||||
createGeneralPatchEffect<TriggerState>(patch, general.id),
|
||||
createLogEffect(`${ACTION_NAME}으로 부상이 ${applied} 회복되었습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.injury = nextInjury;
|
||||
general.gold = Math.max(0, general.gold - costGold);
|
||||
|
||||
context.addLog(`${ACTION_NAME}으로 부상이 ${applied} 회복되었습니다.`);
|
||||
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,9 +27,6 @@ import type {
|
||||
} from '../../engine.js';
|
||||
import {
|
||||
createGeneralAddEffect,
|
||||
createGeneralPatchEffect,
|
||||
createLogEffect,
|
||||
createNationPatchEffect,
|
||||
} from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import { buildRecruitmentGeneral } from './recruitment.js';
|
||||
@@ -250,47 +247,34 @@ export class ActionResolver<
|
||||
_args: VolunteerRecruitArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
void _args;
|
||||
const effects: Array<GeneralActionEffect<TriggerState>> = [];
|
||||
const nation = context.nation ?? null;
|
||||
const general = context.general;
|
||||
const nation = context.nation;
|
||||
|
||||
const expGain = 5 * (DEFAULT_PRE_TURN + 1);
|
||||
const dedGain = 5 * (DEFAULT_PRE_TURN + 1);
|
||||
effects.push(
|
||||
createGeneralPatchEffect({
|
||||
experience: context.general.experience + expGain,
|
||||
dedication: context.general.dedication + dedGain,
|
||||
})
|
||||
);
|
||||
|
||||
effects.push(
|
||||
createLogEffect(`${ACTION_NAME} 발동!`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
})
|
||||
);
|
||||
effects.push(
|
||||
createLogEffect(`${ACTION_NAME} 발동`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
})
|
||||
);
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.experience += expGain;
|
||||
general.dedication += dedGain;
|
||||
|
||||
if (nation?.id) {
|
||||
const generalName = context.general.name;
|
||||
context.addLog(`${ACTION_NAME} 발동!`);
|
||||
context.addLog(`${ACTION_NAME} 발동`, {
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
});
|
||||
|
||||
if (nation) {
|
||||
const generalName = general.name;
|
||||
const generalJosa = JosaUtil.pick(generalName, '이');
|
||||
const actionJosa = JosaUtil.pick(ACTION_NAME, '을');
|
||||
effects.push(
|
||||
createLogEffect(
|
||||
`<Y>${generalName}</>${generalJosa} <M>${ACTION_NAME}</>${actionJosa} 발동했습니다.`,
|
||||
{
|
||||
scope: LogScope.NATION,
|
||||
category: LogCategory.HISTORY,
|
||||
nationId: nation.id,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
}
|
||||
)
|
||||
context.addLog(
|
||||
`<Y>${generalName}</>${generalJosa} <M>${ACTION_NAME}</>${actionJosa} 발동했습니다.`,
|
||||
{
|
||||
scope: LogScope.NATION,
|
||||
category: LogCategory.HISTORY,
|
||||
nationId: nation.id,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -310,16 +294,15 @@ export class ActionResolver<
|
||||
const globalDelay = this.command.getGlobalDelay(context);
|
||||
|
||||
if (nation) {
|
||||
effects.push(
|
||||
createNationPatchEffect({
|
||||
meta: {
|
||||
gennum: nextGennum,
|
||||
strategic_cmd_limit: globalDelay,
|
||||
},
|
||||
}, nation.id)
|
||||
);
|
||||
nation.meta = {
|
||||
...nation.meta as object,
|
||||
gennum: nextGennum,
|
||||
strategic_cmd_limit: globalDelay,
|
||||
};
|
||||
}
|
||||
|
||||
const effects: Array<GeneralActionEffect<TriggerState>> = [];
|
||||
|
||||
const baseAge = this.env.npcAge ?? DEFAULT_NPC_AGE;
|
||||
const deathYears = this.env.npcDeathYears ?? DEFAULT_NPC_DEATH_YEARS;
|
||||
const killTurnMin = this.env.killTurnMin ?? DEFAULT_KILLTURN_MIN;
|
||||
|
||||
@@ -20,14 +20,11 @@ import {
|
||||
} from '../../../triggers/general-action.js';
|
||||
import type { GeneralActionDefinition } from '../../definition.js';
|
||||
import type {
|
||||
GeneralActionEffect,
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import {
|
||||
createGeneralAddEffect,
|
||||
createGeneralPatchEffect,
|
||||
createLogEffect,
|
||||
} from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import { buildRecruitmentGeneral } from './recruitment.js';
|
||||
@@ -301,40 +298,35 @@ export class ActionResolver<
|
||||
_args: TalentScoutArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
void _args;
|
||||
const general = context.general;
|
||||
const { gold: reqGold, rice: reqRice } = this.command.getCost();
|
||||
const prop = this.command.calcFoundProp(context);
|
||||
const found = context.rng.nextBool(prop);
|
||||
|
||||
const statKey = pickStatExpKey(context.rng, context.general);
|
||||
const statKey = pickStatExpKey(context.rng, general);
|
||||
const metaAfter =
|
||||
found
|
||||
? addMetaNumber(context.general.meta, statKey, 3)
|
||||
: addMetaNumber(context.general.meta, statKey, 1);
|
||||
? addMetaNumber(general.meta, statKey, 3)
|
||||
: addMetaNumber(general.meta, statKey, 1);
|
||||
|
||||
const nextGold = Math.max(0, context.general.gold - reqGold);
|
||||
const nextRice = Math.max(0, context.general.rice - reqRice);
|
||||
const nextGold = Math.max(0, general.gold - reqGold);
|
||||
const nextRice = Math.max(0, general.rice - reqRice);
|
||||
const expGain = found ? 200 : 100;
|
||||
const dedGain = found ? 300 : 70;
|
||||
|
||||
const effects: Array<GeneralActionEffect<TriggerState>> = [
|
||||
createGeneralPatchEffect({
|
||||
gold: nextGold,
|
||||
rice: nextRice,
|
||||
experience: context.general.experience + expGain,
|
||||
dedication: context.general.dedication + dedGain,
|
||||
meta: metaAfter,
|
||||
}),
|
||||
];
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.gold = nextGold;
|
||||
general.rice = nextRice;
|
||||
general.experience += expGain;
|
||||
general.dedication += dedGain;
|
||||
general.meta = metaAfter;
|
||||
|
||||
if (!found) {
|
||||
effects.push(
|
||||
createLogEffect('인재를 찾을 수 없었습니다.', {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
})
|
||||
);
|
||||
return { effects };
|
||||
context.addLog('인재를 찾을 수 없었습니다.', {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const candidate = resolveCandidate(context, context.rng, this.env);
|
||||
@@ -396,32 +388,25 @@ export class ActionResolver<
|
||||
meta,
|
||||
});
|
||||
|
||||
effects.push(createGeneralAddEffect(newGeneral));
|
||||
const nameObjJosa = JosaUtil.pick(name, '을');
|
||||
const nameSubjJosa = JosaUtil.pick(name, '이');
|
||||
effects.push(
|
||||
createLogEffect(`인재 <Y>${name}</>${nameObjJosa} 발견했습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
})
|
||||
);
|
||||
effects.push(
|
||||
createLogEffect(`인재 <Y>${name}</>${nameSubjJosa} 등장했습니다.`, {
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.MONTH,
|
||||
})
|
||||
);
|
||||
effects.push(
|
||||
createLogEffect(`인재 <Y>${name}</>${nameObjJosa} 발견했습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
})
|
||||
);
|
||||
context.addLog(`인재 <Y>${name}</>${nameObjJosa} 발견했습니다.`, {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
context.addLog(`인재 <Y>${name}</>${nameSubjJosa} 등장했습니다.`, {
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
context.addLog(`인재 <Y>${name}</>${nameObjJosa} 발견했습니다.`, {
|
||||
category: LogCategory.HISTORY,
|
||||
format: LogFormat.YEAR_MONTH,
|
||||
});
|
||||
|
||||
return { effects };
|
||||
return {
|
||||
effects: [createGeneralAddEffect(newGeneral)],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,7 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import { LogCategory, LogFormat } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -47,22 +46,17 @@ export class ActionDefinition<
|
||||
}
|
||||
|
||||
resolve(
|
||||
_context: GeneralActionResolveContext<TriggerState>,
|
||||
context: GeneralActionResolveContext<TriggerState>,
|
||||
args: AppointmentArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
void _context;
|
||||
return {
|
||||
effects: [
|
||||
createLogEffect(
|
||||
`${ACTION_NAME}을 신청했습니다. (국가 ${args.destNationId})`,
|
||||
{
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
),
|
||||
],
|
||||
};
|
||||
context.addLog(
|
||||
`${ACTION_NAME}을 신청했습니다. (국가 ${args.destNationId})`,
|
||||
{
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
);
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,9 @@ import {
|
||||
} from '../../../triggers/general-action.js';
|
||||
import type { GeneralActionDefinition } from '../../definition.js';
|
||||
import type {
|
||||
GeneralActionEffect,
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import {
|
||||
createCityPatchEffect,
|
||||
createGeneralPatchEffect,
|
||||
createLogEffect,
|
||||
} from '../../engine.js';
|
||||
import type { MapDefinition, UnitSetDefinition } from '../../../world/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
@@ -351,16 +345,14 @@ export class ActionResolver<
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
const { general, city } = context;
|
||||
if (!city) {
|
||||
return {
|
||||
effects: [createLogEffect('도시 정보가 없습니다.')],
|
||||
};
|
||||
context.addLog('도시 정보가 없습니다.');
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const crewType = findCrewTypeById(context.unitSet, args.crewType);
|
||||
if (!crewType) {
|
||||
return {
|
||||
effects: [createLogEffect('병종 정보가 없습니다.')],
|
||||
};
|
||||
context.addLog('병종 정보가 없습니다.');
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const availabilityContext: CrewTypeAvailabilityContext = {
|
||||
@@ -376,9 +368,8 @@ export class ActionResolver<
|
||||
availabilityContext.startYear = context.startYear;
|
||||
}
|
||||
if (!isCrewTypeAvailable(context.unitSet, crewType.id, availabilityContext)) {
|
||||
return {
|
||||
effects: [createLogEffect('현재 선택할 수 없는 병종입니다.')],
|
||||
};
|
||||
context.addLog('현재 선택할 수 없는 병종입니다.');
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const plan = this.command.getCost(
|
||||
@@ -435,30 +426,26 @@ export class ActionResolver<
|
||||
const expGain = Math.round(appliedCrew / 100);
|
||||
const dedGain = Math.round(appliedCrew / 100);
|
||||
|
||||
const metaUpdated = addMetaNumber(general.meta, 'leadership_exp', 1);
|
||||
// 직접 수정 (Immer Draft)
|
||||
city.population = nextPopulation;
|
||||
city.meta = {
|
||||
...city.meta as object,
|
||||
trust: nextTrust,
|
||||
};
|
||||
|
||||
const effects: Array<GeneralActionEffect<TriggerState>> = [
|
||||
createCityPatchEffect({
|
||||
population: nextPopulation,
|
||||
meta: {
|
||||
trust: nextTrust,
|
||||
},
|
||||
}),
|
||||
createGeneralPatchEffect({
|
||||
crewTypeId: nextCrewTypeId,
|
||||
crew: nextCrew,
|
||||
train: nextTrain,
|
||||
atmos: nextAtmos,
|
||||
gold: nextGold,
|
||||
rice: nextRice,
|
||||
experience: general.experience + expGain,
|
||||
dedication: general.dedication + dedGain,
|
||||
meta: metaUpdated,
|
||||
}),
|
||||
createLogEffect(logMessage),
|
||||
];
|
||||
general.crewTypeId = nextCrewTypeId;
|
||||
general.crew = nextCrew;
|
||||
general.train = nextTrain;
|
||||
general.atmos = nextAtmos;
|
||||
general.gold = nextGold;
|
||||
general.rice = nextRice;
|
||||
general.experience += expGain;
|
||||
general.dedication += dedGain;
|
||||
general.meta = addMetaNumber(general.meta, 'leadership_exp', 1);
|
||||
|
||||
return { effects };
|
||||
context.addLog(logMessage);
|
||||
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,8 +12,7 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import { LogCategory, LogFormat } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -56,19 +55,14 @@ export class ActionDefinition<
|
||||
}
|
||||
|
||||
resolve(
|
||||
_context: GeneralActionResolveContext<TriggerState>,
|
||||
context: GeneralActionResolveContext<TriggerState>,
|
||||
args: DispatchArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
void _context;
|
||||
return {
|
||||
effects: [
|
||||
createLogEffect(`${ACTION_NAME}을 준비했습니다. (목표 도시 ${args.destCityId})`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
context.addLog(`${ACTION_NAME}을 준비했습니다. (목표 도시 ${args.destCityId})`, {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import type {
|
||||
import {
|
||||
createCityPatchEffect,
|
||||
createGeneralPatchEffect,
|
||||
createLogEffect,
|
||||
} from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
@@ -332,6 +331,7 @@ export class ActionResolver<
|
||||
_args: FireAttackArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
void _args;
|
||||
const general = context.general;
|
||||
const city = context.city;
|
||||
if (!city) {
|
||||
throw new Error('Fire attack requires a city context.');
|
||||
@@ -351,13 +351,13 @@ export class ActionResolver<
|
||||
|
||||
const effects: Array<GeneralActionEffect<TriggerState>> = [];
|
||||
|
||||
const nextGold = Math.max(0, context.general.gold - result.costGold);
|
||||
const nextRice = Math.max(0, context.general.rice - result.costRice);
|
||||
const nextExperience = context.general.experience + result.exp;
|
||||
const nextDedication = context.general.dedication + result.dedication;
|
||||
const nextGold = Math.max(0, general.gold - result.costGold);
|
||||
const nextRice = Math.max(0, general.rice - result.costRice);
|
||||
const nextExperience = general.experience + result.exp;
|
||||
const nextDedication = general.dedication + result.dedication;
|
||||
|
||||
const metaWithStatExp = addMetaNumber(
|
||||
context.general.meta,
|
||||
general.meta,
|
||||
STAT_EXP_KEY,
|
||||
1
|
||||
);
|
||||
@@ -365,26 +365,21 @@ export class ActionResolver<
|
||||
? addMetaNumber(metaWithStatExp, 'firenum', 1)
|
||||
: metaWithStatExp;
|
||||
|
||||
effects.push(
|
||||
createGeneralPatchEffect({
|
||||
gold: nextGold,
|
||||
rice: nextRice,
|
||||
experience: nextExperience,
|
||||
dedication: nextDedication,
|
||||
meta: metaUpdated,
|
||||
})
|
||||
);
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.gold = nextGold;
|
||||
general.rice = nextRice;
|
||||
general.experience = nextExperience;
|
||||
general.dedication = nextDedication;
|
||||
general.meta = metaUpdated;
|
||||
|
||||
if (!result.success) {
|
||||
effects.push(
|
||||
createLogEffect(
|
||||
`<G><b>${context.destCity.name}</b></>에 ${ACTION_NAME} 실패했습니다.`,
|
||||
{
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
)
|
||||
context.addLog(
|
||||
`<G><b>${context.destCity.name}</b></>에 ${ACTION_NAME} 실패했습니다.`,
|
||||
{
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
);
|
||||
return { effects };
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const updatedCityMeta: Record<string, TriggerValue> = {
|
||||
@@ -392,6 +387,7 @@ export class ActionResolver<
|
||||
state: CITY_STATE_BURNING,
|
||||
};
|
||||
|
||||
// 타겟 도시는 Draft가 아니므로 Effect 반환
|
||||
effects.push(
|
||||
createCityPatchEffect(
|
||||
{
|
||||
@@ -403,45 +399,38 @@ export class ActionResolver<
|
||||
)
|
||||
);
|
||||
|
||||
effects.push(
|
||||
createLogEffect(
|
||||
`<G><b>${context.destCity.name}</b></>이 불타고 있습니다.`,
|
||||
{
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
)
|
||||
context.addLog(
|
||||
`<G><b>${context.destCity.name}</b></>이 불타고 있습니다.`,
|
||||
{
|
||||
scope: LogScope.SYSTEM,
|
||||
category: LogCategory.SUMMARY,
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
);
|
||||
effects.push(
|
||||
createLogEffect(
|
||||
`<G><b>${context.destCity.name}</b></>에 ${ACTION_NAME} 성공했습니다.`,
|
||||
{
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
)
|
||||
context.addLog(
|
||||
`<G><b>${context.destCity.name}</b></>에 ${ACTION_NAME} 성공했습니다.`,
|
||||
{
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
);
|
||||
effects.push(
|
||||
createLogEffect(
|
||||
`도시의 농업이 <C>${result.agriDamage}</>, 상업이 <C>${result.commDamage}</>만큼 감소하고, 장수 <C>${result.injuryCount}</>명이 부상 당했습니다.`,
|
||||
{
|
||||
format: LogFormat.PLAIN,
|
||||
}
|
||||
)
|
||||
context.addLog(
|
||||
`도시의 농업이 <C>${result.agriDamage}</>, 상업이 <C>${result.commDamage}</>만큼 감소하고, 장수 <C>${result.injuryCount}</>명이 부상 당했습니다.`,
|
||||
{
|
||||
format: LogFormat.PLAIN,
|
||||
}
|
||||
);
|
||||
|
||||
for (const injured of result.injuredGenerals) {
|
||||
// 타겟 장수는 Draft가 아니므로 Effect 반환
|
||||
effects.push(
|
||||
createGeneralPatchEffect(injured.patch, injured.id)
|
||||
);
|
||||
effects.push(
|
||||
createLogEffect(
|
||||
`<M>${ACTION_KEY}</>로 인해 <R>부상</>을 당했습니다.`,
|
||||
{
|
||||
generalId: injured.id,
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
)
|
||||
context.addLog(
|
||||
`<M>${ACTION_KEY}</>로 인해 <R>부상</>을 당했습니다.`,
|
||||
{
|
||||
generalId: injured.id,
|
||||
format: LogFormat.MONTH,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type {
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
} from '../../../domain/entities.js';
|
||||
import type {
|
||||
@@ -13,8 +12,6 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createGeneralPatchEffect, createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -74,21 +71,14 @@ export class ActionDefinition<
|
||||
const nextTrain = clamp(general.train + delta, 0, maxTrain);
|
||||
const applied = nextTrain - general.train;
|
||||
const costGold = this.env.costGold ?? 0;
|
||||
const patch: Partial<General<TriggerState>> = {
|
||||
train: nextTrain,
|
||||
gold: Math.max(0, general.gold - costGold),
|
||||
};
|
||||
|
||||
return {
|
||||
effects: [
|
||||
createGeneralPatchEffect<TriggerState>(patch, general.id),
|
||||
createLogEffect(`${ACTION_NAME}을 통해 훈련도가 ${applied} 증가했습니다.`, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
// 직접 수정 (Immer Draft)
|
||||
general.train = nextTrain;
|
||||
general.gold = Math.max(0, general.gold - costGold);
|
||||
|
||||
context.addLog(`${ACTION_NAME}을 통해 훈련도가 ${applied} 증가했습니다.`);
|
||||
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type {
|
||||
City,
|
||||
General,
|
||||
GeneralTriggerState,
|
||||
} from '../../../domain/entities.js';
|
||||
import type {
|
||||
@@ -21,12 +20,6 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import {
|
||||
createCityPatchEffect,
|
||||
createGeneralPatchEffect,
|
||||
createLogEffect,
|
||||
} from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
|
||||
export interface CityDevelopmentArgs {}
|
||||
|
||||
@@ -98,53 +91,28 @@ export class CityDevelopmentActionDefinition<
|
||||
const general = context.general;
|
||||
const city = context.city;
|
||||
if (!city) {
|
||||
return {
|
||||
effects: [
|
||||
createLogEffect('도시 정보를 찾지 못했습니다.', {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
context.addLog('도시 정보를 찾지 못했습니다.');
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const baseAmount = this.env.amount ?? this.config.baseAmount;
|
||||
const current = readNumber(city[this.config.statKey]);
|
||||
const max = readNumber(city[this.config.maxKey]);
|
||||
if (current === null || max === null) {
|
||||
return {
|
||||
effects: [
|
||||
createLogEffect('도시 정보를 찾지 못했습니다.', {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
context.addLog('도시 정보를 찾지 못했습니다.');
|
||||
return { effects: [] };
|
||||
}
|
||||
|
||||
const nextValue = clamp(current + baseAmount, 0, max);
|
||||
const costGold = this.env.develCost ?? 0;
|
||||
const generalPatch: Partial<General<TriggerState>> = {
|
||||
gold: Math.max(0, general.gold - costGold),
|
||||
};
|
||||
|
||||
// 직접 수정 (Immer Draft)
|
||||
(city as any)[this.config.statKey] = nextValue;
|
||||
general.gold = Math.max(0, general.gold - costGold);
|
||||
|
||||
const logMessage = `${this.config.label}이 ${nextValue - current} 증가했습니다.`;
|
||||
context.addLog(logMessage);
|
||||
|
||||
return {
|
||||
effects: [
|
||||
createCityPatchEffect(
|
||||
{ [this.config.statKey]: nextValue } as Partial<City>,
|
||||
city.id
|
||||
),
|
||||
createGeneralPatchEffect<TriggerState>(generalPatch, general.id),
|
||||
createLogEffect(logMessage, {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,7 @@ import type {
|
||||
GeneralActionOutcome,
|
||||
GeneralActionResolveContext,
|
||||
} from '../../engine.js';
|
||||
import { createLogEffect } from '../../engine.js';
|
||||
import { LogCategory, LogFormat, LogScope } from '../../../logging/types.js';
|
||||
import { LogCategory, LogFormat } from '../../../logging/types.js';
|
||||
import type { TurnCommandEnv } from '../commandEnv.js';
|
||||
import type { GeneralTurnCommandSpec } from './index.js';
|
||||
|
||||
@@ -23,20 +22,14 @@ export class ActionResolver<
|
||||
TriggerState extends GeneralTriggerState = GeneralTriggerState
|
||||
> {
|
||||
resolve(
|
||||
_context: GeneralActionResolveContext<TriggerState>,
|
||||
context: GeneralActionResolveContext<TriggerState>,
|
||||
_args: RestArgs
|
||||
): GeneralActionOutcome<TriggerState> {
|
||||
void _context;
|
||||
void _args;
|
||||
return {
|
||||
effects: [
|
||||
createLogEffect('아무것도 실행하지 않았습니다.', {
|
||||
scope: LogScope.GENERAL,
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
}),
|
||||
],
|
||||
};
|
||||
context.addLog('아무것도 실행하지 않았습니다.', {
|
||||
category: LogCategory.ACTION,
|
||||
format: LogFormat.MONTH,
|
||||
});
|
||||
return { effects: [] };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user