Add class and terrain battle rules

This commit is contained in:
2026-06-22 02:12:57 +09:00
parent 6db887e2da
commit f99884ea29
4 changed files with 314 additions and 83 deletions

View File

@@ -0,0 +1,195 @@
export type TerrainType = 'plain' | 'road' | 'forest' | 'hill' | 'village' | 'fort' | 'camp';
export type UnitClassKey = 'lord' | 'infantry' | 'cavalry' | 'spearman' | 'archer' | 'bandit' | 'yellowTurban' | 'rebelLeader';
export type TerrainRule = {
label: string;
color: number;
alpha: number;
moveCost: number;
defenseBonus: number;
};
export type UnitClassDefinition = {
key: UnitClassKey;
name: string;
family: string;
role: string;
description: string;
terrainRatings: Record<TerrainType, number>;
movementCosts?: Partial<Record<TerrainType, number>>;
};
export const terrainRules: Record<TerrainType, TerrainRule> = {
plain: { label: '평지', color: 0x6d8056, alpha: 0.04, moveCost: 1, defenseBonus: 0 },
road: { label: '길', color: 0xb08b55, alpha: 0.1, moveCost: 1, defenseBonus: 0 },
forest: { label: '숲', color: 0x355f42, alpha: 0.17, moveCost: 2, defenseBonus: 10 },
hill: { label: '언덕', color: 0x82724e, alpha: 0.15, moveCost: 2, defenseBonus: 8 },
village: { label: '마을', color: 0x8d6f4b, alpha: 0.2, moveCost: 1, defenseBonus: 12 },
fort: { label: '요새', color: 0x735c46, alpha: 0.22, moveCost: 1, defenseBonus: 18 },
camp: { label: '진영', color: 0x4f6a73, alpha: 0.17, moveCost: 1, defenseBonus: 15 }
};
export const unitClasses: Record<UnitClassKey, UnitClassDefinition> = {
lord: {
key: 'lord',
name: '군웅',
family: '군주계',
role: '지휘 / 균형',
description: '지형을 크게 타지 않고 아군 전선을 안정시키는 지휘관 병과.',
terrainRatings: {
plain: 100,
road: 105,
forest: 95,
hill: 95,
village: 110,
fort: 115,
camp: 115
}
},
infantry: {
key: 'infantry',
name: '의용 보병',
family: '보병계',
role: '전열 / 방어',
description: '숲과 진영에서 버티는 힘이 좋고 기병의 돌격을 받아내기 좋다.',
terrainRatings: {
plain: 100,
road: 100,
forest: 110,
hill: 105,
village: 115,
fort: 120,
camp: 115
},
movementCosts: {
forest: 1,
village: 1,
fort: 1,
camp: 1
}
},
cavalry: {
key: 'cavalry',
name: '경기병',
family: '기병계',
role: '기동 / 돌파',
description: '길과 평지에서 빠르게 압박하지만 숲과 언덕에서는 힘이 떨어진다.',
terrainRatings: {
plain: 110,
road: 120,
forest: 75,
hill: 80,
village: 95,
fort: 90,
camp: 100
},
movementCosts: {
road: 1,
forest: 3,
hill: 3
}
},
spearman: {
key: 'spearman',
name: '창병',
family: '창병계',
role: '대기병 / 압박',
description: '기병 견제와 전선 고정에 특화된 근접 병과.',
terrainRatings: {
plain: 105,
road: 100,
forest: 95,
hill: 105,
village: 105,
fort: 110,
camp: 110
}
},
archer: {
key: 'archer',
name: '궁병',
family: '궁병계',
role: '원거리 / 견제',
description: '언덕과 요새에서 유리하고 근접 압박에는 약한 원거리 병과.',
terrainRatings: {
plain: 95,
road: 95,
forest: 90,
hill: 115,
village: 100,
fort: 120,
camp: 105
},
movementCosts: {
hill: 1
}
},
bandit: {
key: 'bandit',
name: '도적',
family: '적병계',
role: '습격 / 산악',
description: '숲과 언덕에서 강하고 정규전 방어 지형에서는 약하다.',
terrainRatings: {
plain: 95,
road: 95,
forest: 115,
hill: 115,
village: 100,
fort: 90,
camp: 95
},
movementCosts: {
forest: 1,
hill: 1
}
},
yellowTurban: {
key: 'yellowTurban',
name: '황건병',
family: '황건적계',
role: '민병 / 물량',
description: '능력은 낮지만 험지에서 성가시게 버티는 반란군 병과.',
terrainRatings: {
plain: 95,
road: 95,
forest: 105,
hill: 105,
village: 100,
fort: 90,
camp: 95
},
movementCosts: {
forest: 1
}
},
rebelLeader: {
key: 'rebelLeader',
name: '두령',
family: '두령계',
role: '지휘 / 근접',
description: '황건적을 이끄는 지휘관형 적병. 진영과 험지에서 버티는 힘이 있다.',
terrainRatings: {
plain: 100,
road: 100,
forest: 110,
hill: 110,
village: 105,
fort: 105,
camp: 110
},
movementCosts: {
forest: 1,
hill: 1
}
}
};
export function getUnitClass(classKey: UnitClassKey) {
return unitClasses[classKey];
}
export function getTerrainRule(terrain: TerrainType) {
return terrainRules[terrain];
}

View File

@@ -1,3 +1,5 @@
import type { TerrainType, UnitClassKey } from './battleRules';
export type PortraitKey = 'liuBei' | 'guanYu' | 'zhangFei';
export type StoryPage = {
@@ -15,6 +17,7 @@ export type UnitData = {
name: string;
faction: 'ally' | 'enemy';
className: string;
classKey: UnitClassKey;
hp: number;
maxHp: number;
attack: number;
@@ -41,8 +44,6 @@ export type BattleBond = {
description: string;
};
export type TerrainType = 'plain' | 'forest' | 'hill' | 'fort';
export type BattleMap = {
width: number;
height: number;
@@ -140,18 +141,18 @@ export const firstBattleMap: BattleMap = {
width: 12,
height: 12,
terrain: [
['plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
['plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain'],
['plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'plain', 'plain', 'fort', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest'],
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain'],
['forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
['forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain'],
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain']
['plain', 'plain', 'forest', 'forest', 'plain', 'road', 'road', 'hill', 'hill', 'plain', 'plain', 'plain'],
['plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'hill', 'plain', 'plain', 'plain'],
['plain', 'forest', 'plain', 'plain', 'plain', 'road', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'hill', 'hill', 'road', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain'],
['plain', 'plain', 'plain', 'hill', 'plain', 'road', 'road', 'forest', 'plain', 'plain', 'fort', 'plain'],
['plain', 'plain', 'plain', 'plain', 'plain', 'village', 'road', 'plain', 'plain', 'plain', 'forest', 'forest'],
['plain', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'plain'],
['forest', 'forest', 'plain', 'plain', 'plain', 'road', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
['forest', 'plain', 'plain', 'plain', 'plain', 'road', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain'],
['plain', 'camp', 'plain', 'forest', 'forest', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'plain'],
['plain', 'camp', 'camp', 'plain', 'forest', 'plain', 'road', 'plain', 'plain', 'forest', 'forest', 'plain'],
['plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain']
]
};
@@ -160,7 +161,8 @@ export const firstBattleUnits: UnitData[] = [
id: 'liu-bei',
name: '유비',
faction: 'ally',
className: '의병장',
className: '군웅',
classKey: 'lord',
hp: 32,
maxHp: 32,
attack: 9,
@@ -173,7 +175,8 @@ export const firstBattleUnits: UnitData[] = [
id: 'guan-yu',
name: '관우',
faction: 'ally',
className: '보병',
className: '의용 보병',
classKey: 'infantry',
hp: 30,
maxHp: 30,
attack: 10,
@@ -187,6 +190,7 @@ export const firstBattleUnits: UnitData[] = [
name: '장비',
faction: 'ally',
className: '창병',
classKey: 'spearman',
hp: 30,
maxHp: 30,
attack: 10,
@@ -199,7 +203,8 @@ export const firstBattleUnits: UnitData[] = [
id: 'rebel-a',
name: '황건병',
faction: 'enemy',
className: '도적',
className: '황건병',
classKey: 'yellowTurban',
hp: 20,
maxHp: 20,
attack: 6,
@@ -212,7 +217,8 @@ export const firstBattleUnits: UnitData[] = [
id: 'rebel-b',
name: '황건병',
faction: 'enemy',
className: '도적',
className: '황건병',
classKey: 'yellowTurban',
hp: 20,
maxHp: 20,
attack: 6,
@@ -226,6 +232,7 @@ export const firstBattleUnits: UnitData[] = [
name: '황건궁병',
faction: 'enemy',
className: '궁병',
classKey: 'archer',
hp: 18,
maxHp: 18,
attack: 7,
@@ -239,6 +246,7 @@ export const firstBattleUnits: UnitData[] = [
name: '두령 한석',
faction: 'enemy',
className: '두령',
classKey: 'rebelLeader',
hp: 30,
maxHp: 30,
attack: 9,