Add Qishan renewed offensive chapter

This commit is contained in:
2026-06-24 15:00:24 +09:00
parent 305ead1777
commit 44c017ab6c
16 changed files with 892 additions and 98 deletions

View File

@@ -11,6 +11,7 @@ import fiftyEighthBattleMapUrl from '../../assets/images/battle/fifty-eighth-bat
import fiftyNinthBattleMapUrl from '../../assets/images/battle/fifty-ninth-battle-map.svg';
import sixtiethBattleMapUrl from '../../assets/images/battle/sixtieth-battle-map.svg';
import sixtyFirstBattleMapUrl from '../../assets/images/battle/sixty-first-battle-map.svg';
import sixtySecondBattleMapUrl from '../../assets/images/battle/sixty-second-battle-map.svg';
import fiftySeventhBattleMapUrl from '../../assets/images/battle/fifty-seventh-battle-map.svg';
import fiftySixthBattleMapUrl from '../../assets/images/battle/fifty-sixth-battle-map.svg';
import fifthBattleMapUrl from '../../assets/images/battle/fifth-battle-map.svg';
@@ -120,6 +121,7 @@ export const battleMapAssets: Record<string, string> = {
'battle-map-fifty-ninth': fiftyNinthBattleMapUrl,
'battle-map-sixtieth': sixtiethBattleMapUrl,
'battle-map-sixty-first': sixtyFirstBattleMapUrl,
'battle-map-sixty-second': sixtySecondBattleMapUrl,
'battle-map-fifty-seventh': fiftySeventhBattleMapUrl,
'battle-map-fifty-sixth': fiftySixthBattleMapUrl
};

View File

@@ -34,15 +34,15 @@ export type UnitClassDefinition = {
};
export const terrainRules: Record<TerrainType, TerrainRule> = {
plain: { label: '평지', color: 0x6d8056, alpha: 0.01, moveCost: 1, defenseBonus: 0 },
road: { label: '길', color: 0xb08b55, alpha: 0.015, moveCost: 1, defenseBonus: 0 },
forest: { label: '숲', color: 0x355f42, alpha: 0.025, moveCost: 2, defenseBonus: 10 },
hill: { label: '언덕', color: 0x82724e, alpha: 0.025, moveCost: 2, defenseBonus: 8 },
village: { label: '마을', color: 0x8d6f4b, alpha: 0.03, moveCost: 1, defenseBonus: 12, recoveryHp: 8, moraleBonus: 3 },
fort: { label: '요새', color: 0x735c46, alpha: 0.035, moveCost: 1, defenseBonus: 18, recoveryHp: 10, moraleBonus: 4 },
camp: { label: '진영', color: 0x4f6a73, alpha: 0.03, moveCost: 1, defenseBonus: 15, recoveryHp: 6, moraleBonus: 4 },
river: { label: '강', color: 0x2c6687, alpha: 0.035, moveCost: 99, defenseBonus: -4, passable: false },
cliff: { label: '절벽', color: 0x39342e, alpha: 0.045, moveCost: 99, defenseBonus: 20, passable: false }
plain: { label: '평지', color: 0x6d8056, alpha: 0.18, moveCost: 1, defenseBonus: 0 },
road: { label: '길', color: 0xb08b55, alpha: 0.22, moveCost: 1, defenseBonus: 0 },
forest: { label: '숲', color: 0x355f42, alpha: 0.24, moveCost: 2, defenseBonus: 10 },
hill: { label: '언덕', color: 0x82724e, alpha: 0.24, moveCost: 2, defenseBonus: 8 },
village: { label: '마을', color: 0x8d6f4b, alpha: 0.28, moveCost: 1, defenseBonus: 12, recoveryHp: 8, moraleBonus: 3 },
fort: { label: '요새', color: 0x735c46, alpha: 0.3, moveCost: 1, defenseBonus: 18, recoveryHp: 10, moraleBonus: 4 },
camp: { label: '진영', color: 0x4f6a73, alpha: 0.3, moveCost: 1, defenseBonus: 15, recoveryHp: 6, moraleBonus: 4 },
river: { label: '강', color: 0x2c6687, alpha: 0.32, moveCost: 99, defenseBonus: -4, passable: false },
cliff: { label: '절벽', color: 0x39342e, alpha: 0.34, moveCost: 99, defenseBonus: 20, passable: false }
};
export const unitClasses: Record<UnitClassKey, UnitClassDefinition> = {

View File

@@ -43,6 +43,10 @@ import {
sixtyFirstBattleMap,
sixtyFirstBattleUnits,
sixtyFirstBattleVictoryPages,
sixtySecondBattleBonds,
sixtySecondBattleMap,
sixtySecondBattleUnits,
sixtySecondBattleVictoryPages,
fiftySeventhBattleBonds,
fiftySeventhBattleMap,
fiftySeventhBattleUnits,
@@ -310,7 +314,8 @@ export type BattleScenarioId =
| 'fifty-eighth-battle-qishan-retreat'
| 'fifty-ninth-battle-chencang-siege'
| 'sixtieth-battle-wudu-yinping'
| 'sixty-first-battle-hanzhong-rain-defense';
| 'sixty-first-battle-hanzhong-rain-defense'
| 'sixty-second-battle-qishan-renewed-offensive';
export type BattleObjectiveKind = 'defeat-leader' | 'pacify-leader' | 'keep-unit-alive' | 'secure-terrain' | 'quick-victory';
@@ -4119,6 +4124,73 @@ export const sixtyFirstBattleScenario: BattleScenarioDefinition = {
nextCampScene: 'CampScene'
};
export const sixtySecondBattleScenario: BattleScenarioDefinition = {
id: 'sixty-second-battle-qishan-renewed-offensive',
title: '기산 재출정전',
victoryConditionLabel: '노성 지연선 돌파와 기산 보급로 전진',
defeatConditionLabel: '제갈량 퇴각 또는 회수선 붕괴',
openingObjectiveLines: [
'한중 우로 방어 후 촉한군은 지켜 낸 무도·음평 보급선을 다시 북쪽 공격로로 바꾸려 합니다. 노성 앞의 위군 지연선은 사마의가 시간을 벌기 위해 세운 첫 벽입니다.',
'강유와 위연은 앞길을 열고, 조운과 왕평은 깊이 들어간 부대가 돌아올 표식을 남겨야 합니다. 황권과 마량은 군량과 민심을 함께 보아 공세가 약탈처럼 보이지 않게 해야 합니다.',
'사마의의 본영, 장합의 추격대, 곽회의 측면 차단을 동시에 끊으십시오. 기산 수레 길과 전방 마을, 회수 진영을 지켜야 다음 북벌이 더 깊이 이어집니다.'
],
map: sixtySecondBattleMap,
units: sixtySecondBattleUnits,
bonds: sixtySecondBattleBonds,
mapTextureKey: 'battle-map-sixty-second',
leaderUnitId: 'northern-eighth-leader-sima-yi',
quickVictoryTurnLimit: 124,
baseVictoryGold: 19240,
objectives: [
{
id: 'press-sima-yi-delay-line',
kind: 'defeat-leader',
label: '사마의 지연선 돌파',
rewardGold: 13600,
unitId: 'northern-eighth-leader-sima-yi'
},
{
id: 'check-zhang-he-pursuit',
kind: 'defeat-leader',
label: '장합 추격대 저지',
rewardGold: 8200,
unitId: 'northern-eighth-officer-zhang-he'
},
{
id: 'break-guo-huai-flank',
kind: 'defeat-leader',
label: '곽회 측면 차단 해제',
rewardGold: 7600,
unitId: 'northern-eighth-officer-guo-huai'
},
{
id: 'qishan-supply-camps',
kind: 'secure-terrain',
label: '기산 보급 진영 유지',
rewardGold: 6400,
terrain: 'camp'
},
{
id: 'lucheng-front-villages',
kind: 'secure-terrain',
label: '노성 앞 마을 안심',
rewardGold: 6200,
terrain: 'village'
},
{
id: 'quick',
kind: 'quick-victory',
label: '124턴 이내 지연선 돌파',
rewardGold: 4180,
maxTurn: 124
}
],
defeatConditions: [{ kind: 'unit-defeated', unitId: 'zhuge-liang' }],
itemRewards: ['금 126', '상처약 98', '군주 48', '기산 재출정 장부 1', '노성 지연선 기록 1'],
victoryPages: sixtySecondBattleVictoryPages,
nextCampScene: 'CampScene'
};
export const defaultBattleScenarioId: BattleScenarioId = firstBattleScenario.id;
export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition> = {
@@ -4182,7 +4254,8 @@ export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition>
'fifty-eighth-battle-qishan-retreat': fiftyEighthBattleScenario,
'fifty-ninth-battle-chencang-siege': fiftyNinthBattleScenario,
'sixtieth-battle-wudu-yinping': sixtiethBattleScenario,
'sixty-first-battle-hanzhong-rain-defense': sixtyFirstBattleScenario
'sixty-first-battle-hanzhong-rain-defense': sixtyFirstBattleScenario,
'sixty-second-battle-qishan-renewed-offensive': sixtySecondBattleScenario
};
export const defaultBattleScenario = battleScenarios[defaultBattleScenarioId];

View File

@@ -16,6 +16,7 @@ import {
fiftySeventhBattleScenario,
sixtiethBattleScenario,
sixtyFirstBattleScenario,
sixtySecondBattleScenario,
fortiethBattleScenario,
fortyFirstBattleScenario,
fortySecondBattleScenario,
@@ -81,6 +82,8 @@ import {
sixtiethBattleVictoryPages,
sixtyFirstBattleIntroPages,
sixtyFirstBattleVictoryPages,
sixtySecondBattleIntroPages,
sixtySecondBattleVictoryPages,
fiftyFirstBattleIntroPages,
fiftyFirstBattleVictoryPages,
fiftySecondBattleIntroPages,
@@ -897,13 +900,24 @@ const sortieFlows: Record<string, SortieFlow> = {
},
[sixtyFirstBattleScenario.id]: {
afterBattleId: sixtyFirstBattleScenario.id,
eyebrow: '다음 장 준비',
title: '한중 우로 방어 이후',
eyebrow: '다음 장',
title: sixtySecondBattleScenario.title,
description:
'조진의 역공을 막아 냈지만 한중 북문, 무도, 음평을 함께 지키는 부담은 그대로 남았습니다. 다음 북방 계산에서는 지켜 낸 길을 다시 공격로로 바꾸는 일과 후방에 남길 장수를 함께 정해야 합니다.',
rewardHint: '다음 목표: 한중 방어선 재정비 / 다음 북벌 공격로 검토',
pages: sixtyFirstBattleVictoryPages,
unavailableNotice: '한중 우로 방어 이후의 다음 북벌 전투는 이어지는 작업에서 확장합니다. 현재는 조진 역공 방어, 한중 창고 유지, 무도·음평 보급 책임을 확인할 수 있습니다.'
'조진의 역공을 막아 낸 뒤, 지켜 낸 한중 북문 무도·음평 수레 길을 다시 기산 방면 공격로로 바꿉니다. 사마의의 지연전, 장합의 추격, 곽회의 차단을 상대하려면 7명 안에서 전진과 회수, 보급과 민심을 다시 나누어야 합니다.',
rewardHint: `예상 보상: ${sixtySecondBattleScenario.title} 개방 / 기산 재출정 장부`,
nextBattleId: sixtySecondBattleScenario.id,
campaignStep: 'sixty-second-battle',
pages: [...sixtyFirstBattleVictoryPages, ...sixtySecondBattleIntroPages]
},
[sixtySecondBattleScenario.id]: {
afterBattleId: sixtySecondBattleScenario.id,
eyebrow: '다음 장 준비',
title: '기산 재출정 이후',
description:
'사마의의 지연선을 밀어 냈지만 북방 전선은 더 깊어졌습니다. 다음 계산에서는 노성 앞에서 확보한 길을 어디까지 밀고, 누가 돌아올 표식과 군량 장부를 맡을지 다시 정해야 합니다.',
rewardHint: '다음 목표: 노성 전선 재정비 / 더 깊은 북벌 공격로 검토',
pages: sixtySecondBattleVictoryPages,
unavailableNotice: '기산 재출정 이후의 다음 북벌 전투는 이어지는 작업에서 확장합니다. 현재는 사마의 지연선 돌파, 회수선 표식, 기산 보급로 전진을 확인할 수 있습니다.'
},
'northern-campaign-prep-camp-legacy': {
afterBattleId: fiftyFourthBattleScenario.id,

View File

@@ -3907,6 +3907,59 @@ export const sixtyFirstBattleVictoryPages: StoryPage[] = [
}
];
export const sixtySecondBattleIntroPages: StoryPage[] = [
{
id: 'sixty-second-renewed-qishan-council',
bgm: 'story-dark',
chapter: '기산 재출정',
background: 'story-qishan-renewed',
speaker: '제갈량',
text: '한중 북문의 비가 그치자 지켜 낸 길은 다시 북쪽을 향한 길이 되었습니다. 이번에는 노성 앞의 위군 지연선을 흔들어, 무도와 음평에서 이어진 보급로를 공격의 숨으로 바꾸겠습니다.'
},
{
id: 'sixty-second-sima-yi-delay-line',
bgm: 'battle-prep',
chapter: '사마의의 지연전',
background: 'story-qishan-renewed',
speaker: '강유',
text: '사마의는 성을 내주지 않고 길을 늦추려 합니다. 장합은 기병으로 앞선을 찌르고, 곽회는 돌아갈 길을 끊으려 할 것입니다. 빠른 돌파와 돌아올 표식이 함께 움직여야 합니다.'
},
{
id: 'sixty-second-supply-into-offense',
bgm: 'story-dark',
chapter: '보급을 공세로',
background: 'story-qishan-renewed',
speaker: '황권',
text: '지난 전투에서 지킨 장부를 이번에는 밀어 올릴 차례입니다. 이엄을 후방에 남길지, 마대의 측면 기동을 넣을지, 혹은 마량의 민심 문서를 앞세울지 출전 명단에서 먼저 정해야 합니다.'
}
];
export const sixtySecondBattleVictoryPages: StoryPage[] = [
{
id: 'sixty-second-victory-lucheng-pressure',
bgm: 'militia-theme',
chapter: '노성 앞을 누르다',
background: 'story-qishan-renewed',
text: '촉한군은 노성 앞의 지연선을 밀어 냈고, 사마의의 봉화와 곽회의 차단로가 동시에 이어지는 것을 막았습니다. 기산의 길은 다시 북벌군의 군문 앞에 열렸습니다.'
},
{
id: 'sixty-second-victory-return-markers',
bgm: 'story-dark',
chapter: '돌아올 표식',
background: 'story-qishan-renewed',
speaker: '왕평',
text: '앞으로 나아간 길마다 돌아올 표식이 함께 세워졌습니다. 강유와 위연이 열어 둔 길을 조운이 회수선으로 묶고, 황권과 마량은 그 길이 백성의 부담이 되지 않게 장부를 고쳤습니다.'
},
{
id: 'sixty-second-next-northern-plan',
bgm: 'story-dark',
chapter: '더 깊은 북방 계산',
background: 'story-qishan-renewed',
speaker: '제갈량',
text: '사마의는 한 번 물러났으나 전쟁은 아직 짧아지지 않았습니다. 다음 북벌에서는 이긴 길을 지키는 사람과 더 깊이 들어갈 사람을 다시 나누어야 합니다.'
}
];
export const firstBattleMap: BattleMap = {
width: 20,
height: 18,
@@ -4418,6 +4471,12 @@ export const sixtyFirstBattleMap: BattleMap = {
terrain: createSixtyFirstBattleTerrain()
};
export const sixtySecondBattleMap: BattleMap = {
width: 124,
height: 106,
terrain: createSixtySecondBattleTerrain()
};
export const firstBattleUnits: UnitData[] = [
{
id: 'liu-bei',
@@ -17726,6 +17785,97 @@ export const sixtyFirstBattleUnits: UnitData[] = [
createThirtyFirstEnemyUnit('northern-seventh-leader-cao-zhen', '조진', '위군 역공 총대장', 'rebelLeader', 213, 1660, 392, 114, 38)
];
const sixtySecondBattleAllyPositions: Record<string, { x: number; y: number }> = {
'guan-yu': { x: 15, y: 94 },
'zhang-fei': { x: 22, y: 96 },
'jian-yong': { x: 8, y: 101 },
'mi-zhu': { x: 13, y: 102 },
'sun-qian': { x: 18, y: 102 },
'zhao-yun': { x: 30, y: 93 },
'zhuge-liang': { x: 18, y: 89 },
'ma-liang': { x: 24, y: 86 },
'yi-ji': { x: 8, y: 103 },
'gong-zhi': { x: 28, y: 101 },
'huang-zhong': { x: 38, y: 90 },
'wei-yan': { x: 45, y: 91 },
'pang-tong': { x: 15, y: 85 },
'fa-zheng': { x: 28, y: 84 },
'wu-yi': { x: 47, y: 99 },
'yan-yan': { x: 51, y: 92 },
'li-yan': { x: 34, y: 83 },
'huang-quan': { x: 12, y: 88 },
'ma-chao': { x: 54, y: 88 },
'ma-dai': { x: 59, y: 95 },
'wang-ping': { x: 41, y: 83 },
'jiang-wei': { x: 35, y: 86 }
};
export const sixtySecondBattleUnits: 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,
...jiangWeiRecruitUnits
].map((unit) => placeScenarioUnit(unit, sixtySecondBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
createThirtyFirstEnemyUnit('northern-eighth-scout-a', '노성 서로 척후병', '서쪽 고개 정찰', 'bandit', 214, 1060, 292, 52, 84),
createThirtyFirstEnemyUnit('northern-eighth-scout-b', '기산 보급로 척후병', '보급로 정찰', 'bandit', 214, 1060, 292, 64, 81),
createThirtyFirstEnemyUnit('northern-eighth-scout-c', '노성 앞 척후병', '중앙 고개 정찰', 'bandit', 215, 1072, 294, 78, 74),
createThirtyFirstEnemyUnit('northern-eighth-scout-d', '동쪽 물목 척후병', '동쪽 물목 정찰', 'bandit', 215, 1072, 294, 92, 66),
createThirtyFirstEnemyUnit('northern-eighth-scout-e', '사마의 전초 척후병', '본영 정찰', 'bandit', 216, 1084, 296, 108, 50),
createThirtyFirstEnemyUnit('northern-eighth-scout-f', '북쪽 봉화 척후병', '봉화대 정찰', 'bandit', 216, 1084, 296, 121, 30),
createThirtyFirstEnemyUnit('northern-eighth-infantry-a', '위군 기산 보병', '서쪽 고개 보병', 'yellowTurban', 215, 1540, 368, 55, 78),
createThirtyFirstEnemyUnit('northern-eighth-infantry-b', '위군 보급로 보병', '보급로 보병', 'yellowTurban', 215, 1540, 368, 68, 74),
createThirtyFirstEnemyUnit('northern-eighth-infantry-c', '위군 노성 보병', '중앙 보병', 'yellowTurban', 216, 1560, 372, 81, 68),
createThirtyFirstEnemyUnit('northern-eighth-infantry-d', '위군 물목 보병', '동쪽 물목 보병', 'yellowTurban', 216, 1560, 372, 94, 61),
createThirtyFirstEnemyUnit('northern-eighth-infantry-e', '위군 지연 보병', '지연선 보병', 'yellowTurban', 217, 1580, 376, 106, 53),
createThirtyFirstEnemyUnit('northern-eighth-infantry-f', '사마의 본영 보병', '본영 보병', 'yellowTurban', 217, 1580, 376, 116, 41),
createThirtyFirstEnemyUnit('northern-eighth-infantry-g', '위군 보급 차단병', '수레길 차단 보병', 'yellowTurban', 218, 1600, 380, 100, 82),
createThirtyFirstEnemyUnit('northern-eighth-infantry-h', '장합 별동 보병', '별동대 보병', 'yellowTurban', 218, 1600, 380, 121, 23),
createThirtyFirstEnemyUnit('northern-eighth-archer-a', '기산 서로 궁병', '서쪽 능선 궁병', 'archer', 215, 1130, 310, 62, 69),
createThirtyFirstEnemyUnit('northern-eighth-archer-b', '보급로 궁병', '보급로 궁병', 'archer', 215, 1130, 310, 74, 64),
createThirtyFirstEnemyUnit('northern-eighth-archer-c', '노성 능선 궁병', '중앙 능선 궁병', 'archer', 216, 1142, 312, 89, 57),
createThirtyFirstEnemyUnit('northern-eighth-archer-d', '동쪽 물목 궁병', '동쪽 물목 궁병', 'archer', 217, 1154, 314, 102, 49),
createThirtyFirstEnemyUnit('northern-eighth-archer-e', '사마의 본영 궁병', '본영 궁병', 'archer', 217, 1154, 314, 114, 37),
createThirtyFirstEnemyUnit('northern-eighth-archer-f', '북쪽 봉화 궁병', '봉화대 궁병', 'archer', 218, 1166, 316, 122, 18),
createThirtyFirstEnemyUnit('northern-eighth-cavalry-a', '장합 추격 기병', '서쪽 추격 기병', 'cavalry', 216, 1600, 388, 53, 72),
createThirtyFirstEnemyUnit('northern-eighth-cavalry-b', '장합 추격 기병', '보급로 추격 기병', 'cavalry', 216, 1600, 388, 66, 69),
createThirtyFirstEnemyUnit('northern-eighth-cavalry-c', '장합 추격 기병', '중앙 추격 기병', 'cavalry', 217, 1622, 392, 82, 62),
createThirtyFirstEnemyUnit('northern-eighth-cavalry-d', '곽회 차단 기병', '동쪽 차단 기병', 'cavalry', 218, 1644, 396, 96, 55),
createThirtyFirstEnemyUnit('northern-eighth-cavalry-e', '사마의 예비 기병', '본영 예비 기병', 'cavalry', 218, 1644, 396, 110, 47),
createThirtyFirstEnemyUnit('northern-eighth-cavalry-f', '북쪽 봉화 기병', '봉화 차단 기병', 'cavalry', 219, 1666, 400, 121, 28),
createThirtyFirstEnemyUnit('northern-eighth-ridge-guard-a', '기산 서로 수비대', '서쪽 고개 수비', 'yellowTurban', 216, 1560, 374, 48, 58),
createThirtyFirstEnemyUnit('northern-eighth-ridge-guard-b', '보급로 수비대', '보급로 수비', 'yellowTurban', 216, 1560, 374, 64, 53),
createThirtyFirstEnemyUnit('northern-eighth-ridge-guard-c', '노성 앞 수비대', '중앙 고개 수비', 'yellowTurban', 217, 1580, 378, 80, 45),
createThirtyFirstEnemyUnit('northern-eighth-ridge-guard-d', '동쪽 물목 수비대', '동쪽 물목 수비', 'yellowTurban', 217, 1580, 378, 96, 38),
createThirtyFirstEnemyUnit('northern-eighth-ridge-guard-e', '사마의 본영 수비대', '본영 수비', 'yellowTurban', 218, 1600, 382, 110, 30),
createThirtyFirstEnemyUnit('northern-eighth-ridge-guard-f', '북쪽 봉화 수비대', '북쪽 봉화 수비', 'yellowTurban', 218, 1600, 382, 122, 14),
createThirtyFirstEnemyUnit('northern-eighth-strategist-a', '위군 지연 군리', '지연 장부 군리', 'strategist', 216, 1200, 328, 78, 60),
createThirtyFirstEnemyUnit('northern-eighth-strategist-b', '위군 보급 군리', '보급 차단 군리', 'strategist', 217, 1212, 330, 92, 51),
createThirtyFirstEnemyUnit('northern-eighth-strategist-c', '사마의 본영 군리', '본영 장부 군리', 'strategist', 217, 1212, 330, 106, 42),
createThirtyFirstEnemyUnit('northern-eighth-strategist-d', '곽회 차단 군리', '차단로 장부 군리', 'strategist', 218, 1224, 332, 112, 70),
createThirtyFirstEnemyUnit('northern-eighth-officer-lucheng', '노성 전초장', '노성 압박 장수', 'rebelLeader', 220, 1660, 398, 90, 76),
createThirtyFirstEnemyUnit('northern-eighth-officer-supply', '보급 차단장', '기산 보급 차단 장수', 'rebelLeader', 220, 1660, 398, 103, 64),
createThirtyFirstEnemyUnit('northern-eighth-officer-guo-huai', '곽회', '측면 차단 장수', 'rebelLeader', 219, 1640, 394, 112, 74),
createThirtyFirstEnemyUnit('northern-eighth-officer-relay', '봉화 연락장', '노성 봉화 장수', 'rebelLeader', 219, 1640, 394, 108, 58),
createThirtyFirstEnemyUnit('northern-eighth-officer-zhang-he', '장합', '추격 별동대장', 'rebelLeader', 221, 1700, 404, 118, 27),
createThirtyFirstEnemyUnit('northern-eighth-leader-sima-yi', '사마의', '위군 지연 총지휘', 'rebelLeader', 222, 1740, 410, 116, 40)
];
export const firstBattleBonds: BattleBond[] = [
{
id: 'liu-bei__guan-yu',
@@ -19083,6 +19233,46 @@ export const sixtyFirstBattleBonds: BattleBond[] = [
...northernSeventhBattleBonds
].map(cloneBattleBondForScenario);
export const northernEighthBattleBonds: BattleBond[] = [
{
id: 'jiang-wei__wei-yan_northern-eighth',
unitIds: ['jiang-wei', 'wei-yan'],
title: '앞길과 돌파',
level: 52,
exp: 0,
description: '강유의 길눈과 위연의 돌파 판단이 맞물리면, 노성 앞 지연선을 빠르게 흔들되 돌아올 길을 잃지 않을 수 있습니다.'
},
{
id: 'zhao-yun__wang-ping_northern-eighth',
unitIds: ['zhao-yun', 'wang-ping'],
title: '회수선의 표식',
level: 54,
exp: 0,
description: '조운의 예비대 회수와 왕평의 산길 표식이 이어지면, 깊이 들어간 부대도 사마의의 지연전 속에서 제때 돌아올 수 있습니다.'
},
{
id: 'huang-quan__ma-liang_northern-eighth',
unitIds: ['huang-quan', 'ma-liang'],
title: '장부와 고시문',
level: 48,
exp: 0,
description: '황권의 군량 장부와 마량의 민심 문서가 함께 움직이면, 공격로가 백성의 부담으로만 남지 않게 조정할 수 있습니다.'
},
{
id: 'li-yan__ma-dai_northern-eighth',
unitIds: ['li-yan', 'ma-dai'],
title: '수레와 측면',
level: 44,
exp: 0,
description: '이엄의 운송 기준과 마대의 측면 기동이 맞으면, 보급 수레가 늦어질 때도 기병이 빈틈을 메울 수 있습니다.'
}
];
export const sixtySecondBattleBonds: BattleBond[] = [
...sixtyFirstBattleBonds,
...northernEighthBattleBonds
].map(cloneBattleBondForScenario);
function createEighthBattleTerrain(): TerrainType[][] {
return Array.from({ length: 22 }, (_, y) =>
Array.from({ length: 26 }, (_, x): TerrainType => {
@@ -22479,6 +22669,86 @@ function createSixtyFirstBattleTerrain(): TerrainType[][] {
);
}
function createSixtySecondBattleTerrain(): TerrainType[][] {
return Array.from({ length: 106 }, (_, y) =>
Array.from({ length: 124 }, (_, x): TerrainType => {
if (
(x >= 5 && x <= 12 && y >= 0 && y <= 105) ||
(x >= 64 && x <= 71 && y >= 12 && y <= 105) ||
(x >= 0 && x <= 123 && y >= 98)
) {
return 'river';
}
if (
(x >= 7 && x <= 84 && y >= 90 && y <= 96) ||
(x >= 22 && x <= 103 && y >= 78 && y <= 86) ||
(x >= 48 && x <= 112 && y >= 66 && y <= 76) ||
(x >= 76 && x <= 120 && y >= 52 && y <= 64) ||
(x >= 93 && x <= 123 && y >= 37 && y <= 49) ||
(x >= 30 && x <= 40 && y >= 70 && y <= 100) ||
(x >= 55 && x <= 64 && y >= 56 && y <= 92) ||
(x >= 85 && x <= 95 && y >= 42 && y <= 82) ||
(x >= 99 && x <= 109 && y >= 24 && y <= 68) ||
(x >= 44 && x <= 82 && y >= 82 && y <= 88)
) {
return 'road';
}
if (
(x >= 0 && x <= 38 && y >= 60 && y <= 94) ||
(x >= 42 && x <= 82 && y >= 75 && y <= 97) ||
(x >= 88 && x <= 123 && y >= 69 && y <= 95) ||
(x >= 14 && x <= 42 && y >= 44 && y <= 64) ||
(x >= 98 && x <= 123 && y >= 52 && y <= 74) ||
(x >= 116 && x <= 123 && y >= 26 && y <= 48)
) {
return 'forest';
}
if (
(x >= 0 && x <= 52 && y <= 41) ||
(x >= 46 && x <= 94 && y <= 35) ||
(x >= 86 && x <= 123 && y <= 53) ||
(x >= 98 && x <= 123 && y >= 45 && y <= 74)
) {
return 'hill';
}
if (
(x <= 2 && y <= 96) ||
(x >= 121 && y <= 90) ||
(x >= 39 && x <= 46 && y >= 0 && y <= 66) ||
(x >= 77 && x <= 83 && y >= 0 && y <= 48) ||
(x >= 60 && x <= 68 && y >= 86 && y <= 105)
) {
return 'cliff';
}
if (
(x >= 12 && x <= 30 && y >= 86 && y <= 96) ||
(x >= 43 && x <= 64 && y >= 73 && y <= 85) ||
(x >= 80 && x <= 100 && y >= 59 && y <= 71) ||
(x >= 102 && x <= 120 && y >= 43 && y <= 57)
) {
return 'village';
}
if (
(x >= 20 && x <= 48 && y >= 80 && y <= 94) ||
(x >= 55 && x <= 82 && y >= 67 && y <= 80) ||
(x >= 89 && x <= 114 && y >= 53 && y <= 66) ||
(x >= 99 && x <= 118 && y >= 24 && y <= 39)
) {
return 'camp';
}
if (
(x >= 49 && x <= 70 && y >= 48 && y <= 64) ||
(x >= 73 && x <= 100 && y >= 38 && y <= 53) ||
(x >= 95 && x <= 120 && y >= 47 && y <= 63) ||
(x >= 104 && x <= 123 && y >= 14 && y <= 36)
) {
return 'fort';
}
return 'plain';
})
);
}
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
return {
...cloneUnitForScenario(unit),