사령턴 추가 구현

This commit is contained in:
2026-01-10 14:49:39 +00:00
parent f334cc297a
commit baa913a09c
13 changed files with 1755 additions and 27 deletions
+66 -1
View File
@@ -1,4 +1,4 @@
import type { General, Nation } from '@sammo-ts/logic/domain/entities.js';
import type { City, General, Nation } from '@sammo-ts/logic/domain/entities.js';
import { allow, readGeneral, readMetaNumber, readNation, resolveDestNationId, unknownOrDeny } from './helpers.js';
import type { Constraint, ConstraintContext, RequirementKey, StateView } from './types.js';
@@ -272,3 +272,68 @@ export const checkNationNameDuplicate = (name: string): Constraint => ({
return allow();
},
});
export const nearNation = (): Constraint => ({
name: 'nearNation',
requires: (ctx) => {
const reqs: RequirementKey[] = [
{ kind: 'env', key: 'cities' },
{ kind: 'env', key: 'map' },
];
if (ctx.nationId !== undefined) {
reqs.push({ kind: 'nation', id: ctx.nationId });
} else {
reqs.push({ kind: 'general', id: ctx.actorId });
}
const destNationId = resolveDestNationId(ctx);
if (destNationId !== undefined) {
reqs.push({ kind: 'destNation', id: destNationId });
}
return reqs;
},
test: (ctx, view) => {
const cities = view.get({ kind: 'env', key: 'cities' }) as City[] | null;
const map = view.get({ kind: 'env', key: 'map' }) as {
cities: Array<{ id: number; connections: number[] }>;
} | null;
if (!cities || !map) {
return unknownOrDeny(ctx, [], '지도 정보가 없습니다.');
}
const destNationId = resolveDestNationId(ctx);
if (destNationId === undefined) {
return unknownOrDeny(ctx, [], '상대 국가 정보가 없습니다.');
}
let baseNationId = ctx.nationId;
if (baseNationId === undefined) {
const general = readGeneral(ctx, view);
if (!general) {
return unknownOrDeny(ctx, [], '아국 정보가 없습니다.');
}
baseNationId = general.nationId;
}
const baseNationCityIds = new Set(cities.filter((c) => c.nationId === baseNationId).map((c) => c.id));
const destNationCityIds = new Set(cities.filter((c) => c.nationId === destNationId).map((c) => c.id));
if (baseNationCityIds.size === 0) {
return { kind: 'deny', reason: '아국 도시가 없습니다.' };
}
if (destNationCityIds.size === 0) {
return { kind: 'deny', reason: '상대 국가 도시가 없습니다.' };
}
for (const cityId of baseNationCityIds) {
const cityDef = map.cities.find((c) => c.id === cityId);
if (!cityDef) continue;
for (const neighborId of cityDef.connections) {
if (destNationCityIds.has(neighborId)) {
return allow();
}
}
}
return { kind: 'deny', reason: '인접 국가가 아닙니다.' };
},
});