feat: Add new general actions and constraints for game mechanics

- Introduced new general turn commands: 'che_거병', 'che_임관', 'che_건국', 'che_훈련', 'che_사기진작', 'che_요양', 'che_출병', 'che_주민선정', 'che_농지개간', 'che_기술연구', 'che_치안강화', 'che_수비강화', 'che_성벽보수'.
- Implemented action definitions for each new command, including constraints and resolution logic.
- Added new constraints: 'beNeutral', 'remainCityCapacityByMax', 'existsDestNation'.
- Enhanced city development actions for '농지개간', '성벽보수', and '수비강화'.
- Created a new action for declaring war: 'che_선전포고' with appropriate constraints.
- Updated existing action definitions to accommodate new game mechanics and ensure proper functionality.
This commit is contained in:
2026-01-01 11:49:15 +00:00
parent 8c9f61700e
commit cce57d0fea
20 changed files with 1344 additions and 2 deletions
+77
View File
@@ -174,6 +174,25 @@ export const notBeNeutral = (): Constraint => ({
},
});
export const beNeutral = (): Constraint => ({
name: 'BeNeutral',
requires: (ctx) => [{ kind: 'general', id: ctx.actorId }],
test: (ctx, view) => {
const req: RequirementKey = { kind: 'general', id: ctx.actorId };
if (!view.has(req)) {
return unknownOrDeny(ctx, [req], '장수 정보가 없습니다.');
}
const general = view.get(req) as General | null;
if (!general) {
return unknownOrDeny(ctx, [req], '장수 정보가 없습니다.');
}
if (general.nationId === 0) {
return allow();
}
return { kind: 'deny', reason: '이미 국가에 속해 있습니다.' };
},
});
export const alwaysFail = (reason: string): Constraint => ({
name: 'AlwaysFail',
requires: () => [],
@@ -561,6 +580,36 @@ export const remainCityCapacity = (
},
});
export const remainCityCapacityByMax = (
key: string,
maxKey: string,
label: string
): Constraint => ({
name: 'RemainCityCapacityByMax',
requires: (ctx) =>
ctx.cityId !== undefined ? [{ kind: 'city', id: ctx.cityId }] : [],
test: (ctx, view) => {
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 record = city as unknown as Record<string, number | undefined>;
const current = record[key];
const max = record[maxKey];
if (current === undefined || max === undefined) {
return unknownOrDeny(ctx, [], '도시 정보가 없습니다.');
}
if (current < max) {
return allow();
}
return { kind: 'deny', reason: `${label}이 충분합니다.` };
},
});
export const reqCityCapacity = (
key: string,
label: string,
@@ -689,6 +738,34 @@ export const existsDestCity = (): Constraint => ({
},
});
export const existsDestNation = (): Constraint => ({
name: 'ExistsDestNation',
requires: (ctx) =>
resolveDestNationId(ctx) !== undefined
? [
{
kind: 'destNation',
id: resolveDestNationId(ctx) ?? 0,
},
]
: [],
test: (ctx, view) => {
const destNationId = resolveDestNationId(ctx);
if (destNationId === undefined) {
return unknownOrDeny(ctx, [], '국가 정보가 없습니다.');
}
const req: RequirementKey = { kind: 'destNation', id: destNationId };
if (!view.has(req)) {
return unknownOrDeny(ctx, [req], '국가 정보가 없습니다.');
}
const nation = view.get(req) as Nation | null;
if (!nation) {
return unknownOrDeny(ctx, [req], '국가 정보가 없습니다.');
}
return allow();
},
});
export const existsDestGeneral = (): Constraint => ({
name: 'ExistsDestGeneral',
requires: (ctx) =>