feat: add atmos property to General entity and update related logic

- Added `atmos` property to the `General` interface in `entities.ts`.
- Initialized `atmos` to 0 in the `buildGeneralSeeds` function in `bootstrap.ts`.
- Updated exports in `index.ts` to include `unitSet.js`.
- Introduced new types for crew types and their requirements in `types.ts`.
- Implemented `unitSetLoader.ts` for loading unit set definitions from JSON files.
- Created `che_징병.ts` action for recruiting crew types with cost and training calculations.
- Developed `unitSet.ts` for handling crew type availability and requirements.
- Added tests for crew type availability and unit set parsing in `crewType.test.ts`.
This commit is contained in:
2025-12-30 09:45:51 +00:00
parent e3bbbd3718
commit d599d94a7b
18 changed files with 1665 additions and 31 deletions
+242
View File
@@ -0,0 +1,242 @@
import { describe, expect, it } from 'vitest';
import type { City, General, Nation } from '../src/domain/entities.js';
import type { MapDefinition } from '../src/world/types.js';
import {
isCrewTypeAvailable,
parseUnitSetDefinition,
} from '../src/world/unitSet.js';
const buildMap = (): MapDefinition => ({
id: 'test',
name: 'test',
cities: [
{
id: 1,
name: 'A',
level: 6,
region: 1,
position: { x: 0, y: 0 },
connections: [],
max: {
population: 100000,
agriculture: 1000,
commerce: 1000,
security: 1000,
defence: 1000,
wall: 1000,
},
initial: {
population: 50000,
agriculture: 500,
commerce: 500,
security: 500,
defence: 500,
wall: 500,
},
},
],
meta: {
regionMap: {
A: 1,
},
},
});
const buildNation = (): Nation => ({
id: 1,
name: 'TestNation',
color: '#000000',
capitalCityId: 1,
chiefGeneralId: null,
gold: 1000,
rice: 1000,
power: 0,
level: 1,
typeCode: 'test',
meta: {
tech: 3000,
},
});
const buildGeneral = (): General => ({
id: 1,
name: 'TestGeneral',
nationId: 1,
cityId: 1,
troopId: 0,
stats: { leadership: 50, strength: 50, intelligence: 50 },
experience: 0,
dedication: 0,
officerLevel: 3,
role: {
personality: null,
specialDomestic: null,
specialWar: null,
items: {
horse: null,
weapon: null,
book: null,
item: null,
},
},
injury: 0,
gold: 1000,
rice: 1000,
crew: 0,
crewTypeId: 1100,
train: 0,
atmos: 0,
age: 20,
npcState: 0,
triggerState: {
flags: {},
counters: {},
modifiers: {},
meta: {},
},
meta: {},
});
const buildCities = (): City[] => [
{
id: 1,
name: 'A',
nationId: 1,
level: 6,
population: 50000,
populationMax: 100000,
agriculture: 500,
agricultureMax: 1000,
commerce: 500,
commerceMax: 1000,
security: 500,
securityMax: 1000,
supplyState: 1,
frontState: 0,
defence: 500,
defenceMax: 1000,
wall: 500,
wallMax: 1000,
meta: {},
},
];
describe('crew type availability', () => {
it('parses unit set data and normalizes coefficients', () => {
const parsed = parseUnitSetDefinition({
id: 'test',
name: 'test',
defaultCrewTypeId: 1100,
crewTypes: [
{
id: 1100,
armType: 1,
name: '보병',
attack: 100,
defence: 100,
speed: 7,
avoid: 10,
magicCoef: 0,
cost: 9,
rice: 9,
requirements: [],
attackCoef: [],
defenceCoef: { 1: 1.2 },
info: ['테스트'],
initSkillTrigger: null,
phaseSkillTrigger: null,
iActionList: null,
},
],
});
expect(parsed.defaultCrewTypeId).toBe(1100);
expect(parsed.crewTypes?.[0]?.attackCoef).toEqual({});
});
it('checks tech/region/city requirements', () => {
const unitSet = parseUnitSetDefinition({
id: 'test',
name: 'test',
crewTypes: [
{
id: 1100,
armType: 1,
name: '기본',
attack: 100,
defence: 100,
speed: 7,
avoid: 10,
magicCoef: 0,
cost: 9,
rice: 9,
requirements: [],
attackCoef: {},
defenceCoef: {},
info: [],
initSkillTrigger: null,
phaseSkillTrigger: null,
iActionList: null,
},
{
id: 1200,
armType: 1,
name: '기술병',
attack: 100,
defence: 100,
speed: 7,
avoid: 10,
magicCoef: 0,
cost: 9,
rice: 9,
requirements: [
{ type: 'ReqTech', tech: 2000 },
{ type: 'ReqRegions', regions: ['A'] },
],
attackCoef: {},
defenceCoef: {},
info: [],
initSkillTrigger: null,
phaseSkillTrigger: null,
iActionList: null,
},
{
id: 1300,
armType: 1,
name: '도시병',
attack: 100,
defence: 100,
speed: 7,
avoid: 10,
magicCoef: 0,
cost: 9,
rice: 9,
requirements: [
{ type: 'ReqCitiesWithCityLevel', level: 7, cities: ['A'] },
],
attackCoef: {},
defenceCoef: {},
info: [],
initSkillTrigger: null,
phaseSkillTrigger: null,
iActionList: null,
},
],
});
const context = {
general: buildGeneral(),
nation: buildNation(),
map: buildMap(),
cities: buildCities(),
currentYear: 10,
startYear: 1,
};
expect(isCrewTypeAvailable(unitSet, 1100, context)).toBe(true);
expect(isCrewTypeAvailable(unitSet, 1200, context)).toBe(true);
expect(isCrewTypeAvailable(unitSet, 1300, context)).toBe(false);
});
});