feat: Zod를 사용한 월드 상태 구성 및 메타 타입 추가, 사용자 ID 처리 개선

This commit is contained in:
2026-01-05 14:49:19 +00:00
parent 4391110ab0
commit 1861cbc9c6
17 changed files with 118 additions and 153 deletions
@@ -15,6 +15,6 @@ export interface TurnCommandModule<TSpec extends TurnCommandSpecBase = TurnComma
commandSpec: TSpec;
ActionDefinition: new (...args: any[]) => GeneralActionDefinition;
ActionResolver?: new (...args: any[]) => GeneralActionResolver;
CommandResolver?: new (...args: any[]) => unknown;
CommandResolver?: new (...args: any[]) => any;
actionContextBuilder?: ActionContextBuilder;
}
@@ -29,11 +29,13 @@ export interface CityDevelopmentEnvironment {
amount?: number;
}
type NumberKeys<T> = { [K in keyof T]: T[K] extends number ? K : never }[keyof T];
export interface CityDevelopmentConfig {
key: string;
name: string;
statKey: keyof City;
maxKey: keyof City;
statKey: NumberKeys<City>;
maxKey: NumberKeys<City>;
label: string;
baseAmount: number;
}
@@ -74,8 +76,8 @@ export class CityDevelopmentActionDefinition<
occupiedCity(),
suppliedCity(),
remainCityCapacityByMax(
String(this.config.statKey),
String(this.config.maxKey),
this.config.statKey,
this.config.maxKey,
this.config.label
),
reqGeneralGold(getRequiredGold),
@@ -105,7 +107,7 @@ export class CityDevelopmentActionDefinition<
const costGold = this.env.develCost ?? 0;
// 직접 수정 (Immer Draft)
(city as any)[this.config.statKey] = nextValue;
city[this.config.statKey] = nextValue;
general.gold = Math.max(0, general.gold - costGold);
const logMessage = `${this.config.label}${nextValue - current} 증가했습니다.`;
+17 -20
View File
@@ -143,7 +143,7 @@ export const suppliedDestCity = (): Constraint => ({
});
export const remainCityCapacity = (
key: string,
key: keyof City,
label: string
): Constraint => ({
name: 'RemainCityCapacity',
@@ -158,11 +158,10 @@ export const remainCityCapacity = (
const req: RequirementKey = { kind: 'city', id: ctx.cityId };
return unknownOrDeny(ctx, [req], '도시 정보가 없습니다.');
}
const record = city as unknown as Record<string, number | undefined>;
const maxKey = `${key}_max`;
const current = record[key];
const max = record[maxKey];
if (current === undefined || max === undefined) {
const maxKey = `${String(key)}Max` as keyof City;
const current = city[key];
const max = city[maxKey];
if (typeof current !== 'number' || typeof max !== 'number') {
return unknownOrDeny(ctx, [], '도시 정보가 없습니다.');
}
if (current < max) {
@@ -173,8 +172,8 @@ export const remainCityCapacity = (
});
export const remainCityCapacityByMax = (
key: string,
maxKey: string,
key: keyof City,
maxKey: keyof City,
label: string
): Constraint => ({
name: 'RemainCityCapacityByMax',
@@ -189,10 +188,9 @@ export const remainCityCapacityByMax = (
const req: RequirementKey = { kind: 'city', id: ctx.cityId };
return unknownOrDeny(ctx, [req], '도시 정보가 없습니다.');
}
const record = city as unknown as Record<string, number | undefined>;
const current = record[key];
const max = record[maxKey];
if (current === undefined || max === undefined) {
const current = city[key];
const max = city[maxKey];
if (typeof current !== 'number' || typeof max !== 'number') {
return unknownOrDeny(ctx, [], '도시 정보가 없습니다.');
}
if (current < max) {
@@ -203,7 +201,7 @@ export const remainCityCapacityByMax = (
});
export const reqCityCapacity = (
key: string,
key: keyof City,
label: string,
required: number | string
): Constraint => ({
@@ -219,16 +217,15 @@ export const reqCityCapacity = (
const req: RequirementKey = { kind: 'city', id: ctx.cityId };
return unknownOrDeny(ctx, [req], '도시 정보가 없습니다.');
}
const record = city as unknown as Record<string, number | undefined>;
const current = record[key];
if (current === undefined) {
const current = city[key];
if (typeof current !== 'number') {
return unknownOrDeny(ctx, [], '도시 정보가 없습니다.');
}
if (typeof required === 'string') {
const ratio = parsePercent(required);
const maxKey = `${key}Max`;
const max = record[maxKey];
if (ratio === null || max === undefined) {
const maxKey = `${String(key)}Max` as keyof City;
const max = city[maxKey];
if (ratio === null || typeof max !== 'number') {
return unknownOrDeny(ctx, [], '도시 정보가 없습니다.');
}
if (current >= max * ratio) {
@@ -256,7 +253,7 @@ export const reqCityTrust = (minTrust: number): Constraint => ({
}
const trust =
readMetaNumberFromUnknown(
city.meta as Record<string, unknown>,
city.meta,
'trust'
) ?? null;
if (trust === null) {