Add Jiameng Pass Ma Chao chapter
This commit is contained in:
@@ -481,7 +481,30 @@ const unitTexture: Record<string, string> = {
|
||||
'chengdu-final-ambusher-b': 'unit-rebel',
|
||||
'chengdu-final-ambusher-c': 'unit-rebel',
|
||||
'chengdu-final-officer-a': 'unit-rebel-leader',
|
||||
'chengdu-final-leader-huang-quan': 'unit-rebel-leader'
|
||||
'chengdu-final-leader-huang-quan': 'unit-rebel-leader',
|
||||
'ma-chao': 'unit-rebel-cavalry',
|
||||
'jiameng-scout-a': 'unit-rebel',
|
||||
'jiameng-scout-b': 'unit-rebel',
|
||||
'jiameng-scout-c': 'unit-rebel',
|
||||
'jiameng-infantry-a': 'unit-rebel',
|
||||
'jiameng-infantry-b': 'unit-rebel',
|
||||
'jiameng-infantry-c': 'unit-rebel',
|
||||
'jiameng-infantry-d': 'unit-rebel',
|
||||
'jiameng-archer-a': 'unit-rebel-archer',
|
||||
'jiameng-archer-b': 'unit-rebel-archer',
|
||||
'jiameng-archer-c': 'unit-rebel-archer',
|
||||
'jiameng-cavalry-a': 'unit-rebel-cavalry',
|
||||
'jiameng-cavalry-b': 'unit-rebel-cavalry',
|
||||
'jiameng-cavalry-c': 'unit-rebel-cavalry',
|
||||
'jiameng-cavalry-d': 'unit-rebel-cavalry',
|
||||
'jiameng-guard-a': 'unit-rebel',
|
||||
'jiameng-guard-b': 'unit-rebel',
|
||||
'jiameng-strategist-a': 'unit-rebel-archer',
|
||||
'jiameng-strategist-b': 'unit-rebel-archer',
|
||||
'jiameng-ambusher-a': 'unit-rebel',
|
||||
'jiameng-ambusher-b': 'unit-rebel',
|
||||
'jiameng-officer-pang-de': 'unit-rebel-leader',
|
||||
'jiameng-leader-ma-chao': 'unit-rebel-cavalry'
|
||||
};
|
||||
|
||||
const unitTextureByClass: Partial<Record<UnitClassKey, string>> = {
|
||||
@@ -1423,7 +1446,29 @@ const enemyAiByUnitId: Record<string, EnemyAiBehavior> = {
|
||||
'chengdu-final-ambusher-b': 'aggressive',
|
||||
'chengdu-final-ambusher-c': 'aggressive',
|
||||
'chengdu-final-officer-a': 'guard',
|
||||
'chengdu-final-leader-huang-quan': 'guard'
|
||||
'chengdu-final-leader-huang-quan': 'guard',
|
||||
'jiameng-scout-a': 'aggressive',
|
||||
'jiameng-scout-b': 'aggressive',
|
||||
'jiameng-scout-c': 'aggressive',
|
||||
'jiameng-infantry-a': 'guard',
|
||||
'jiameng-infantry-b': 'guard',
|
||||
'jiameng-infantry-c': 'guard',
|
||||
'jiameng-infantry-d': 'guard',
|
||||
'jiameng-archer-a': 'hold',
|
||||
'jiameng-archer-b': 'hold',
|
||||
'jiameng-archer-c': 'hold',
|
||||
'jiameng-cavalry-a': 'aggressive',
|
||||
'jiameng-cavalry-b': 'aggressive',
|
||||
'jiameng-cavalry-c': 'aggressive',
|
||||
'jiameng-cavalry-d': 'aggressive',
|
||||
'jiameng-guard-a': 'guard',
|
||||
'jiameng-guard-b': 'guard',
|
||||
'jiameng-strategist-a': 'hold',
|
||||
'jiameng-strategist-b': 'hold',
|
||||
'jiameng-ambusher-a': 'aggressive',
|
||||
'jiameng-ambusher-b': 'aggressive',
|
||||
'jiameng-officer-pang-de': 'guard',
|
||||
'jiameng-leader-ma-chao': 'aggressive'
|
||||
};
|
||||
|
||||
const defaultEnemyAiByClass: Partial<Record<UnitClassKey, EnemyAiBehavior>> = {
|
||||
|
||||
@@ -16,6 +16,7 @@ import sixteenthBattleMapUrl from '../../assets/images/battle/sixteenth-battle-m
|
||||
import seventeenthBattleMapUrl from '../../assets/images/battle/seventeenth-battle-map.svg';
|
||||
import tenthBattleMapUrl from '../../assets/images/battle/tenth-battle-map.svg';
|
||||
import thirteenthBattleMapUrl from '../../assets/images/battle/thirteenth-battle-map.svg';
|
||||
import thirtyFourthBattleMapUrl from '../../assets/images/battle/thirty-fourth-battle-map.svg';
|
||||
import thirtyFirstBattleMapUrl from '../../assets/images/battle/thirty-first-battle-map.svg';
|
||||
import thirtySecondBattleMapUrl from '../../assets/images/battle/thirty-second-battle-map.svg';
|
||||
import thirtyThirdBattleMapUrl from '../../assets/images/battle/thirty-third-battle-map.svg';
|
||||
@@ -124,6 +125,7 @@ export class BootScene extends Phaser.Scene {
|
||||
this.load.image('battle-map-thirty-first', thirtyFirstBattleMapUrl);
|
||||
this.load.image('battle-map-thirty-second', thirtySecondBattleMapUrl);
|
||||
this.load.image('battle-map-thirty-third', thirtyThirdBattleMapUrl);
|
||||
this.load.image('battle-map-thirty-fourth', thirtyFourthBattleMapUrl);
|
||||
this.load.image('portrait-liu-bei', liuBeiPortraitUrl);
|
||||
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
||||
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
||||
|
||||
@@ -19,6 +19,8 @@ import {
|
||||
fuPassRecruitUnits,
|
||||
guiyangRecruitBonds,
|
||||
guiyangRecruitUnits,
|
||||
hanzhongOpeningRecruitBonds,
|
||||
hanzhongOpeningRecruitUnits,
|
||||
jingzhouRecruitBonds,
|
||||
jingzhouRecruitUnits,
|
||||
liuBiaoRecruitBonds,
|
||||
@@ -337,7 +339,7 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
|
||||
title: '촉한 건국',
|
||||
period: '한실을 잇는 깃발',
|
||||
description: '여러 전장을 지나 촉한을 세우고 북벌을 바라보는 장기 목표입니다.',
|
||||
battleIds: [],
|
||||
battleIds: ['thirty-fourth-battle-jiameng-pass'],
|
||||
nextHints: ['왕업 선언', '오호대장군', '북벌 준비']
|
||||
}
|
||||
];
|
||||
@@ -375,7 +377,8 @@ const campBattleIds = {
|
||||
thirtieth: 'thirtieth-battle-luofeng-ambush',
|
||||
thirtyFirst: 'thirty-first-battle-luo-main-gate',
|
||||
thirtySecond: 'thirty-second-battle-mianzhu-gate',
|
||||
thirtyThird: 'thirty-third-battle-chengdu-surrender'
|
||||
thirtyThird: 'thirty-third-battle-chengdu-surrender',
|
||||
thirtyFourth: 'thirty-fourth-battle-jiameng-pass'
|
||||
} as const;
|
||||
|
||||
const requiredSortieUnitIds = new Set(['liu-bei']);
|
||||
@@ -746,6 +749,18 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
|
||||
{ unitId: 'wu-yi', reason: '성도 외곽 길과 창고 위치를 아는 현지 장수입니다.' }
|
||||
],
|
||||
note: '성도 항복 권고전은 열여덟 명 중 여섯 명만 출전합니다. 새로 합류한 이엄을 데려가면 설득과 생존 목표가 맞물리고, 책사 비중을 늘리면 항복 명분이 강해집니다.'
|
||||
},
|
||||
'thirty-fourth-battle-jiameng-pass': {
|
||||
maxUnits: 6,
|
||||
recommended: [
|
||||
{ unitId: 'liu-bei', reason: '가맹관에서 마초를 받아들일 명분과 패배 조건의 중심입니다.' },
|
||||
{ unitId: 'huang-quan', reason: '황권의 첫 출전입니다. 익주 북문과 한중 길을 해석하는 현지 참모입니다.' },
|
||||
{ unitId: 'zhao-yun', reason: '서량 기병의 돌격을 기동으로 받아내고 역습하기 좋습니다.' },
|
||||
{ unitId: 'zhang-fei', reason: '마초의 돌격을 정면에서 받아낼 전열 압박이 필요합니다.' },
|
||||
{ unitId: 'fa-zheng', reason: '마초가 장로에게 마음을 다 주지 못한 틈을 읽는 책사입니다.' },
|
||||
{ unitId: 'huang-zhong', reason: '산허리 궁병과 기병 돌격을 안정적으로 견제합니다.' }
|
||||
],
|
||||
note: '가맹관 마초 대면전은 열아홉 명 중 여섯 명만 출전합니다. 황권을 데려가면 한중 길 해석과 생존 목표가 맞물리고, 기병 대응 무장을 고르는 압박이 커집니다.'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3503,6 +3518,87 @@ const campDialogues: CampDialogue[] = [
|
||||
rewardExp: 12
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'liu-ma-after-jiameng',
|
||||
title: '떠도는 창과 새 깃발',
|
||||
availableAfterBattleIds: [campBattleIds.thirtyFourth],
|
||||
unitIds: ['liu-bei', 'ma-chao'],
|
||||
bondId: 'liu-bei__ma-chao',
|
||||
rewardExp: 40,
|
||||
lines: [
|
||||
'마초: 저는 조조를 미워했고, 장로에게 기대었으나 마음 둘 곳을 찾지 못했습니다.',
|
||||
'유비: 떠돌던 창도 지킬 백성과 뜻을 만나면 깃발 아래 설 수 있소. 그대의 용맹을 사람을 살리는 데 쓰시오.',
|
||||
'마초: 그렇다면 서량의 창은 더는 원한만 좇지 않겠습니다. 유비공의 깃발 아래에서 북쪽 길을 열겠습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'accept-ma-chao-oath',
|
||||
label: '마초의 맹세를 받아들인다',
|
||||
response: '유비가 직접 마초의 창을 들어 올리자, 서량 기병들은 새 깃발 앞에서 고개를 숙였습니다.',
|
||||
rewardExp: 15
|
||||
},
|
||||
{
|
||||
id: 'ask-ma-chao-hanzhong-road',
|
||||
label: '한중 길을 묻는다',
|
||||
response: '마초는 장로군의 관문, 기병이 달릴 수 있는 능선, 조조군의 감시 지점을 지도에 표시했습니다.',
|
||||
rewardExp: 14
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'zhao-ma-after-jiameng',
|
||||
title: '백마와 금마초',
|
||||
availableAfterBattleIds: [campBattleIds.thirtyFourth],
|
||||
unitIds: ['zhao-yun', 'ma-chao'],
|
||||
bondId: 'zhao-yun__ma-chao',
|
||||
rewardExp: 36,
|
||||
lines: [
|
||||
'조운: 서량 기병의 돌격은 빠르나, 빠른 창일수록 돌아올 길도 보아야 합니다.',
|
||||
'마초: 백마 장군의 말은 조용하지만 빈틈이 없군. 내 창은 때로 너무 멀리 나갔습니다.',
|
||||
'조운: 다음 전장에서는 서로의 말발굽이 너무 멀어지지 않게 합시다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'practice-cavalry-turn',
|
||||
label: '기병 회전을 연습한다',
|
||||
response: '조운의 절제된 말놀림과 마초의 폭발적인 돌격이 이어지며, 기병대의 움직임이 한층 깊어졌습니다.',
|
||||
rewardExp: 13
|
||||
},
|
||||
{
|
||||
id: 'share-scouting-pace',
|
||||
label: '정찰 속도를 맞춘다',
|
||||
response: '두 장수는 빠른 돌파와 안전한 귀환의 간격을 맞추며 한중 전선의 정찰 기준을 세웠습니다.',
|
||||
rewardExp: 12
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'zhang-ma-after-jiameng',
|
||||
title: '호통과 돌격',
|
||||
availableAfterBattleIds: [campBattleIds.thirtyFourth],
|
||||
unitIds: ['zhang-fei', 'ma-chao'],
|
||||
bondId: 'zhang-fei__ma-chao',
|
||||
rewardExp: 36,
|
||||
lines: [
|
||||
'장비: 하하! 네놈 창끝이 제법 매섭더군. 그래도 내 앞에서는 한 번 멈췄지 않느냐!',
|
||||
'마초: 장비 장군의 호통도 말발굽을 흔들 만큼 거칠었습니다. 다음에는 같은 방향으로 흔들어 보지요.',
|
||||
'장비: 좋다! 누가 먼저 적진을 갈라 놓는지 겨뤄 보자.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'promise-front-duel',
|
||||
label: '전열 돌파를 겨룬다',
|
||||
response: '장비와 마초가 웃으며 창을 맞대자, 병사들은 다음 전열 돌파가 조금 두려우면서도 기대된다고 수군거렸습니다.',
|
||||
rewardExp: 13
|
||||
},
|
||||
{
|
||||
id: 'split-shock-roles',
|
||||
label: '돌격 역할을 나눈다',
|
||||
response: '장비는 좁은 길을 부수고, 마초는 열린 측면을 파고드는 방식으로 서로의 돌격을 나누었습니다.',
|
||||
rewardExp: 12
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -4612,6 +4708,66 @@ const campVisits: CampVisitDefinition[] = [
|
||||
itemRewards: ['한중 길 초안 1']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'jiameng-stable-mustering',
|
||||
title: '가맹관 마구간 정비',
|
||||
location: '가맹관 마구간',
|
||||
availableAfterBattleIds: [campBattleIds.thirtyFourth],
|
||||
bondId: 'liu-bei__ma-chao',
|
||||
description: '마초가 합류한 뒤 서량 기병의 말과 유비군의 보급 체계를 함께 맞춥니다.',
|
||||
lines: [
|
||||
'마초: 서량 말은 성질이 급합니다. 배불리 먹이고 달릴 길을 먼저 보여 주어야 힘을 냅니다.',
|
||||
'유비: 우리 군영의 말과 사람도 그대의 길에 맞추어 배워야 하오. 북쪽 길은 이제 함께 열 길이오.',
|
||||
'마구간에는 새로 들어온 서량 말의 숨소리가 가득했고, 병사들은 다음 출전 명단을 떠올리며 고삐를 고쳤습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'align-cavalry-feed',
|
||||
label: '말먹이 기준을 맞춘다',
|
||||
response: '서량 말의 먹이와 휴식 기준이 정리되자, 마초의 기병대가 유비군의 행군 속도에 맞춰 움직이기 시작했습니다.',
|
||||
bondExp: 30,
|
||||
itemRewards: ['콩 8', '탁주 3']
|
||||
},
|
||||
{
|
||||
id: 'mark-cavalry-routes',
|
||||
label: '기병 돌파로를 표시한다',
|
||||
response: '마초가 지도 위에 기병이 달릴 수 있는 능선과 피해야 할 늪길을 표시했습니다.',
|
||||
bondExp: 28,
|
||||
gold: 520,
|
||||
itemRewards: ['서량 말먹이 1']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'hanzhong-front-council',
|
||||
title: '한중 전선 회의',
|
||||
location: '가맹관 본막',
|
||||
availableAfterBattleIds: [campBattleIds.thirtyFourth],
|
||||
bondId: 'zhao-yun__ma-chao',
|
||||
description: '조운과 마초가 한중으로 이어지는 관문, 산길, 기병 운용을 놓고 다음 출전 구성을 논의합니다.',
|
||||
lines: [
|
||||
'조운: 가맹관을 넘었다고 길이 열린 것은 아닙니다. 좁은 산길에서는 빠른 말도 한 번 멈춰야 합니다.',
|
||||
'마초: 멈출 곳을 알면 더 세게 달릴 수 있지요. 장로의 군세와 조조의 눈이 모두 북쪽 길에 걸려 있습니다.',
|
||||
'본막의 지도 위에는 한중 전선과 촉한 건국의 다음 숙제가 한 줄로 이어졌습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'plan-hanzhong-scouting',
|
||||
label: '한중 정찰을 준비한다',
|
||||
response: '조운의 정찰 기준과 마초의 기병 감각이 합쳐져, 다음 장의 출전 후보가 더 분명해졌습니다.',
|
||||
bondExp: 30,
|
||||
itemRewards: ['한중 정찰도 1']
|
||||
},
|
||||
{
|
||||
id: 'divide-pass-roles',
|
||||
label: '관문 돌파 역할을 나눈다',
|
||||
response: '조운은 후방을 지키고 마초는 전열을 찌르는 방식으로, 두 기병장의 역할이 정리되었습니다.',
|
||||
bondExp: 28,
|
||||
gold: 540,
|
||||
itemRewards: ['가맹관 전술 기록 1']
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -4698,6 +4854,47 @@ export class CampScene extends Phaser.Scene {
|
||||
return;
|
||||
}
|
||||
|
||||
if (battleId === campBattleIds.thirtyFourth) {
|
||||
this.campaign = ensureCampaignRosterUnits(
|
||||
[
|
||||
...xuzhouRecruitUnits,
|
||||
...caoBreakRecruitUnits,
|
||||
...liuBiaoRecruitUnits,
|
||||
...zhugeRecruitUnits,
|
||||
...jingzhouRecruitUnits,
|
||||
...guiyangRecruitUnits,
|
||||
...wulingRecruitUnits,
|
||||
...changshaRecruitUnits,
|
||||
...yizhouRecruitUnits,
|
||||
...fuPassRecruitUnits,
|
||||
...luoCastleRecruitUnits,
|
||||
...luoCastleProperRecruitUnits,
|
||||
...chengduPressureRecruitUnits,
|
||||
...chengduSurrenderRecruitUnits,
|
||||
...hanzhongOpeningRecruitUnits
|
||||
],
|
||||
[
|
||||
...xuzhouRecruitBonds,
|
||||
...caoBreakRecruitBonds,
|
||||
...liuBiaoRecruitBonds,
|
||||
...zhugeRecruitBonds,
|
||||
...jingzhouRecruitBonds,
|
||||
...guiyangRecruitBonds,
|
||||
...wulingRecruitBonds,
|
||||
...changshaRecruitBonds,
|
||||
...yizhouRecruitBonds,
|
||||
...fuPassRecruitBonds,
|
||||
...luoCastleRecruitBonds,
|
||||
...luoCastleProperRecruitBonds,
|
||||
...chengduPressureRecruitBonds,
|
||||
...chengduSurrenderRecruitBonds,
|
||||
...hanzhongOpeningRecruitBonds
|
||||
]
|
||||
);
|
||||
this.report = this.campaign.firstBattleReport ?? this.report;
|
||||
return;
|
||||
}
|
||||
|
||||
if (battleId === campBattleIds.thirtyThird) {
|
||||
this.campaign = ensureCampaignRosterUnits(
|
||||
[
|
||||
@@ -5050,6 +5247,9 @@ export class CampScene extends Phaser.Scene {
|
||||
|
||||
private currentCampTitle() {
|
||||
const battleId = this.currentCampBattleId();
|
||||
if (battleId === campBattleIds.thirtyFourth) {
|
||||
return '가맹관 대면전 후 군영';
|
||||
}
|
||||
if (battleId === campBattleIds.thirtyThird) {
|
||||
return '성도 항복 후 군영';
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
seventeenthBattleScenario,
|
||||
tenthBattleScenario,
|
||||
thirteenthBattleScenario,
|
||||
thirtyFourthBattleScenario,
|
||||
thirtyFirstBattleScenario,
|
||||
thirtySecondBattleScenario,
|
||||
thirtyThirdBattleScenario,
|
||||
@@ -365,7 +366,8 @@ export class TitleScene extends Phaser.Scene {
|
||||
campaign.step === 'thirtieth-camp' ||
|
||||
campaign.step === 'thirty-first-camp' ||
|
||||
campaign.step === 'thirty-second-camp' ||
|
||||
campaign.step === 'thirty-third-camp'
|
||||
campaign.step === 'thirty-third-camp' ||
|
||||
campaign.step === 'thirty-fourth-camp'
|
||||
) {
|
||||
this.scene.start('CampScene');
|
||||
return;
|
||||
@@ -536,6 +538,11 @@ export class TitleScene extends Phaser.Scene {
|
||||
return;
|
||||
}
|
||||
|
||||
if (campaign.step === 'thirty-fourth-battle') {
|
||||
this.scene.start('BattleScene', { battleId: thirtyFourthBattleScenario.id });
|
||||
return;
|
||||
}
|
||||
|
||||
if (campaign.step === 'first-victory-story') {
|
||||
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user