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 thirdBattleIntroPages: StoryPage[] = [ { id: 'third-guangzong-report', bgm: 'militia-theme', chapter: '광종의 급보', background: 'story-militia', text: '탁현 북쪽의 소란을 잠재우자, 장터에는 더 큰 소식이 밀려왔다. 광종 방면에서 황건 본대가 관군을 압박하고 있다는 전갈이었다.' }, { id: 'third-liu-bei-decision', bgm: 'battle-prep', chapter: '넓어지는 길', background: 'story-liu-bei', speaker: '유비', portrait: 'liuBei', text: '작은 마을만 지키고 물러선다면 난세는 끝나지 않는다. 광종으로 향하는 길을 열어, 더 많은 백성이 버틸 시간을 만들어야 한다.' }, { id: 'third-guan-yu-bridge', bgm: 'battle-prep', chapter: '강가의 길목', background: 'story-three-heroes', speaker: '관우', portrait: 'guanYu', text: '적 전령이 강가 요새를 거쳐 본대로 향한다면, 그 길을 끊어야 합니다. 좁은 다리와 숲길을 먼저 장악하겠습니다.' }, { id: 'third-zhang-fei-vanguard', bgm: 'battle-prep', chapter: '전초전', background: 'story-sortie', speaker: '장비', portrait: 'zhangFei', text: '전령이든 두령이든 내 앞길에 서면 다 똑같소! 형님, 명만 내리시오. 길목을 시원하게 열어 버리겠소!' } ]; export const thirdBattleVictoryPages: StoryPage[] = [ { id: 'third-victory-road', bgm: 'militia-theme', chapter: '열린 길목', background: 'story-sortie', text: '황건 전령의 깃발이 꺾이자 광종으로 향하는 길목에 잠시 숨통이 트였다. 흩어진 백성들은 의용군의 이름을 기억하기 시작했다.' }, { id: 'third-victory-liu-bei', bgm: 'militia-theme', chapter: '큰 싸움의 그림자', background: 'story-liu-bei', speaker: '유비', portrait: 'liuBei', text: '우리가 막은 것은 작은 전갈 하나일 뿐이다. 그러나 작은 길 하나가 열리면, 다음 전장에서도 백성을 구할 길이 생긴다.' }, { id: 'third-victory-next', bgm: 'battle-prep', chapter: '광종으로', background: 'story-militia', text: '세 형제는 병사를 추스르고 광종 방면으로 눈을 돌렸다. 황건의 큰 물결은 아직 꺾이지 않았다.' } ]; export const fourthBattleIntroPages: StoryPage[] = [ { id: 'fourth-guangzong-camp', bgm: 'militia-theme', chapter: '광종 본영', background: 'story-militia', text: '전령의 길이 끊기자 황건 본대는 광종 들판에 진을 굳혔다. 노란 깃발이 겹겹이 서고, 장각의 이름이 병사들 사이에 낮게 울렸다.' }, { id: 'fourth-liu-bei-resolve', bgm: 'battle-prep', chapter: '의용군의 결의', background: 'story-liu-bei', speaker: '유비', portrait: 'liuBei', text: '장각을 치는 일은 한 사람을 꺾는 일이 아니다. 두려움에 몰린 백성들이 다시 길을 찾게 하는 일이다.' }, { id: 'fourth-guan-yu-line', bgm: 'battle-prep', chapter: '본영 돌파', background: 'story-three-heroes', speaker: '관우', portrait: 'guanYu', text: '적은 요새와 숲 사이에 궁병을 숨겼습니다. 전열을 흐트러뜨리지 않고 본영의 깃발까지 밀고 들어가야 합니다.' }, { id: 'fourth-zhang-fei-roar', bgm: 'battle-prep', chapter: '황건적 토벌', background: 'story-sortie', speaker: '장비', portrait: 'zhangFei', text: '좋소! 광종의 누런 깃발을 오늘 모두 뽑아 버립시다. 뒤돌아볼 틈 없이 밀고 들어가겠소!' } ]; export const fourthBattleVictoryPages: StoryPage[] = [ { id: 'fourth-victory-yellow-flags-fall', bgm: 'militia-theme', chapter: '황건적 토벌', background: 'story-militia', text: '광종의 들판에서 황건의 본영이 무너졌다. 피난민들은 오래 닫았던 문을 열고, 쓰러진 깃발 너머로 관군과 의용군을 바라보았다.' }, { id: 'fourth-victory-liu-bei', bgm: 'militia-theme', chapter: '이름이 퍼지다', background: 'story-liu-bei', speaker: '유비', portrait: 'liuBei', text: '난은 잠시 가라앉았지만, 천하가 바로 선 것은 아니다. 오늘의 공이 교만이 되지 않도록 마음을 다잡자.' }, { id: 'fourth-victory-next-era', 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 thirdBattleMap: BattleMap = { width: 24, height: 20, terrain: [ ['forest', 'forest', 'hill', 'plain', 'plain', 'road', 'plain', 'forest', 'hill', 'plain', 'river', 'river', 'plain', 'forest', 'forest', 'hill', 'hill', 'plain', 'fort', 'fort', 'plain', 'forest', 'forest', 'hill'], ['forest', 'hill', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'plain', 'plain', 'river', 'river', 'road', 'plain', 'forest', 'plain', 'hill', 'plain', 'fort', 'plain', 'plain', 'plain', 'forest', 'forest'], ['hill', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'forest'], ['plain', 'plain', 'forest', 'road', 'plain', 'plain', 'forest', 'plain', 'hill', 'road', 'river', 'river', 'road', 'road', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain'], ['plain', 'forest', 'forest', 'road', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'river', 'river', 'plain', 'road', 'plain', 'plain', 'hill', 'cliff', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain'], ['plain', 'plain', 'forest', 'road', 'road', 'plain', 'plain', 'hill', 'plain', 'road', 'plain', 'river', 'river', 'road', 'plain', 'village', 'plain', 'hill', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain'], ['plain', 'plain', 'plain', 'plain', 'road', 'plain', 'hill', 'hill', 'plain', 'road', 'plain', 'river', 'river', 'road', 'plain', 'village', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'forest', 'plain'], ['plain', 'forest', 'plain', 'plain', 'road', 'plain', 'plain', 'hill', 'plain', 'road', 'road', 'plain', 'river', 'river', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain'], ['forest', 'forest', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'hill', 'plain', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain'], ['forest', 'plain', 'plain', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain'], ['plain', 'plain', 'road', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'plain'], ['plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'village', 'plain', 'plain', 'plain', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain'], ['hill', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'village', 'plain', 'forest', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'plain', 'plain', 'plain', 'plain'], ['hill', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'plain', 'plain'], ['plain', 'road', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'plain'], ['camp', 'road', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'hill'], ['camp', 'camp', 'road', 'plain', 'forest', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'hill'], ['plain', 'camp', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'hill', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain'], ['plain', 'plain', 'camp', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'plain'], ['plain', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain'] ] }; export const fourthBattleMap: BattleMap = { width: 24, height: 20, terrain: [ ['forest', 'forest', 'hill', 'hill', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'forest', 'hill', 'plain', 'plain', 'fort', 'fort', 'fort', 'plain', 'hill', 'hill', 'forest', 'forest', 'plain', 'plain'], ['forest', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'hill', 'plain', 'plain', 'fort', 'fort', 'fort', 'plain', 'plain', 'hill', 'forest', 'forest', 'plain', 'plain', 'hill'], ['hill', 'plain', 'plain', 'forest', 'plain', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'road', 'road', 'fort', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'hill', 'hill'], ['plain', 'plain', 'forest', 'forest', 'plain', 'road', 'plain', 'forest', 'plain', 'plain', 'hill', 'plain', 'road', 'road', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain'], ['plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'hill', 'hill', 'plain', 'river', 'river', 'road', 'road', 'plain', 'hill', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain'], ['plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'road', 'road', 'plain', 'village', 'plain', 'plain', 'forest', 'forest', 'plain'], ['plain', 'forest', 'plain', 'plain', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'river', 'river', 'plain', 'plain', 'road', 'plain', 'village', 'plain', 'forest', 'forest', 'plain', 'plain'], ['plain', 'forest', 'plain', 'road', 'road', 'plain', 'hill', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'river', 'river', 'plain', 'road', 'road', 'plain', 'plain', 'plain', 'forest', 'hill', 'plain'], ['plain', 'plain', 'road', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'road', 'plain', 'plain', 'hill', 'hill', 'plain', 'plain'], ['plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'village', 'plain', 'plain', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'road', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain'], ['road', 'road', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'village', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'plain'], ['plain', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'road', 'road', 'plain', 'forest', 'plain', 'plain'], ['plain', 'road', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'forest', 'plain', 'hill'], ['hill', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'hill', 'hill'], ['hill', 'road', 'road', 'plain', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain', 'hill'], ['plain', 'camp', 'road', 'road', 'plain', 'hill', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain', 'plain'], ['camp', 'camp', 'road', 'plain', 'plain', 'hill', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'river', 'plain'], ['plain', 'camp', 'road', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'river', 'plain'], ['plain', 'plain', 'road', 'road', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', 'plain', 'plain', 'plain'], ['plain', 'plain', 'plain', 'road', 'road', 'plain', 'forest', 'forest', 'plain', 'plain', 'plain', 'forest', 'plain', 'plain', 'plain', 'plain', 'hill', 'plain', 'plain', 'plain', 'road', 'road', '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 = { '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 } ]; const thirdBattleAllyPositions: Record = { 'liu-bei': { x: 2, y: 17 }, 'guan-yu': { x: 3, y: 17 }, 'zhang-fei': { x: 2, y: 18 } }; export const thirdBattleUnits: UnitData[] = [ ...firstBattleUnits .filter((unit) => unit.faction === 'ally') .map((unit) => placeScenarioUnit(unit, thirdBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })), { id: 'guangzong-scout-a', name: '황건 척후병', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 3, exp: 18, hp: 25, maxHp: 25, attack: 8, move: 3, stats: { might: 58, intelligence: 36, leadership: 45, agility: 52, luck: 43 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 18 }, armor: { itemId: 'rebel-vest', level: 1, exp: 8 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 6, y: 15 }, { id: 'guangzong-bandit-a', name: '숲길 복병', faction: 'enemy', className: '도적', classKey: 'bandit', level: 3, exp: 20, hp: 26, maxHp: 26, attack: 9, move: 3, stats: { might: 62, intelligence: 34, leadership: 42, agility: 58, luck: 46 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 18 }, armor: { itemId: 'rebel-vest', level: 1, exp: 8 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 8, y: 13 }, { id: 'guangzong-archer-a', name: '강가 궁병', faction: 'enemy', className: '궁병', classKey: 'archer', level: 3, exp: 20, hp: 22, maxHp: 22, attack: 9, move: 3, stats: { might: 57, intelligence: 45, leadership: 47, agility: 60, luck: 48 }, equipment: { weapon: { itemId: 'short-bow', level: 1, exp: 20 }, armor: { itemId: 'cloth-armor', level: 1, exp: 8 }, accessory: { itemId: 'wind-quiver', level: 1, exp: 0 } }, x: 11, y: 14 }, { id: 'guangzong-cavalry-a', name: '황건 기병', faction: 'enemy', className: '경기병', classKey: 'cavalry', level: 4, exp: 22, hp: 29, maxHp: 29, attack: 10, move: 5, stats: { might: 66, intelligence: 36, leadership: 50, agility: 68, luck: 48 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 22 }, armor: { itemId: 'rebel-vest', level: 1, exp: 9 }, accessory: { itemId: 'grain-pouch', level: 1, exp: 0 } }, x: 12, y: 11 }, { id: 'guangzong-raider-a', name: '황건 장정', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 3, exp: 18, hp: 26, maxHp: 26, attack: 8, move: 3, stats: { might: 59, intelligence: 35, leadership: 46, agility: 51, luck: 44 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 18 }, armor: { itemId: 'rebel-vest', level: 1, exp: 8 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 10, y: 9 }, { id: 'guangzong-raider-b', name: '황건 장정', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 3, exp: 18, hp: 26, maxHp: 26, attack: 8, move: 3, stats: { might: 59, intelligence: 35, leadership: 46, agility: 51, luck: 44 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 18 }, armor: { itemId: 'rebel-vest', level: 1, exp: 8 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 14, y: 10 }, { id: 'guangzong-archer-b', name: '요새 궁병', faction: 'enemy', className: '궁병', classKey: 'archer', level: 4, exp: 24, hp: 23, maxHp: 23, attack: 9, move: 3, stats: { might: 58, intelligence: 47, leadership: 48, agility: 61, luck: 49 }, equipment: { weapon: { itemId: 'short-bow', level: 1, exp: 24 }, armor: { itemId: 'cloth-armor', level: 1, exp: 9 }, accessory: { itemId: 'wind-quiver', level: 1, exp: 0 } }, x: 15, y: 7 }, { id: 'guangzong-cavalry-b', name: '전령 기병', faction: 'enemy', className: '경기병', classKey: 'cavalry', level: 4, exp: 25, hp: 30, maxHp: 30, attack: 10, move: 5, stats: { might: 67, intelligence: 38, leadership: 51, agility: 70, luck: 50 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 25 }, armor: { itemId: 'rebel-vest', level: 1, exp: 10 }, accessory: { itemId: 'grain-pouch', level: 1, exp: 0 } }, x: 17, y: 6 }, { id: 'guangzong-guard-a', name: '요새 수비병', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 4, exp: 22, hp: 28, maxHp: 28, attack: 9, move: 3, stats: { might: 62, intelligence: 37, leadership: 50, agility: 52, luck: 46 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 22 }, armor: { itemId: 'rebel-vest', level: 1, exp: 10 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 18, y: 4 }, { id: 'guangzong-guard-b', name: '요새 수비병', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 4, exp: 22, hp: 28, maxHp: 28, attack: 9, move: 3, stats: { might: 62, intelligence: 37, leadership: 50, agility: 52, luck: 46 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 22 }, armor: { itemId: 'rebel-vest', level: 1, exp: 10 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 20, y: 4 }, { id: 'guangzong-fort-archer', name: '망루 궁병', faction: 'enemy', className: '궁병', classKey: 'archer', level: 4, exp: 25, hp: 24, maxHp: 24, attack: 10, move: 3, stats: { might: 60, intelligence: 49, leadership: 49, agility: 61, luck: 50 }, equipment: { weapon: { itemId: 'short-bow', level: 1, exp: 25 }, armor: { itemId: 'cloth-armor', level: 1, exp: 10 }, accessory: { itemId: 'wind-quiver', level: 1, exp: 0 } }, x: 19, y: 2 }, { id: 'guangzong-leader-ma-yuan', name: '전령 마원', faction: 'enemy', className: '두령', classKey: 'rebelLeader', level: 5, exp: 30, hp: 42, maxHp: 42, attack: 12, move: 3, stats: { might: 72, intelligence: 50, leadership: 68, agility: 56, luck: 54 }, equipment: { weapon: { itemId: 'leader-axe', level: 2, exp: 8 }, armor: { itemId: 'lamellar-armor', level: 1, exp: 18 }, accessory: { itemId: 'grain-pouch', level: 1, exp: 0 } }, x: 20, y: 2 } ]; const fourthBattleAllyPositions: Record = { 'liu-bei': { x: 2, y: 16 }, 'guan-yu': { x: 3, y: 16 }, 'zhang-fei': { x: 2, y: 17 } }; export const fourthBattleUnits: UnitData[] = [ ...firstBattleUnits .filter((unit) => unit.faction === 'ally') .map((unit) => placeScenarioUnit(unit, fourthBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })), { id: 'guangzong-main-vanguard-a', name: '황건 선봉', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 4, exp: 24, hp: 28, maxHp: 28, attack: 9, move: 3, stats: { might: 63, intelligence: 36, leadership: 48, agility: 53, luck: 45 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 24 }, armor: { itemId: 'rebel-vest', level: 1, exp: 10 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 6, y: 14 }, { id: 'guangzong-main-vanguard-b', name: '황건 선봉', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 4, exp: 24, hp: 28, maxHp: 28, attack: 9, move: 3, stats: { might: 63, intelligence: 36, leadership: 48, agility: 53, luck: 45 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 24 }, armor: { itemId: 'rebel-vest', level: 1, exp: 10 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 8, y: 12 }, { id: 'guangzong-main-bandit-a', name: '숲길 돌격병', faction: 'enemy', className: '도적', classKey: 'bandit', level: 4, exp: 26, hp: 29, maxHp: 29, attack: 10, move: 3, stats: { might: 66, intelligence: 34, leadership: 44, agility: 61, luck: 48 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 24 }, armor: { itemId: 'rebel-vest', level: 1, exp: 10 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 5, y: 10 }, { id: 'guangzong-main-archer-a', name: '황건 궁병', faction: 'enemy', className: '궁병', classKey: 'archer', level: 4, exp: 26, hp: 24, maxHp: 24, attack: 10, move: 3, stats: { might: 60, intelligence: 48, leadership: 50, agility: 62, luck: 50 }, equipment: { weapon: { itemId: 'short-bow', level: 1, exp: 26 }, armor: { itemId: 'cloth-armor', level: 1, exp: 10 }, accessory: { itemId: 'wind-quiver', level: 1, exp: 0 } }, x: 9, y: 10 }, { id: 'guangzong-main-cavalry-a', name: '황건 기병', faction: 'enemy', className: '경기병', classKey: 'cavalry', level: 5, exp: 28, hp: 32, maxHp: 32, attack: 11, move: 5, stats: { might: 70, intelligence: 38, leadership: 52, agility: 72, luck: 51 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 28 }, armor: { itemId: 'rebel-vest', level: 1, exp: 12 }, accessory: { itemId: 'grain-pouch', level: 1, exp: 0 } }, x: 12, y: 9 }, { id: 'guangzong-main-guard-a', name: '본영 수비병', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 5, exp: 28, hp: 31, maxHp: 31, attack: 10, move: 3, stats: { might: 68, intelligence: 38, leadership: 54, agility: 55, luck: 49 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 28 }, armor: { itemId: 'rebel-vest', level: 1, exp: 12 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 14, y: 7 }, { id: 'guangzong-main-archer-b', name: '본영 궁병', faction: 'enemy', className: '궁병', classKey: 'archer', level: 5, exp: 30, hp: 25, maxHp: 25, attack: 10, move: 3, stats: { might: 61, intelligence: 50, leadership: 51, agility: 63, luck: 51 }, equipment: { weapon: { itemId: 'short-bow', level: 1, exp: 30 }, armor: { itemId: 'cloth-armor', level: 1, exp: 12 }, accessory: { itemId: 'wind-quiver', level: 1, exp: 0 } }, x: 16, y: 6 }, { id: 'guangzong-main-cavalry-b', name: '황건 기병', faction: 'enemy', className: '경기병', classKey: 'cavalry', level: 5, exp: 30, hp: 33, maxHp: 33, attack: 11, move: 5, stats: { might: 71, intelligence: 38, leadership: 53, agility: 73, luck: 51 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 30 }, armor: { itemId: 'rebel-vest', level: 1, exp: 12 }, accessory: { itemId: 'grain-pouch', level: 1, exp: 0 } }, x: 18, y: 10 }, { id: 'guangzong-main-guard-b', name: '본영 수비병', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 5, exp: 30, hp: 32, maxHp: 32, attack: 10, move: 3, stats: { might: 69, intelligence: 39, leadership: 55, agility: 55, luck: 50 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 30 }, armor: { itemId: 'rebel-vest', level: 1, exp: 12 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 18, y: 4 }, { id: 'guangzong-main-guard-c', name: '본영 수비병', faction: 'enemy', className: '황건병', classKey: 'yellowTurban', level: 5, exp: 30, hp: 32, maxHp: 32, attack: 10, move: 3, stats: { might: 69, intelligence: 39, leadership: 55, agility: 55, luck: 50 }, equipment: { weapon: { itemId: 'yellow-turban-saber', level: 1, exp: 30 }, armor: { itemId: 'rebel-vest', level: 1, exp: 12 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 14, y: 3 }, { id: 'guangzong-main-archer-c', name: '망루 궁병', faction: 'enemy', className: '궁병', classKey: 'archer', level: 5, exp: 32, hp: 26, maxHp: 26, attack: 11, move: 3, stats: { might: 62, intelligence: 52, leadership: 52, agility: 64, luck: 52 }, equipment: { weapon: { itemId: 'short-bow', level: 2, exp: 6 }, armor: { itemId: 'cloth-armor', level: 1, exp: 12 }, accessory: { itemId: 'wind-quiver', level: 1, exp: 0 } }, x: 16, y: 1 }, { id: 'guangzong-main-leader-zhang-jue', name: '장각', faction: 'enemy', className: '황건 대현량사', classKey: 'rebelLeader', level: 6, exp: 40, hp: 48, maxHp: 48, attack: 13, move: 3, stats: { might: 74, intelligence: 78, leadership: 82, agility: 58, luck: 64 }, equipment: { weapon: { itemId: 'leader-axe', level: 2, exp: 16 }, armor: { itemId: 'lamellar-armor', level: 2, exp: 4 }, accessory: { itemId: 'yellow-scarf-charm', level: 1, exp: 0 } }, x: 15, 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); export const thirdBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario); export const fourthBattleBonds: 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] }; }