Add Luo Castle outer wall chapter

This commit is contained in:
2026-06-23 09:43:18 +09:00
parent 8349d1044f
commit cc6365d5ba
11 changed files with 1270 additions and 34 deletions

View File

@@ -1828,6 +1828,68 @@ export const twentyEighthBattleVictoryPages: StoryPage[] = [
}
];
export const twentyNinthBattleIntroPages: StoryPage[] = [
{
id: 'twenty-ninth-luo-outer-wall',
bgm: 'story-dark',
chapter: '낙성의 바깥 성벽',
background: 'story-three-heroes',
speaker: '법정',
text: '부수관을 넘으면 낙성의 바깥 성벽이 기다립니다. 장임은 성 밖의 고개와 마을을 겹쳐 방어선을 세웠고, 성 안의 장수들은 아직 우리 뜻을 믿지 않습니다.'
},
{
id: 'twenty-ninth-wu-yi-rumor',
bgm: 'battle-prep',
chapter: '흔들리는 오의',
background: 'story-liu-bei',
speaker: '방통',
text: '오의는 성문을 지키지만 백성을 함부로 몰아붙이는 명령을 달가워하지 않는다고 합니다. 전장을 거칠게 밀지 않고 길을 열면, 그의 마음도 움직일 수 있습니다.'
},
{
id: 'twenty-ninth-roster-pressure',
bgm: 'battle-prep',
chapter: '법정의 첫 출전 선택',
background: 'story-militia',
speaker: '제갈량',
text: '이번 전투부터 법정도 출전 후보입니다. 익주 내부 사정을 아는 법정을 데려가면 설득은 쉬워지지만, 무장을 줄이는 부담은 더 커집니다.'
},
{
id: 'twenty-ninth-luo-sortie',
bgm: 'battle-prep',
chapter: '낙성 외곽전',
background: 'story-sortie',
speaker: '유비',
portrait: 'liuBei',
text: '낙성의 문을 여는 일은 칼보다 마음이 먼저요. 마을을 지키고 장임의 전열을 물리쳐, 성 안 사람들에게 우리가 어떤 군인지 보이겠소.'
}
];
export const twentyNinthBattleVictoryPages: StoryPage[] = [
{
id: 'twenty-ninth-victory-outer-wall',
bgm: 'militia-theme',
chapter: '외곽 방어선 붕괴',
background: 'story-sortie',
text: '장임은 낙성 안쪽으로 물러났고, 외곽 마을은 큰 피해 없이 유비군의 보호 아래 들어왔습니다. 성 안의 장수들은 유비군을 단순한 침입군으로만 볼 수 없게 되었습니다.'
},
{
id: 'twenty-ninth-wu-yi-joins',
bgm: 'battle-prep',
chapter: '오의의 합류',
background: 'story-liu-bei',
speaker: '오의',
text: '성문을 지킨다는 명분으로 백성을 몰아넣는 일은 장수의 길이 아니었습니다. 주공께서 마을을 먼저 살피셨으니, 이 오의도 낙성의 다음 길을 열겠습니다.'
},
{
id: 'twenty-ninth-next-luofeng',
bgm: 'story-dark',
chapter: '낙봉파의 그늘',
background: 'story-three-heroes',
speaker: '방통',
text: '낙성 바깥은 열렸지만 길은 더 좁아집니다. 봉황이 떨어진다는 이름의 고개가 있다 하니, 다음 진군은 가볍게 볼 수 없습니다.'
}
];
export const firstBattleMap: BattleMap = {
width: 20,
height: 18,
@@ -2141,6 +2203,12 @@ export const twentyEighthBattleMap: BattleMap = {
terrain: createTwentyEighthBattleTerrain()
};
export const twentyNinthBattleMap: BattleMap = {
width: 58,
height: 40,
terrain: createTwentyNinthBattleTerrain()
};
export const firstBattleUnits: UnitData[] = [
{
id: 'liu-bei',
@@ -4262,6 +4330,30 @@ export const fuPassRecruitUnits: UnitData[] = [
}
];
export const luoCastleRecruitUnits: UnitData[] = [
{
id: 'wu-yi',
name: '오의',
faction: 'ally',
className: '익주 장군',
classKey: 'infantry',
level: 18,
exp: 0,
hp: 46,
maxHp: 46,
attack: 15,
move: 4,
stats: { might: 82, intelligence: 72, leadership: 86, agility: 74, luck: 78 },
equipment: {
weapon: { itemId: 'iron-spear', level: 3, exp: 16 },
armor: { itemId: 'reinforced-lamellar', level: 2, exp: 18 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 9,
y: 26
}
];
const eighthBattleAllyPositions: Record<string, { x: number; y: number }> = {
'liu-bei': { x: 2, y: 17 },
'guan-yu': { x: 3, y: 17 },
@@ -11649,6 +11741,460 @@ export const twentyEighthBattleUnits: UnitData[] = [
}
];
const twentyNinthBattleAllyPositions: Record<string, { x: number; y: number }> = {
'liu-bei': { x: 4, y: 34 },
'guan-yu': { x: 6, y: 33 },
'zhang-fei': { x: 5, y: 36 },
'jian-yong': { x: 3, y: 37 },
'mi-zhu': { x: 4, y: 38 },
'sun-qian': { x: 7, y: 37 },
'zhao-yun': { x: 8, y: 34 },
'zhuge-liang': { x: 5, y: 34 },
'ma-liang': { x: 8, y: 38 },
'yi-ji': { x: 9, y: 36 },
'gong-zhi': { x: 10, y: 38 },
'huang-zhong': { x: 7, y: 35 },
'wei-yan': { x: 10, y: 33 },
'pang-tong': { x: 9, y: 35 },
'fa-zheng': { x: 11, y: 36 }
};
export const twentyNinthBattleUnits: UnitData[] = [
...[
...firstBattleUnits.filter((unit) => unit.faction === 'ally'),
...xuzhouRecruitUnits,
...caoBreakRecruitUnits,
...liuBiaoRecruitUnits,
...zhugeRecruitUnits,
...jingzhouRecruitUnits,
...guiyangRecruitUnits,
...wulingRecruitUnits,
...changshaRecruitUnits,
...yizhouRecruitUnits,
...fuPassRecruitUnits
].map((unit) => placeScenarioUnit(unit, twentyNinthBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
{
id: 'luo-scout-a',
name: '낙성 척후',
faction: 'enemy',
className: '외곽 척후',
classKey: 'bandit',
level: 47,
exp: 74,
hp: 122,
maxHp: 122,
attack: 46,
move: 4,
stats: { might: 142, intelligence: 100, leadership: 116, agility: 130, luck: 88 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 5, exp: 24 },
armor: { itemId: 'rebel-vest', level: 5, exp: 10 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 16,
y: 34
},
{
id: 'luo-scout-b',
name: '낙성 척후',
faction: 'enemy',
className: '동문 척후',
classKey: 'bandit',
level: 47,
exp: 74,
hp: 122,
maxHp: 122,
attack: 46,
move: 4,
stats: { might: 142, intelligence: 100, leadership: 116, agility: 130, luck: 88 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 5, exp: 24 },
armor: { itemId: 'rebel-vest', level: 5, exp: 10 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 22,
y: 32
},
{
id: 'luo-infantry-a',
name: '낙성 보병',
faction: 'enemy',
className: '외곽 보병',
classKey: 'yellowTurban',
level: 48,
exp: 76,
hp: 168,
maxHp: 168,
attack: 52,
move: 3,
stats: { might: 158, intelligence: 100, leadership: 136, agility: 108, luck: 88 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 36 },
armor: { itemId: 'lamellar-armor', level: 5, exp: 22 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 27,
y: 31
},
{
id: 'luo-infantry-b',
name: '낙성 보병',
faction: 'enemy',
className: '성벽 보병',
classKey: 'yellowTurban',
level: 48,
exp: 76,
hp: 168,
maxHp: 168,
attack: 52,
move: 3,
stats: { might: 158, intelligence: 100, leadership: 136, agility: 108, luck: 88 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 36 },
armor: { itemId: 'lamellar-armor', level: 5, exp: 22 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 34,
y: 27
},
{
id: 'luo-infantry-c',
name: '낙성 보병',
faction: 'enemy',
className: '서문 보병',
classKey: 'yellowTurban',
level: 49,
exp: 78,
hp: 176,
maxHp: 176,
attack: 54,
move: 3,
stats: { might: 162, intelligence: 102, leadership: 140, agility: 110, luck: 88 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 44 },
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 30 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 42,
y: 22
},
{
id: 'luo-infantry-d',
name: '낙성 보병',
faction: 'enemy',
className: '성문 보병',
classKey: 'yellowTurban',
level: 49,
exp: 78,
hp: 176,
maxHp: 176,
attack: 54,
move: 3,
stats: { might: 162, intelligence: 102, leadership: 140, agility: 110, luck: 88 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 44 },
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 30 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 50,
y: 18
},
{
id: 'luo-archer-a',
name: '낙성 궁병',
faction: 'enemy',
className: '산루 궁병',
classKey: 'archer',
level: 48,
exp: 76,
hp: 120,
maxHp: 120,
attack: 48,
move: 3,
stats: { might: 112, intelligence: 112, leadership: 118, agility: 126, luck: 90 },
equipment: {
weapon: { itemId: 'short-bow', level: 5, exp: 30 },
armor: { itemId: 'cloth-armor', level: 5, exp: 18 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 31,
y: 24
},
{
id: 'luo-archer-b',
name: '낙성 궁병',
faction: 'enemy',
className: '성벽 궁병',
classKey: 'archer',
level: 49,
exp: 78,
hp: 124,
maxHp: 124,
attack: 49,
move: 3,
stats: { might: 114, intelligence: 114, leadership: 120, agility: 128, luck: 90 },
equipment: {
weapon: { itemId: 'short-bow', level: 5, exp: 38 },
armor: { itemId: 'cloth-armor', level: 5, exp: 24 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 45,
y: 16
},
{
id: 'luo-archer-c',
name: '낙성 궁병',
faction: 'enemy',
className: '성루 궁병',
classKey: 'archer',
level: 49,
exp: 78,
hp: 124,
maxHp: 124,
attack: 49,
move: 3,
stats: { might: 114, intelligence: 114, leadership: 120, agility: 128, luck: 90 },
equipment: {
weapon: { itemId: 'short-bow', level: 5, exp: 38 },
armor: { itemId: 'cloth-armor', level: 5, exp: 24 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 53,
y: 21
},
{
id: 'luo-cavalry-a',
name: '낙성 기병',
faction: 'enemy',
className: '외곽 기병',
classKey: 'cavalry',
level: 49,
exp: 78,
hp: 178,
maxHp: 178,
attack: 55,
move: 5,
stats: { might: 162, intelligence: 102, leadership: 138, agility: 130, luck: 90 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 46 },
armor: { itemId: 'lamellar-armor', level: 5, exp: 32 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 25,
y: 36
},
{
id: 'luo-cavalry-b',
name: '낙성 기병',
faction: 'enemy',
className: '성문 기병',
classKey: 'cavalry',
level: 49,
exp: 78,
hp: 178,
maxHp: 178,
attack: 55,
move: 5,
stats: { might: 162, intelligence: 102, leadership: 138, agility: 130, luck: 90 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 46 },
armor: { itemId: 'lamellar-armor', level: 5, exp: 32 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 44,
y: 31
},
{
id: 'luo-cavalry-c',
name: '낙성 기병',
faction: 'enemy',
className: '동문 기병',
classKey: 'cavalry',
level: 50,
exp: 80,
hp: 186,
maxHp: 186,
attack: 57,
move: 5,
stats: { might: 166, intelligence: 104, leadership: 142, agility: 132, luck: 90 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 54 },
armor: { itemId: 'lamellar-armor', level: 5, exp: 40 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 52,
y: 29
},
{
id: 'luo-guard-a',
name: '장임 친위대',
faction: 'enemy',
className: '낙성 친위대',
classKey: 'yellowTurban',
level: 50,
exp: 80,
hp: 188,
maxHp: 188,
attack: 57,
move: 3,
stats: { might: 168, intelligence: 104, leadership: 146, agility: 112, luck: 90 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 58 },
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 42 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 50,
y: 16
},
{
id: 'luo-guard-b',
name: '장임 친위대',
faction: 'enemy',
className: '성문 친위대',
classKey: 'yellowTurban',
level: 50,
exp: 80,
hp: 188,
maxHp: 188,
attack: 57,
move: 3,
stats: { might: 168, intelligence: 104, leadership: 146, agility: 112, luck: 90 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 58 },
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 42 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 54,
y: 19
},
{
id: 'luo-strategist-a',
name: '낙성 책사',
faction: 'enemy',
className: '성벽 책사',
classKey: 'archer',
level: 49,
exp: 78,
hp: 124,
maxHp: 124,
attack: 49,
move: 3,
stats: { might: 96, intelligence: 128, leadership: 120, agility: 102, luck: 90 },
equipment: {
weapon: { itemId: 'short-bow', level: 5, exp: 40 },
armor: { itemId: 'cloth-armor', level: 5, exp: 26 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 46,
y: 14
},
{
id: 'luo-strategist-b',
name: '낙성 책사',
faction: 'enemy',
className: '외곽 책사',
classKey: 'archer',
level: 49,
exp: 78,
hp: 124,
maxHp: 124,
attack: 49,
move: 3,
stats: { might: 96, intelligence: 128, leadership: 120, agility: 102, luck: 90 },
equipment: {
weapon: { itemId: 'short-bow', level: 5, exp: 40 },
armor: { itemId: 'cloth-armor', level: 5, exp: 26 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 53,
y: 24
},
{
id: 'luo-ambusher-a',
name: '낙성 복병',
faction: 'enemy',
className: '서쪽 복병',
classKey: 'bandit',
level: 48,
exp: 76,
hp: 128,
maxHp: 128,
attack: 48,
move: 4,
stats: { might: 146, intelligence: 102, leadership: 118, agility: 132, luck: 88 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 5, exp: 34 },
armor: { itemId: 'rebel-vest', level: 5, exp: 20 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 34,
y: 36
},
{
id: 'luo-ambusher-b',
name: '낙성 복병',
faction: 'enemy',
className: '북쪽 복병',
classKey: 'bandit',
level: 48,
exp: 76,
hp: 128,
maxHp: 128,
attack: 48,
move: 4,
stats: { might: 146, intelligence: 102, leadership: 118, agility: 132, luck: 88 },
equipment: {
weapon: { itemId: 'yellow-turban-saber', level: 5, exp: 34 },
armor: { itemId: 'rebel-vest', level: 5, exp: 20 },
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
},
x: 37,
y: 8
},
{
id: 'luo-officer-wu-yi',
name: '오의',
faction: 'enemy',
className: '익주 장군',
classKey: 'yellowTurban',
level: 50,
exp: 82,
hp: 190,
maxHp: 190,
attack: 56,
move: 4,
stats: { might: 158, intelligence: 108, leadership: 148, agility: 114, luck: 90 },
equipment: {
weapon: { itemId: 'iron-spear', level: 5, exp: 60 },
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 46 },
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
},
x: 47,
y: 23
},
{
id: 'luo-leader-zhang-ren',
name: '장임',
faction: 'enemy',
className: '낙성 수장',
classKey: 'rebelLeader',
level: 51,
exp: 84,
hp: 238,
maxHp: 238,
attack: 59,
move: 4,
stats: { might: 170, intelligence: 118, leadership: 152, agility: 116, luck: 92 },
equipment: {
weapon: { itemId: 'leader-axe', level: 5, exp: 48 },
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 48 },
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
},
x: 54,
y: 18
}
];
export const firstBattleBonds: BattleBond[] = [
{
id: 'liu-bei__guan-yu',
@@ -11946,6 +12492,33 @@ export const fuPassRecruitBonds: BattleBond[] = [
}
];
export const luoCastleRecruitBonds: BattleBond[] = [
{
id: 'liu-bei__wu-yi',
unitIds: ['liu-bei', 'wu-yi'],
title: '낙성의 새 길',
level: 34,
exp: 0,
description: '유비의 절제된 진군과 오의의 성문 지식이 만나 낙성으로 들어가는 길을 열어 갑니다.'
},
{
id: 'fa-zheng__wu-yi',
unitIds: ['fa-zheng', 'wu-yi'],
title: '익주 내부 설득',
level: 32,
exp: 0,
description: '법정의 내부 정세 판단과 오의의 현장 인맥이 맞물려 익주 장수들의 마음을 흔듭니다.'
},
{
id: 'pang-tong__wu-yi',
unitIds: ['pang-tong', 'wu-yi'],
title: '봉추와 성문 장수',
level: 30,
exp: 0,
description: '방통의 빠른 계책과 오의의 성문 방비 지식이 합쳐져 좁은 길에서도 새로운 돌파구를 찾습니다.'
}
];
export const secondBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
export const thirdBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
export const fourthBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
@@ -11979,6 +12552,7 @@ export const twentyFifthBattleBonds: BattleBond[] = [...twentyFourthBattleBonds,
export const twentySixthBattleBonds: BattleBond[] = [...twentyFifthBattleBonds, ...wulingRecruitBonds].map(cloneBattleBondForScenario);
export const twentySeventhBattleBonds: BattleBond[] = [...twentySixthBattleBonds, ...changshaRecruitBonds].map(cloneBattleBondForScenario);
export const twentyEighthBattleBonds: BattleBond[] = [...twentySeventhBattleBonds, ...yizhouRecruitBonds].map(cloneBattleBondForScenario);
export const twentyNinthBattleBonds: BattleBond[] = [...twentyEighthBattleBonds, ...fuPassRecruitBonds].map(cloneBattleBondForScenario);
function createEighthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 22 }, (_, y) =>
@@ -13056,6 +13630,66 @@ function createTwentyEighthBattleTerrain(): TerrainType[][] {
);
}
function createTwentyNinthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 40 }, (_, y) =>
Array.from({ length: 58 }, (_, x): TerrainType => {
if (x <= 4 && y >= 33) {
return 'camp';
}
if ((x >= 48 && x <= 56 && y >= 13 && y <= 23) || (x >= 44 && x <= 50 && y >= 10 && y <= 15)) {
return 'fort';
}
if (
(x >= 18 && x <= 21 && y >= 30 && y <= 32) ||
(x >= 34 && x <= 36 && y >= 25 && y <= 27) ||
(x >= 41 && x <= 43 && y >= 19 && y <= 21)
) {
return 'village';
}
if (
(y >= 35 && x >= 4 && x <= 16) ||
(x >= 15 && x <= 27 && y >= 31 && y <= 35) ||
(x >= 26 && x <= 36 && y >= 27 && y <= 31) ||
(x >= 35 && x <= 45 && y >= 22 && y <= 27) ||
(x >= 44 && x <= 53 && y >= 17 && y <= 22) ||
(x >= 52 && x <= 56 && y >= 18 && y <= 20)
) {
return 'road';
}
if ((x === 12 || x === 13) && y >= 0 && y <= 29) {
return 'river';
}
if ((x === 31 || x === 32) && y >= 2 && y <= 25) {
return 'river';
}
if ((x === 40 || x === 41) && y >= 30 && y <= 39) {
return 'river';
}
if (
(x >= 4 && x <= 18 && y >= 8 && y <= 24) ||
(x >= 19 && x <= 30 && y >= 12 && y <= 25) ||
(x >= 33 && x <= 46 && y >= 30 && y <= 39) ||
(x >= 45 && x <= 57 && y >= 25 && y <= 38)
) {
return 'forest';
}
if (
(x >= 0 && x <= 10 && y <= 13) ||
(x >= 20 && x <= 30 && y <= 11) ||
(x >= 32 && x <= 47 && y >= 3 && y <= 14) ||
(x >= 50 && x <= 57 && y <= 12) ||
(x >= 24 && x <= 34 && y >= 33 && y <= 39)
) {
return 'hill';
}
if ((x <= 2 && y <= 32) || (x >= 56 && y >= 23) || (x >= 45 && x <= 47 && y <= 9)) {
return 'cliff';
}
return 'plain';
})
);
}
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
return {
...cloneUnitForScenario(unit),