Add final Meng Huo capture battle

This commit is contained in:
2026-06-24 00:35:04 +09:00
parent a54e852c2b
commit 1c26583508
11 changed files with 883 additions and 63 deletions

View File

@@ -3456,6 +3456,59 @@ export const fiftyThirdBattleVictoryPages: StoryPage[] = [
}
];
export const fiftyFourthBattleIntroPages: StoryPage[] = [
{
id: 'fifty-fourth-final-council',
bgm: 'story-dark',
chapter: '마지막 문턱',
background: 'story-militia',
speaker: '마량',
text: '여섯 번째 생포 뒤 남중의 촌장들은 더는 맹획의 말만 듣지 않았습니다. 그러나 맹획은 마지막 자존심을 걸고, 모든 부족 앞에서 다시 한 번 싸워 보자고 외쳤습니다.'
},
{
id: 'fifty-fourth-seventh-capture-plan',
bgm: 'battle-prep',
chapter: '일곱 번째 생포',
background: 'story-three-heroes',
speaker: '제갈량',
text: '이번 싸움은 남중을 꺾는 전투가 아니라 남중이 스스로 고개를 끄덕이게 만드는 전투입니다. 맹획을 마지막으로 생포하되, 회의장과 마을, 신뢰 장부를 모두 지키십시오.'
},
{
id: 'fifty-fourth-sortie',
bgm: 'battle-prep',
chapter: '칠종칠금 7차전',
background: 'story-sortie',
speaker: '왕평',
text: '마지막 전장은 길이 많아 보이지만, 잘못 밀고 들어가면 촌장들이 보는 앞에서 군율을 잃게 됩니다. 빠른 장수는 추격을 멈추고, 문사는 약속을 남기며, 전열은 회의장을 지켜야 합니다.'
}
];
export const fiftyFourthBattleVictoryPages: StoryPage[] = [
{
id: 'fifty-fourth-victory-seventh-capture',
bgm: 'militia-theme',
chapter: '일곱 번째 생포',
background: 'story-sortie',
text: '맹획은 일곱 번째로 포위되었습니다. 그러나 이번에는 포로장의 침묵이 아니라, 촌장들의 눈앞에서 지켜진 약속과 무너진 고집이 함께 남았습니다.'
},
{
id: 'fifty-fourth-victory-meng-huo-yields',
bgm: 'story-dark',
chapter: '맹획의 항복',
background: 'story-three-heroes',
speaker: '맹획',
text: '일곱 번 잡히고 일곱 번 풀려도 그대들은 남중의 백성을 짓밟지 않았다. 이제는 내가 졌음을 인정하겠다. 남중의 길은 촉한의 길과 함께하겠다.'
},
{
id: 'fifty-fourth-nanzhong-pacified',
bgm: 'story-dark',
chapter: '남중 평정',
background: 'story-militia',
speaker: '제갈량',
text: '남중은 힘으로만 묶인 땅이 아닙니다. 약속을 지킨 뒤 얻은 땅입니다. 이제 촉한은 뒤를 안정시키고, 북쪽을 바라볼 준비를 시작해야 합니다.'
}
];
export const firstBattleMap: BattleMap = {
width: 20,
height: 18,
@@ -3919,6 +3972,12 @@ export const fiftyThirdBattleMap: BattleMap = {
terrain: createFiftyThirdBattleTerrain()
};
export const fiftyFourthBattleMap: BattleMap = {
width: 108,
height: 90,
terrain: createFiftyFourthBattleTerrain()
};
export const firstBattleUnits: UnitData[] = [
{
id: 'liu-bei',
@@ -16482,6 +16541,96 @@ export const fiftyThirdBattleUnits: UnitData[] = [
createThirtyFirstEnemyUnit('menghuo-sixth-leader', '맹획', '남만왕', 'rebelLeader', 168, 1140, 280, 100, 47)
];
const fiftyFourthBattleAllyPositions: Record<string, { x: number; y: number }> = {
'guan-yu': { x: 17, y: 69 },
'zhang-fei': { x: 22, y: 71 },
'jian-yong': { x: 13, y: 76 },
'mi-zhu': { x: 15, y: 74 },
'sun-qian': { x: 19, y: 78 },
'zhao-yun': { x: 27, y: 66 },
'zhuge-liang': { x: 16, y: 66 },
'ma-liang': { x: 20, y: 63 },
'yi-ji': { x: 13, y: 81 },
'gong-zhi': { x: 23, y: 79 },
'huang-zhong': { x: 30, y: 67 },
'wei-yan': { x: 35, y: 71 },
'pang-tong': { x: 18, y: 60 },
'fa-zheng': { x: 24, y: 61 },
'wu-yi': { x: 37, y: 76 },
'yan-yan': { x: 40, y: 69 },
'li-yan': { x: 29, y: 59 },
'huang-quan': { x: 13, y: 64 },
'ma-chao': { x: 42, y: 66 },
'ma-dai': { x: 45, y: 71 },
'wang-ping': { x: 32, y: 62 }
};
export const fiftyFourthBattleUnits: UnitData[] = [
...[
...firstBattleUnits.filter((unit) => unit.faction === 'ally' && unit.id !== 'liu-bei'),
...xuzhouRecruitUnits,
...caoBreakRecruitUnits,
...liuBiaoRecruitUnits,
...zhugeRecruitUnits,
...jingzhouRecruitUnits,
...guiyangRecruitUnits,
...wulingRecruitUnits,
...changshaRecruitUnits,
...yizhouRecruitUnits,
...fuPassRecruitUnits,
...luoCastleRecruitUnits,
...luoCastleProperRecruitUnits,
...chengduPressureRecruitUnits,
...chengduSurrenderRecruitUnits,
...hanzhongOpeningRecruitUnits,
...hanzhongScoutRecruitUnits,
...hanzhongMainRecruitUnits
].map((unit) => placeScenarioUnit(unit, fiftyFourthBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
createThirtyFirstEnemyUnit('menghuo-final-scout-a', '남만 최후 척후', '서쪽 회의장 정찰', 'bandit', 166, 748, 222, 50, 42),
createThirtyFirstEnemyUnit('menghuo-final-scout-b', '남만 최후 척후', '물길 정찰', 'bandit', 166, 748, 222, 66, 40),
createThirtyFirstEnemyUnit('menghuo-final-scout-c', '남만 최후 척후', '마을 감시', 'bandit', 167, 756, 224, 82, 57),
createThirtyFirstEnemyUnit('menghuo-final-scout-d', '남만 최후 척후', '동쪽 산문 감시', 'bandit', 167, 756, 224, 98, 72),
createThirtyFirstEnemyUnit('menghuo-final-scout-e', '남만 최후 척후', '북쪽 절벽 감시', 'bandit', 168, 764, 226, 59, 30),
createThirtyFirstEnemyUnit('menghuo-final-scout-f', '남만 최후 척후', '항복로 감시', 'bandit', 168, 764, 226, 74, 78),
createThirtyFirstEnemyUnit('menghuo-final-vine-a', '최후 등갑병', '회의장 등갑병', 'yellowTurban', 167, 1044, 260, 56, 51),
createThirtyFirstEnemyUnit('menghuo-final-vine-b', '최후 등갑병', '불씨 등갑병', 'yellowTurban', 168, 1056, 262, 69, 54),
createThirtyFirstEnemyUnit('menghuo-final-vine-c', '최후 등갑병', '마을 등갑병', 'yellowTurban', 168, 1056, 262, 82, 64),
createThirtyFirstEnemyUnit('menghuo-final-vine-d', '최후 등갑병', '본영 등갑병', 'yellowTurban', 169, 1068, 264, 94, 56),
createThirtyFirstEnemyUnit('menghuo-final-vine-e', '최후 등갑병', '후위 등갑병', 'yellowTurban', 169, 1068, 264, 102, 48),
createThirtyFirstEnemyUnit('menghuo-final-vine-f', '최후 등갑병', '산문 등갑병', 'yellowTurban', 170, 1080, 266, 90, 37),
createThirtyFirstEnemyUnit('menghuo-final-archer-a', '남만 회의장 궁병', '독천 궁병', 'archer', 166, 800, 234, 52, 34),
createThirtyFirstEnemyUnit('menghuo-final-archer-b', '남만 회의장 궁병', '절벽 궁병', 'archer', 167, 808, 236, 68, 32),
createThirtyFirstEnemyUnit('menghuo-final-archer-c', '남만 회의장 궁병', '마을 궁병', 'archer', 167, 808, 236, 80, 50),
createThirtyFirstEnemyUnit('menghuo-final-archer-d', '남만 회의장 궁병', '물길 궁병', 'archer', 168, 816, 238, 92, 68),
createThirtyFirstEnemyUnit('menghuo-final-archer-e', '남만 회의장 궁병', '본영 궁병', 'archer', 168, 816, 238, 101, 61),
createThirtyFirstEnemyUnit('menghuo-final-beast-a', '남만 최후 맹수대', '강가 맹수대', 'cavalry', 168, 1060, 264, 62, 44),
createThirtyFirstEnemyUnit('menghuo-final-beast-b', '남만 최후 맹수대', '물길 맹수대', 'cavalry', 168, 1060, 264, 75, 46),
createThirtyFirstEnemyUnit('menghuo-final-beast-c', '남만 최후 맹수대', '산채 맹수대', 'cavalry', 169, 1072, 266, 87, 59),
createThirtyFirstEnemyUnit('menghuo-final-beast-d', '남만 최후 맹수대', '추격 맹수대', 'cavalry', 169, 1072, 266, 99, 65),
createThirtyFirstEnemyUnit('menghuo-final-beast-e', '남만 최후 맹수대', '항복로 차단대', 'cavalry', 170, 1084, 268, 73, 74),
createThirtyFirstEnemyUnit('menghuo-final-ambusher-a', '최후 매복병', '회의장 매복병', 'bandit', 167, 768, 228, 43, 57),
createThirtyFirstEnemyUnit('menghuo-final-ambusher-b', '최후 매복병', '숲속 매복병', 'bandit', 168, 776, 230, 59, 60),
createThirtyFirstEnemyUnit('menghuo-final-ambusher-c', '최후 매복병', '강가 매복병', 'bandit', 168, 776, 230, 72, 73),
createThirtyFirstEnemyUnit('menghuo-final-ambusher-d', '최후 매복병', '후방 차단대', 'bandit', 169, 784, 232, 92, 80),
createThirtyFirstEnemyUnit('menghuo-final-ambusher-e', '최후 매복병', '서쪽 차단대', 'bandit', 169, 784, 232, 38, 70),
createThirtyFirstEnemyUnit('menghuo-final-ambusher-f', '최후 매복병', '항복로 감시대', 'bandit', 170, 792, 234, 79, 78),
createThirtyFirstEnemyUnit('menghuo-final-guard-a', '맹획 최후 친위', '포로장 친위대', 'yellowTurban', 170, 1096, 270, 76, 48),
createThirtyFirstEnemyUnit('menghuo-final-guard-b', '맹획 최후 친위', '산채 친위대', 'yellowTurban', 170, 1096, 270, 89, 54),
createThirtyFirstEnemyUnit('menghuo-final-guard-c', '맹획 최후 친위', '본영 친위대', 'yellowTurban', 171, 1108, 272, 100, 51),
createThirtyFirstEnemyUnit('menghuo-final-guard-d', '맹획 최후 친위', '회의장 친위대', 'yellowTurban', 171, 1108, 272, 95, 41),
createThirtyFirstEnemyUnit('menghuo-final-shaman-a', '마지막 독천 제사장', '물길 제사장', 'strategist', 169, 848, 246, 68, 42),
createThirtyFirstEnemyUnit('menghuo-final-shaman-b', '마지막 독천 제사장', '강가 제사장', 'strategist', 170, 856, 248, 83, 55),
createThirtyFirstEnemyUnit('menghuo-final-shaman-c', '마지막 독천 제사장', '산채 제사장', 'strategist', 170, 856, 248, 94, 44),
createThirtyFirstEnemyUnit('menghuo-final-shaman-d', '마지막 독천 제사장', '회의장 제사장', 'strategist', 171, 864, 250, 88, 63),
createThirtyFirstEnemyUnit('menghuo-final-officer-zhurong', '축융 부인', '남중 화전장', 'rebelLeader', 172, 1138, 280, 86, 71),
createThirtyFirstEnemyUnit('menghuo-final-officer-mulu', '목록대왕', '맹수대왕', 'rebelLeader', 172, 1138, 280, 92, 43),
createThirtyFirstEnemyUnit('menghuo-final-officer-duosi', '타사대왕', '독천 호족장', 'rebelLeader', 171, 1118, 274, 76, 66),
createThirtyFirstEnemyUnit('menghuo-final-officer-mengyou', '맹우', '맹획의 아우', 'rebelLeader', 171, 1118, 274, 72, 58),
createThirtyFirstEnemyUnit('menghuo-final-officer-dailai', '대래동주', '깊은 산채 호족', 'rebelLeader', 171, 1118, 274, 85, 73),
createThirtyFirstEnemyUnit('menghuo-final-officer-ahuinan', '아회남', '흔들린 호족 대표', 'rebelLeader', 170, 1098, 270, 74, 70),
createThirtyFirstEnemyUnit('menghuo-final-leader', '맹획', '남만왕', 'rebelLeader', 174, 1180, 288, 102, 49)
];
export const firstBattleBonds: BattleBond[] = [
{
id: 'liu-bei__guan-yu',
@@ -17504,6 +17653,35 @@ export const mengHuoSixthCaptureBonds: BattleBond[] = [
export const fiftyThirdBattleBonds: BattleBond[] = [...fiftySecondBattleBonds, ...mengHuoSixthCaptureBonds].map(
cloneBattleBondForScenario
);
export const mengHuoFinalCaptureBonds: BattleBond[] = [
{
id: 'zhuge-liang__huang-quan_final-capture',
unitIds: ['zhuge-liang', 'huang-quan'],
title: '일곱 번의 약속',
level: 60,
exp: 0,
description: '제갈량의 마지막 회유와 황권의 약속 장부는 맹획의 항복이 굴욕이 아니라 남중의 새 질서가 되게 한다.'
},
{
id: 'ma-liang__wang-ping_final-capture',
unitIds: ['ma-liang', 'wang-ping'],
title: '마을과 회의장',
level: 56,
exp: 0,
description: '마량의 말과 왕평의 길 표식은 촌장들이 보는 앞에서도 촉한군의 군율이 흔들리지 않게 한다.'
},
{
id: 'zhao-yun__ma-dai_final-capture',
unitIds: ['zhao-yun', 'ma-dai'],
title: '멈추는 추격',
level: 50,
exp: 0,
description: '조운과 마대는 빠른 기동으로 잔당을 끊되, 항복로를 밟지 않는 절제의 선을 함께 지킨다.'
}
];
export const fiftyFourthBattleBonds: BattleBond[] = [...fiftyThirdBattleBonds, ...mengHuoFinalCaptureBonds].map(
cloneBattleBondForScenario
);
function createEighthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 22 }, (_, y) =>
@@ -20292,6 +20470,81 @@ function createFiftyThirdBattleTerrain(): TerrainType[][] {
);
}
function createFiftyFourthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 90 }, (_, y) =>
Array.from({ length: 108 }, (_, x): TerrainType => {
if (
(x >= 0 && x <= 107 && y >= 82) ||
(x >= 69 && x <= 75 && y >= 0 && y <= 84) ||
(x >= 7 && x <= 36 && y >= 73 && y <= 89) ||
(x >= 82 && x <= 100 && y >= 64 && y <= 82)
) {
return 'river';
}
if (
(x >= 10 && x <= 103 && y >= 69 && y <= 75) ||
(x >= 30 && x <= 80 && y >= 46 && y <= 54) ||
(x >= 75 && x <= 103 && y >= 38 && y <= 44) ||
(x >= 41 && x <= 50 && y >= 43 && y <= 78) ||
(x >= 82 && x <= 88 && y >= 52 && y <= 86) ||
(x >= 18 && x <= 56 && y >= 61 && y <= 65) ||
(x >= 54 && x <= 69 && y >= 55 && y <= 61) ||
(x >= 60 && x <= 76 && y >= 45 && y <= 53)
) {
return 'road';
}
if (
(x >= 18 && x <= 78 && y >= 23 && y <= 80) ||
(x >= 58 && x <= 102 && y >= 31 && y <= 76) ||
(x >= 2 && x <= 43 && y >= 54 && y <= 80) ||
(x >= 84 && x <= 107 && y >= 45 && y <= 79)
) {
return 'forest';
}
if (
(x >= 0 && x <= 34 && y <= 42) ||
(x >= 36 && x <= 74 && y <= 32) ||
(x >= 77 && x <= 107 && y <= 41) ||
(x >= 92 && x <= 107 && y >= 37 && y <= 61)
) {
return 'hill';
}
if (
(x <= 2 && y <= 80) ||
(x >= 105 && y <= 76) ||
(x >= 33 && x <= 39 && y >= 0 && y <= 44) ||
(x >= 74 && x <= 80 && y >= 0 && y <= 38) ||
(x >= 48 && x <= 54 && y >= 70 && y <= 89)
) {
return 'cliff';
}
if (
(x >= 13 && x <= 30 && y >= 65 && y <= 77) ||
(x >= 42 && x <= 60 && y >= 48 && y <= 62) ||
(x >= 81 && x <= 97 && y >= 54 && y <= 67) ||
(x >= 86 && x <= 100 && y >= 36 && y <= 49)
) {
return 'village';
}
if (
(x >= 26 && x <= 42 && y >= 57 && y <= 68) ||
(x >= 69 && x <= 84 && y >= 62 && y <= 74) ||
(x >= 87 && x <= 102 && y >= 43 && y <= 57)
) {
return 'camp';
}
if (
(x >= 51 && x <= 69 && y >= 35 && y <= 46) ||
(x >= 78 && x <= 99 && y >= 35 && y <= 50) ||
(x >= 90 && x <= 103 && y >= 48 && y <= 60)
) {
return 'fort';
}
return 'plain';
})
);
}
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
return {
...cloneUnitForScenario(unit),