Files
heros_web/src/game/data/scenario.ts

890 lines
30 KiB
TypeScript

import type { TerrainType, UnitClassKey } from './battleRules';
import type { EquipmentSet } from './battleItems';
export type PortraitKey = 'liuBei' | 'guanYu' | 'zhangFei';
export type StoryPage = {
id: string;
chapter: string;
background: string;
speaker?: string;
portrait?: PortraitKey;
bgm?: string;
text: string;
};
export type UnitData = {
id: string;
name: string;
faction: 'ally' | 'enemy';
className: string;
classKey: UnitClassKey;
level: number;
exp: number;
hp: number;
maxHp: number;
attack: number;
move: number;
stats: UnitStats;
equipment: EquipmentSet;
x: number;
y: number;
};
export type UnitStats = {
might: number;
intelligence: number;
leadership: number;
agility: number;
luck: number;
};
export type BattleBond = {
id: string;
unitIds: [string, string];
title: string;
level: number;
exp: number;
description: string;
};
export type BattleMap = {
width: number;
height: number;
terrain: TerrainType[][];
};
export const prologuePages: StoryPage[] = [
{
id: 'yellow-turban-chaos',
bgm: 'story-dark',
chapter: '난세의 시작',
background: 'story-chaos',
text: '중평 원년, 황건의 깃발이 각지에서 일어났다. 관군은 흩어지고, 마을마다 피난 행렬이 이어졌다.'
},
{
id: 'liu-bei-resolve',
bgm: 'story-dark',
chapter: '탁현의 방',
background: 'story-liu-bei',
speaker: '유비',
portrait: 'liuBei',
text: '한실의 백성이 이토록 고통받는데, 어찌 초가집 그늘에 숨어 있을 수 있겠는가.'
},
{
id: 'zhang-fei-joins',
bgm: 'story-dark',
chapter: '뜻이 모이다',
background: 'story-three-heroes',
speaker: '장비',
portrait: 'zhangFei',
text: '좋소! 나라를 바로잡고 백성을 구한다면, 내 재산도 이 목숨도 아깝지 않소.'
},
{
id: 'guan-yu-joins',
bgm: 'story-dark',
chapter: '뜻이 모이다',
background: 'story-three-heroes',
speaker: '관우',
portrait: 'guanYu',
text: '의로운 뜻을 품은 이와 길을 함께한다면, 천 리라도 멀다 하지 않겠습니다.'
},
{
id: 'oath-narration',
bgm: 'oath-theme',
chapter: '도원결의',
background: 'title-taoyuan',
text: '복숭아꽃이 흩날리는 동산에서 세 사람은 하늘과 땅에 고했다.'
},
{
id: 'oath-liu-bei',
bgm: 'oath-theme',
chapter: '도원결의',
background: 'title-taoyuan',
speaker: '유비',
portrait: 'liuBei',
text: '우리는 태어난 날은 달라도, 오늘부터 뜻을 함께한다. 백성을 구하고 한실을 돕는 길에서 물러서지 않겠다.'
},
{
id: 'oath-brothers',
bgm: 'oath-theme',
chapter: '도원결의',
background: 'title-taoyuan',
speaker: '관우',
portrait: 'guanYu',
text: '의를 저버리지 않고, 형제를 저버리지 않겠습니다.'
},
{
id: 'militia-rises',
bgm: 'militia-theme',
chapter: '의용군 모집',
background: 'story-militia',
speaker: '장비',
portrait: 'zhangFei',
text: '창을 들 사람은 앞으로 나오시오! 오늘 모인 이름 없는 무리가 내일의 방패가 될 것이오!'
},
{
id: 'first-sortie',
bgm: 'battle-prep',
chapter: '첫 출진',
background: 'story-sortie',
speaker: '유비',
portrait: 'liuBei',
text: '우리는 아직 작다. 그러나 오늘 한 마을을 지켜낸다면, 그 뜻은 천하로 번져갈 것이다.'
},
{
id: 'battle-briefing',
bgm: 'battle-prep',
chapter: '황건적 토벌',
background: 'story-sortie',
text: '의용군은 마을 어귀로 향했다. 첫 싸움은 곧 시작된다.'
}
];
export const firstBattleVictoryPages: StoryPage[] = [
{
id: 'first-victory-village',
bgm: 'militia-theme',
chapter: '첫 승리',
background: 'story-militia',
text: '황건적의 깃발이 쓰러지자, 숨어 있던 백성들이 하나둘 마을 어귀로 돌아왔다.'
},
{
id: 'first-victory-liu-bei',
bgm: 'militia-theme',
chapter: '첫 승리',
background: 'story-liu-bei',
speaker: '유비',
portrait: 'liuBei',
text: '오늘의 승리는 작다. 그러나 백성 한 사람을 지키는 일에서 천하를 바로잡는 뜻이 시작된다.'
},
{
id: 'first-victory-guan-yu',
bgm: 'militia-theme',
chapter: '의용군의 이름',
background: 'story-three-heroes',
speaker: '관우',
portrait: 'guanYu',
text: '형님의 뜻을 보고 사람들이 모일 것입니다. 다음 싸움은 오늘보다 더 큰 책임을 요구하겠지요.'
},
{
id: 'first-victory-zhang-fei',
bgm: 'militia-theme',
chapter: '의용군의 이름',
background: 'story-militia',
speaker: '장비',
portrait: 'zhangFei',
text: '하하! 그럼 더 크게 외쳐야겠소. 창을 들 자는 모여라! 우리 의용군은 물러서지 않는다!'
},
{
id: 'first-victory-next',
bgm: 'battle-prep',
chapter: '다음 길',
background: 'story-sortie',
text: '세 형제의 이름은 탁현 일대에 퍼지기 시작했다. 이제 더 넓은 전장과 더 많은 인연이 그들을 기다린다.'
}
];
export const secondBattleIntroPages: StoryPage[] = [
{
id: 'second-pursuit-rumor',
bgm: 'militia-theme',
chapter: '잔당의 그림자',
background: 'story-militia',
text: '첫 승리의 기쁨이 채 가시기도 전에, 황건 잔당이 북쪽 길목의 마을을 습격하고 있다는 급보가 도착했다.'
},
{
id: 'second-liu-bei-command',
bgm: 'battle-prep',
chapter: '추격 명령',
background: 'story-liu-bei',
speaker: '유비',
portrait: 'liuBei',
text: '백성이 다시 칼끝에 몰렸다면 우리가 쉬어 갈 곳은 없다. 길을 따라 추격하되, 마을을 먼저 지켜야 한다.'
},
{
id: 'second-guan-yu-vow',
bgm: 'battle-prep',
chapter: '좁은 길목',
background: 'story-three-heroes',
speaker: '관우',
portrait: 'guanYu',
text: '적은 숲과 강을 끼고 물러설 것입니다. 제가 앞길을 열겠습니다. 형님께서는 백성을 살피십시오.'
},
{
id: 'second-zhang-fei-charge',
bgm: 'battle-prep',
chapter: '의용군 출진',
background: 'story-sortie',
speaker: '장비',
portrait: 'zhangFei',
text: '좋다! 숨어 있는 놈들도 모조리 끌어내 주마. 이번에는 도망칠 틈도 주지 않겠다!'
}
];
export const secondBattleVictoryPages: StoryPage[] = [
{
id: 'second-victory-village',
bgm: 'militia-theme',
chapter: '마을의 안도',
background: 'story-militia',
text: '잔당의 불길은 꺼지고, 피난하던 백성들이 하나둘 마을로 돌아왔다. 의용군의 이름은 이제 길목의 장터마다 오르내렸다.'
},
{
id: 'second-victory-liu-bei',
bgm: 'militia-theme',
chapter: '다음 책임',
background: 'story-liu-bei',
speaker: '유비',
portrait: 'liuBei',
text: '우리가 이긴 것은 힘만으로 된 일이 아니다. 백성을 지키겠다는 뜻을 잊지 않는다면, 더 큰 전장도 견딜 수 있을 것이다.'
},
{
id: 'second-victory-next',
bgm: 'battle-prep',
chapter: '넓어지는 전장',
background: 'story-sortie',
text: '탁현 인근의 소란은 잦아들었지만, 황건의 난은 아직 천하 곳곳에서 타오르고 있었다. 세 형제의 길은 이제 더 멀리 이어진다.'
}
];
export const firstBattleMap: BattleMap = {
width: 20,
height: 18,
terrain: [
['forest', 'forest', 'hill', 'hill', 'cliff', 'cliff', 'hill', 'plain', 'road', 'road', 'plain', 'forest', 'hill', 'hill', 'plain', 'plain', 'forest', 'hill', 'hill', 'plain'],
['forest', 'hill', 'hill', 'plain', 'cliff', 'hill', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'forest', 'hill', 'plain', 'plain', 'plain', 'hill', 'fort', 'plain'],
['plain', 'forest', 'plain', 'plain', 'hill', 'hill', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'fort', 'fort', 'plain', 'plain'],
['plain', 'plain', 'plain', 'forest', 'hill', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'fort', 'plain', 'plain', 'forest'],
['plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest'],
['plain', 'plain', 'plain', 'forest', 'plain', 'road', 'road', 'plain', 'forest', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'village', 'plain', 'plain', 'plain', 'forest'],
['plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain'],
['hill', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'hill', 'hill', 'plain', 'hill', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain'],
['hill', 'hill', 'plain', 'road', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain'],
['plain', 'plain', 'road', 'road', 'forest', 'plain', 'plain', 'plain', 'village', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain'],
['plain', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'plain', 'road', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain'],
['road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'forest', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'],
['plain', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'village', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain'],
['plain', 'road', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'camp', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill'],
['camp', 'road', 'plain', 'plain', 'hill', 'plain', 'hill', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'hill'],
['camp', 'camp', 'road', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill'],
['plain', 'camp', 'road', 'plain', 'forest', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain'],
['plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain']
]
};
export const secondBattleMap: BattleMap = {
width: 24,
height: 20,
terrain: [
['forest', 'forest', 'forest', 'hill', 'plain', 'plain', 'road', 'plain', 'forest', 'hill', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'forest', 'plain', 'hill', 'hill', 'plain', 'plain', 'forest', 'forest'],
['forest', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'hill', 'river', 'river', 'plain', 'forest', 'forest', 'plain', 'plain', 'hill', 'fort', 'fort', 'plain', 'plain', 'forest'],
['plain', 'plain', 'plain', 'forest', 'plain', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'river', 'river', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'fort', 'plain', 'plain', 'plain', 'forest'],
['plain', 'plain', 'forest', 'forest', 'plain', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'river', 'river', 'road', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain'],
['plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'hill', 'river', 'river', 'road', 'plain', 'plain', 'hill', 'cliff', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain'],
['plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'river', 'river', 'road', 'plain', 'plain', 'hill', 'plain', 'village', 'plain', 'forest', 'forest', 'plain'],
['plain', 'plain', 'forest', 'plain', 'road', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain', 'river', 'river', 'road', 'road', 'plain', 'plain', 'plain', 'village', 'plain', 'plain', 'forest', 'plain'],
['plain', 'forest', 'forest', 'plain', 'road', 'plain', 'plain', 'plain', 'hill', 'plain', 'road', 'road', 'plain', 'river', 'river', 'road', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain'],
['plain', 'forest', 'plain', 'road', 'road', 'plain', 'hill', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain'],
['plain', 'plain', 'plain', 'road', 'plain', 'plain', 'hill', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain'],
['hill', 'plain', 'plain', 'road', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'village', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain'],
['hill', 'hill', 'plain', 'road', 'plain', 'forest', 'plain', 'road', 'plain', 'plain', 'village', 'village', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain'],
['cliff', 'hill', 'plain', 'road', 'road', 'plain', 'plain', 'road', 'plain', 'plain', 'plain', 'village', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain'],
['cliff', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'road', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain'],
['plain', 'plain', 'camp', 'plain', 'plain', 'road', 'road', 'plain', 'road', 'plain', 'forest', 'plain', 'plain', 'forest', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain'],
['plain', 'camp', 'camp', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'forest', 'plain', 'hill'],
['plain', 'camp', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'hill', 'hill'],
['plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'hill'],
['plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain'],
['plain', 'road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain']
]
};
export const firstBattleUnits: UnitData[] = [
{
id: 'liu-bei',
name: '유비',
faction: 'ally',
className: '군웅',
classKey: 'lord',
level: 3,
exp: 18,
hp: 32,
maxHp: 32,
attack: 9,
move: 4,
stats: { might: 76, intelligence: 78, leadership: 84, agility: 70, luck: 88 },
equipment: {
weapon: { itemId: 'twin-oath-blades', level: 1, exp: 18 },
armor: { itemId: 'oath-robe', level: 1, exp: 12 },
accessory: { itemId: 'peach-charm', level: 1, exp: 0 }
},
x: 1,
y: 15
},
{
id: 'guan-yu',
name: '관우',
faction: 'ally',
className: '의용 보병',
classKey: 'infantry',
level: 3,
exp: 26,
hp: 30,
maxHp: 30,
attack: 10,
move: 3,
stats: { might: 96, intelligence: 74, leadership: 91, agility: 72, luck: 68 },
equipment: {
weapon: { itemId: 'green-dragon-glaive', level: 1, exp: 26 },
armor: { itemId: 'reinforced-lamellar', level: 1, exp: 10 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 2,
y: 15
},
{
id: 'zhang-fei',
name: '장비',
faction: 'ally',
className: '창병',
classKey: 'spearman',
level: 3,
exp: 20,
hp: 30,
maxHp: 30,
attack: 10,
move: 3,
stats: { might: 95, intelligence: 42, leadership: 83, agility: 67, luck: 61 },
equipment: {
weapon: { itemId: 'serpent-spear', level: 1, exp: 20 },
armor: { itemId: 'lamellar-armor', level: 1, exp: 8 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 1,
y: 16
},
{
id: 'rebel-a',
name: '황건병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 1,
exp: 6,
hp: 19,
maxHp: 19,
attack: 6,
move: 3,
stats: { might: 49, intelligence: 34, leadership: 38, agility: 45, luck: 40 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 6 },
armor: { itemId: 'rebel-vest', level: 1, exp: 4 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 6,
y: 13
},
{
id: 'rebel-b',
name: '황건병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 1,
exp: 4,
hp: 20,
maxHp: 20,
attack: 6,
move: 3,
stats: { might: 47, intelligence: 32, leadership: 36, agility: 48, luck: 38 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 4 },
armor: { itemId: 'rebel-vest', level: 1, exp: 5 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 8,
y: 12
},
{
id: 'rebel-c',
name: '황건궁병',
faction: 'enemy',
className: '궁병',
classKey: 'archer',
level: 1,
exp: 10,
hp: 18,
maxHp: 18,
attack: 7,
move: 3,
stats: { might: 52, intelligence: 40, leadership: 43, agility: 56, luck: 42 },
equipment: {
weapon: { itemId: 'short-bow', level: 1, exp: 10 },
armor: { itemId: 'cloth-armor', level: 1, exp: 6 },
accessory: { itemId: 'wind-quiver', level: 1, exp: 0 }
},
x: 10,
y: 11
},
{
id: 'rebel-d',
name: '황건병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 1,
exp: 8,
hp: 20,
maxHp: 20,
attack: 6,
move: 3,
stats: { might: 51, intelligence: 31, leadership: 39, agility: 44, luck: 39 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 8 },
armor: { itemId: 'rebel-vest', level: 1, exp: 5 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 7,
y: 9
},
{
id: 'rebel-e',
name: '황건병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 1,
exp: 8,
hp: 21,
maxHp: 21,
attack: 6,
move: 3,
stats: { might: 50, intelligence: 33, leadership: 37, agility: 46, luck: 41 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 8 },
armor: { itemId: 'rebel-vest', level: 1, exp: 5 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 10,
y: 9
},
{
id: 'rebel-cavalry-a',
name: '황건기병',
faction: 'enemy',
className: '경기병',
classKey: 'cavalry',
level: 2,
exp: 14,
hp: 23,
maxHp: 23,
attack: 8,
move: 5,
stats: { might: 58, intelligence: 34, leadership: 45, agility: 62, luck: 42 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 12 },
armor: { itemId: 'rebel-vest', level: 1, exp: 6 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 12,
y: 10
},
{
id: 'rebel-archer-b',
name: '황건궁병',
faction: 'enemy',
className: '궁병',
classKey: 'archer',
level: 2,
exp: 12,
hp: 19,
maxHp: 19,
attack: 7,
move: 3,
stats: { might: 53, intelligence: 42, leadership: 44, agility: 57, luck: 44 },
equipment: {
weapon: { itemId: 'short-bow', level: 1, exp: 12 },
armor: { itemId: 'cloth-armor', level: 1, exp: 6 },
accessory: { itemId: 'wind-quiver', level: 1, exp: 0 }
},
x: 15,
y: 5
},
{
id: 'rebel-cavalry-b',
name: '황건기병',
faction: 'enemy',
className: '경기병',
classKey: 'cavalry',
level: 2,
exp: 16,
hp: 24,
maxHp: 24,
attack: 8,
move: 5,
stats: { might: 60, intelligence: 35, leadership: 48, agility: 63, luck: 45 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 14 },
armor: { itemId: 'rebel-vest', level: 1, exp: 7 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 13,
y: 7
},
{
id: 'rebel-f',
name: '황건병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 2,
exp: 10,
hp: 22,
maxHp: 22,
attack: 7,
move: 3,
stats: { might: 54, intelligence: 35, leadership: 43, agility: 47, luck: 42 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 10 },
armor: { itemId: 'rebel-vest', level: 1, exp: 6 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 15,
y: 3
},
{
id: 'rebel-g',
name: '황건병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 2,
exp: 11,
hp: 23,
maxHp: 23,
attack: 7,
move: 3,
stats: { might: 55, intelligence: 36, leadership: 44, agility: 48, luck: 43 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 10 },
armor: { itemId: 'rebel-vest', level: 1, exp: 6 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 17,
y: 3
},
{
id: 'rebel-leader',
name: '두령 한석',
faction: 'enemy',
className: '두령',
classKey: 'rebelLeader',
level: 2,
exp: 18,
hp: 32,
maxHp: 32,
attack: 10,
move: 3,
stats: { might: 64, intelligence: 46, leadership: 61, agility: 52, luck: 50 },
equipment: {
weapon: { itemId: 'leader-axe', level: 1, exp: 18 },
armor: { itemId: 'lamellar-armor', level: 1, exp: 12 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 16,
y: 2
}
];
const secondBattleAllyPositions: Record<string, { x: number; y: number }> = {
'liu-bei': { x: 2, y: 15 },
'guan-yu': { x: 3, y: 15 },
'zhang-fei': { x: 2, y: 16 }
};
export const secondBattleUnits: UnitData[] = [
...firstBattleUnits
.filter((unit) => unit.faction === 'ally')
.map((unit) => placeScenarioUnit(unit, secondBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
{
id: 'pursuit-raider-a',
name: '황건 추격병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 2,
exp: 12,
hp: 23,
maxHp: 23,
attack: 7,
move: 3,
stats: { might: 55, intelligence: 34, leadership: 43, agility: 48, luck: 42 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 12 },
armor: { itemId: 'rebel-vest', level: 1, exp: 6 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 7,
y: 12
},
{
id: 'pursuit-raider-b',
name: '황건 습격병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 2,
exp: 12,
hp: 23,
maxHp: 23,
attack: 7,
move: 3,
stats: { might: 54, intelligence: 35, leadership: 42, agility: 47, luck: 41 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 12 },
armor: { itemId: 'rebel-vest', level: 1, exp: 6 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 10,
y: 11
},
{
id: 'pursuit-bandit-a',
name: '산길 복병',
faction: 'enemy',
className: '산적',
classKey: 'bandit',
level: 2,
exp: 14,
hp: 24,
maxHp: 24,
attack: 8,
move: 3,
stats: { might: 58, intelligence: 32, leadership: 40, agility: 53, luck: 44 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 13 },
armor: { itemId: 'rebel-vest', level: 1, exp: 7 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 6,
y: 8
},
{
id: 'pursuit-archer-a',
name: '황건 궁병',
faction: 'enemy',
className: '궁병',
classKey: 'archer',
level: 2,
exp: 16,
hp: 20,
maxHp: 20,
attack: 8,
move: 3,
stats: { might: 54, intelligence: 43, leadership: 45, agility: 58, luck: 45 },
equipment: {
weapon: { itemId: 'short-bow', level: 1, exp: 16 },
armor: { itemId: 'cloth-armor', level: 1, exp: 7 },
accessory: { itemId: 'wind-quiver', level: 1, exp: 0 }
},
x: 12,
y: 10
},
{
id: 'pursuit-cavalry-a',
name: '황건 기병',
faction: 'enemy',
className: '경기병',
classKey: 'cavalry',
level: 3,
exp: 18,
hp: 26,
maxHp: 26,
attack: 9,
move: 5,
stats: { might: 62, intelligence: 35, leadership: 48, agility: 65, luck: 46 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 18 },
armor: { itemId: 'rebel-vest', level: 1, exp: 8 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 15,
y: 7
},
{
id: 'pursuit-raider-c',
name: '마을 습격병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 2,
exp: 13,
hp: 24,
maxHp: 24,
attack: 7,
move: 3,
stats: { might: 56, intelligence: 34, leadership: 43, agility: 49, luck: 42 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 13 },
armor: { itemId: 'rebel-vest', level: 1, exp: 7 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 17,
y: 6
},
{
id: 'pursuit-archer-b',
name: '황건 궁병',
faction: 'enemy',
className: '궁병',
classKey: 'archer',
level: 3,
exp: 18,
hp: 21,
maxHp: 21,
attack: 8,
move: 3,
stats: { might: 55, intelligence: 45, leadership: 46, agility: 59, luck: 47 },
equipment: {
weapon: { itemId: 'short-bow', level: 1, exp: 18 },
armor: { itemId: 'cloth-armor', level: 1, exp: 8 },
accessory: { itemId: 'wind-quiver', level: 1, exp: 0 }
},
x: 20,
y: 5
},
{
id: 'pursuit-cavalry-b',
name: '황건 기병',
faction: 'enemy',
className: '경기병',
classKey: 'cavalry',
level: 3,
exp: 19,
hp: 27,
maxHp: 27,
attack: 9,
move: 5,
stats: { might: 63, intelligence: 35, leadership: 49, agility: 66, luck: 47 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 19 },
armor: { itemId: 'rebel-vest', level: 1, exp: 8 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 18,
y: 3
},
{
id: 'pursuit-guard-a',
name: '한석 친위병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 3,
exp: 16,
hp: 25,
maxHp: 25,
attack: 8,
move: 3,
stats: { might: 58, intelligence: 36, leadership: 48, agility: 50, luck: 44 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 16 },
armor: { itemId: 'rebel-vest', level: 1, exp: 8 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 19,
y: 2
},
{
id: 'pursuit-guard-b',
name: '한석 친위병',
faction: 'enemy',
className: '황건병',
classKey: 'yellowTurban',
level: 3,
exp: 16,
hp: 25,
maxHp: 25,
attack: 8,
move: 3,
stats: { might: 58, intelligence: 36, leadership: 48, agility: 50, luck: 44 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 16 },
armor: { itemId: 'rebel-vest', level: 1, exp: 8 },
accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 }
},
x: 21,
y: 2
},
{
id: 'pursuit-leader-han-seok',
name: '두령 한석',
faction: 'enemy',
className: '두령',
classKey: 'rebelLeader',
level: 4,
exp: 24,
hp: 38,
maxHp: 38,
attack: 11,
move: 3,
stats: { might: 68, intelligence: 48, leadership: 64, agility: 54, luck: 52 },
equipment: {
weapon: { itemId: 'leader-axe', level: 1, exp: 24 },
armor: { itemId: 'lamellar-armor', level: 1, exp: 14 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 20,
y: 1
}
];
export const firstBattleBonds: BattleBond[] = [
{
id: 'liu-bei__guan-yu',
unitIds: ['liu-bei', 'guan-yu'],
title: '도원결의',
level: 72,
exp: 20,
description: '같은 적을 노릴 때 공격 피해 보너스와 연계 공격 확률이 오른다.'
},
{
id: 'liu-bei__zhang-fei',
unitIds: ['liu-bei', 'zhang-fei'],
title: '의형제',
level: 68,
exp: 12,
description: '공격 목표가 겹치면 공명 경험치가 빠르게 쌓인다.'
},
{
id: 'guan-yu__zhang-fei',
unitIds: ['guan-yu', 'zhang-fei'],
title: '맹장 공명',
level: 64,
exp: 8,
description: '둘이 같은 전선을 압박하면 연계 공격의 기초 확률이 오른다.'
}
];
export const secondBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
return {
...cloneUnitForScenario(unit),
x: position.x,
y: position.y
};
}
function cloneUnitForScenario(unit: UnitData): UnitData {
return JSON.parse(JSON.stringify(unit)) as UnitData;
}
function cloneBattleBondForScenario(bond: BattleBond): BattleBond {
return {
...bond,
unitIds: [...bond.unitIds] as [string, string]
};
}