Add Wuling mountain road chapter
This commit is contained in:
@@ -31,6 +31,7 @@ const unitTexture: Record<string, string> = {
|
||||
'zhuge-liang': 'unit-liu-bei',
|
||||
'ma-liang': 'unit-liu-bei',
|
||||
'yi-ji': 'unit-liu-bei',
|
||||
'gong-zhi': 'unit-guan-yu',
|
||||
'rebel-a': 'unit-rebel',
|
||||
'rebel-b': 'unit-rebel',
|
||||
'rebel-c': 'unit-rebel-archer',
|
||||
@@ -276,7 +277,26 @@ const unitTexture: Record<string, string> = {
|
||||
'guiyang-guard-b': 'unit-rebel',
|
||||
'guiyang-strategist-a': 'unit-rebel-archer',
|
||||
'guiyang-strategist-b': 'unit-rebel-archer',
|
||||
'guiyang-leader-zhao-fan': 'unit-rebel-leader'
|
||||
'guiyang-leader-zhao-fan': 'unit-rebel-leader',
|
||||
'wuling-scout-a': 'unit-rebel',
|
||||
'wuling-scout-b': 'unit-rebel',
|
||||
'wuling-scout-c': 'unit-rebel',
|
||||
'wuling-bandit-a': 'unit-rebel',
|
||||
'wuling-bandit-b': 'unit-rebel',
|
||||
'wuling-bandit-c': 'unit-rebel',
|
||||
'wuling-infantry-a': 'unit-rebel',
|
||||
'wuling-infantry-b': 'unit-rebel',
|
||||
'wuling-infantry-c': 'unit-rebel',
|
||||
'wuling-infantry-d': 'unit-rebel',
|
||||
'wuling-archer-a': 'unit-rebel-archer',
|
||||
'wuling-archer-b': 'unit-rebel-archer',
|
||||
'wuling-archer-c': 'unit-rebel-archer',
|
||||
'wuling-cavalry-a': 'unit-rebel-cavalry',
|
||||
'wuling-cavalry-b': 'unit-rebel-cavalry',
|
||||
'wuling-guard-a': 'unit-rebel',
|
||||
'wuling-guard-b': 'unit-rebel',
|
||||
'wuling-strategist-a': 'unit-rebel-archer',
|
||||
'wuling-leader-jin-xuan': 'unit-rebel-leader'
|
||||
};
|
||||
|
||||
const unitTextureByClass: Partial<Record<UnitClassKey, string>> = {
|
||||
@@ -699,7 +719,8 @@ const unitStrategyIds: Record<string, string[]> = {
|
||||
'sun-qian': ['aid', 'encourage', 'fireTactic'],
|
||||
'zhuge-liang': ['aid', 'encourage', 'fireTactic'],
|
||||
'ma-liang': ['aid', 'encourage', 'fireTactic'],
|
||||
'yi-ji': ['aid', 'encourage']
|
||||
'yi-ji': ['aid', 'encourage'],
|
||||
'gong-zhi': ['encourage']
|
||||
};
|
||||
|
||||
const initialItemStocks: Record<string, Record<string, number>> = {
|
||||
@@ -712,7 +733,8 @@ const initialItemStocks: Record<string, Record<string, number>> = {
|
||||
'zhao-yun': { bean: 1, wine: 1 },
|
||||
'zhuge-liang': { bean: 1, wine: 1 },
|
||||
'ma-liang': { bean: 1, salve: 1 },
|
||||
'yi-ji': { bean: 2, salve: 1 }
|
||||
'yi-ji': { bean: 2, salve: 1 },
|
||||
'gong-zhi': { bean: 2 }
|
||||
};
|
||||
|
||||
const attackRangeByClass: Partial<Record<UnitClassKey, number>> = {
|
||||
@@ -1010,7 +1032,26 @@ const enemyAiByUnitId: Record<string, EnemyAiBehavior> = {
|
||||
'guiyang-guard-b': 'guard',
|
||||
'guiyang-strategist-a': 'hold',
|
||||
'guiyang-strategist-b': 'hold',
|
||||
'guiyang-leader-zhao-fan': 'guard'
|
||||
'guiyang-leader-zhao-fan': 'guard',
|
||||
'wuling-scout-a': 'aggressive',
|
||||
'wuling-scout-b': 'aggressive',
|
||||
'wuling-scout-c': 'aggressive',
|
||||
'wuling-bandit-a': 'aggressive',
|
||||
'wuling-bandit-b': 'aggressive',
|
||||
'wuling-bandit-c': 'aggressive',
|
||||
'wuling-infantry-a': 'guard',
|
||||
'wuling-infantry-b': 'guard',
|
||||
'wuling-infantry-c': 'guard',
|
||||
'wuling-infantry-d': 'guard',
|
||||
'wuling-archer-a': 'hold',
|
||||
'wuling-archer-b': 'hold',
|
||||
'wuling-archer-c': 'hold',
|
||||
'wuling-cavalry-a': 'aggressive',
|
||||
'wuling-cavalry-b': 'aggressive',
|
||||
'wuling-guard-a': 'guard',
|
||||
'wuling-guard-b': 'guard',
|
||||
'wuling-strategist-a': 'hold',
|
||||
'wuling-leader-jin-xuan': 'guard'
|
||||
};
|
||||
|
||||
const defaultEnemyAiByClass: Partial<Record<UnitClassKey, EnemyAiBehavior>> = {
|
||||
|
||||
@@ -20,6 +20,7 @@ import thirdBattleMapUrl from '../../assets/images/battle/third-battle-map.svg';
|
||||
import twentyFirstBattleMapUrl from '../../assets/images/battle/twenty-first-battle-map.svg';
|
||||
import twentySecondBattleMapUrl from '../../assets/images/battle/twenty-second-battle-map.svg';
|
||||
import twentyThirdBattleMapUrl from '../../assets/images/battle/twenty-third-battle-map.svg';
|
||||
import twentyFifthBattleMapUrl from '../../assets/images/battle/twenty-fifth-battle-map.svg';
|
||||
import twentyFourthBattleMapUrl from '../../assets/images/battle/twenty-fourth-battle-map.svg';
|
||||
import twentiethBattleMapUrl from '../../assets/images/battle/twentieth-battle-map.svg';
|
||||
import twelfthBattleMapUrl from '../../assets/images/battle/twelfth-battle-map.svg';
|
||||
@@ -106,6 +107,7 @@ export class BootScene extends Phaser.Scene {
|
||||
this.load.image('battle-map-twenty-second', twentySecondBattleMapUrl);
|
||||
this.load.image('battle-map-twenty-third', twentyThirdBattleMapUrl);
|
||||
this.load.image('battle-map-twenty-fourth', twentyFourthBattleMapUrl);
|
||||
this.load.image('battle-map-twenty-fifth', twentyFifthBattleMapUrl);
|
||||
this.load.image('portrait-liu-bei', liuBeiPortraitUrl);
|
||||
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
||||
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
jingzhouRecruitUnits,
|
||||
liuBiaoRecruitBonds,
|
||||
liuBiaoRecruitUnits,
|
||||
wulingRecruitBonds,
|
||||
wulingRecruitUnits,
|
||||
xuzhouRecruitBonds,
|
||||
xuzhouRecruitUnits,
|
||||
zhugeRecruitBonds,
|
||||
@@ -299,7 +301,11 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
|
||||
title: '형주·익주 확보',
|
||||
period: '근본을 세우다',
|
||||
description: '형주와 익주를 기반으로 삼아 유비군이 유랑군에서 국가의 뿌리로 바뀌는 장입니다.',
|
||||
battleIds: ['twenty-third-battle-jingzhou-south-entry', 'twenty-fourth-battle-guiyang-persuasion'],
|
||||
battleIds: [
|
||||
'twenty-third-battle-jingzhou-south-entry',
|
||||
'twenty-fourth-battle-guiyang-persuasion',
|
||||
'twenty-fifth-battle-wuling-mountain-road'
|
||||
],
|
||||
nextHints: ['형주 방위', '익주 진입', '장수 대거 합류']
|
||||
},
|
||||
{
|
||||
@@ -336,7 +342,8 @@ const campBattleIds = {
|
||||
twentyFirst: 'twenty-first-battle-red-cliffs-vanguard',
|
||||
twentySecond: 'twenty-second-battle-red-cliffs-fire',
|
||||
twentyThird: 'twenty-third-battle-jingzhou-south-entry',
|
||||
twentyFourth: 'twenty-fourth-battle-guiyang-persuasion'
|
||||
twentyFourth: 'twenty-fourth-battle-guiyang-persuasion',
|
||||
twentyFifth: 'twenty-fifth-battle-wuling-mountain-road'
|
||||
} as const;
|
||||
|
||||
const requiredSortieUnitIds = new Set(['liu-bei']);
|
||||
@@ -599,6 +606,18 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
|
||||
{ unitId: 'sun-qian', reason: '이적 합류로 이어질 외교 문안과 항복 조건을 정리합니다.' }
|
||||
],
|
||||
note: '계양 설득전은 마량이 처음으로 출전 압박에 들어오는 전장입니다. 아홉 명 중 여섯 명만 고르며, 문사와 무장의 균형을 직접 선택하십시오.'
|
||||
},
|
||||
'twenty-fifth-battle-wuling-mountain-road': {
|
||||
maxUnits: 6,
|
||||
recommended: [
|
||||
{ unitId: 'liu-bei', reason: '무릉 산길 확보의 명분이자 패배 조건의 중심입니다.' },
|
||||
{ unitId: 'ma-liang', reason: '무릉 산길과 마을 이름을 읽는 현지 정보의 핵심입니다.' },
|
||||
{ unitId: 'yi-ji', reason: '공지의 귀순과 마을 설득으로 이어질 예법과 문안을 맡습니다.' },
|
||||
{ unitId: 'zhuge-liang', reason: '좁은 산길에서 적 방어선을 끊는 작전 핵심입니다.' },
|
||||
{ unitId: 'zhao-yun', reason: '긴 산길을 빠르게 넘어 척후와 기병을 끊기 좋습니다.' },
|
||||
{ unitId: 'zhang-fei', reason: '협곡 보병 전열을 밀어붙이는 돌파 역할입니다.' }
|
||||
],
|
||||
note: '무릉 산길 확보전은 열 명 중 여섯 명만 고르는 첫 전장입니다. 마량과 이적을 모두 데려가면 설득은 강해지지만, 전열 무장 한 명을 줄여야 합니다.'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2600,6 +2619,87 @@ const campDialogues: CampDialogue[] = [
|
||||
rewardExp: 9
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'liu-gong-after-wuling',
|
||||
title: '산길의 귀순',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFifth],
|
||||
unitIds: ['liu-bei', 'gong-zhi'],
|
||||
bondId: 'liu-bei__gong-zhi',
|
||||
rewardExp: 28,
|
||||
lines: [
|
||||
'공지: 무릉의 길은 험하지만, 백성이 숨을 곳은 더 좁습니다. 주공께서는 그 길을 밟지 않고 열어 주셨습니다.',
|
||||
'유비: 그대가 산길과 마을을 아는 만큼, 백성의 두려움도 알고 있겠지. 앞으로 그 길을 함께 살피고 싶소.',
|
||||
'공지: 제가 아는 길을 바치겠습니다. 다만 무릉 사람들의 이름을 먼저 불러 주십시오.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'entrust-wuling-roads',
|
||||
label: '무릉 길 안내를 맡긴다',
|
||||
response: '공지는 무릉 산길의 갈림길과 창고 위치를 정리했고, 유비군의 행군 명단에 새 안내선이 생겼다.',
|
||||
rewardExp: 12
|
||||
},
|
||||
{
|
||||
id: 'promise-wuling-discipline',
|
||||
label: '군율을 다시 약속한다',
|
||||
response: '유비는 무릉 마을을 함부로 징발하지 않겠다고 말했고, 공지는 그 약속을 고을 사람들에게 전하겠다고 답했다.',
|
||||
rewardExp: 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'ma-gong-after-wuling',
|
||||
title: '무릉 기록',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFifth],
|
||||
unitIds: ['ma-liang', 'gong-zhi'],
|
||||
bondId: 'ma-liang__gong-zhi',
|
||||
rewardExp: 26,
|
||||
lines: [
|
||||
'마량: 기록에는 길이 하나로 적혀도, 실제 산에는 세 갈래가 숨어 있더군요.',
|
||||
'공지: 맞습니다. 비가 오면 마을 사람도 다른 길을 씁니다. 무릉은 지도가 아니라 사람에게 길을 물어야 합니다.',
|
||||
'마량: 그렇다면 제 기록은 그 사람들의 이름까지 품어야겠습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'revise-wuling-map',
|
||||
label: '무릉 지도를 고친다',
|
||||
response: '마량과 공지는 기존 지도에 숨은 우회로와 위험한 골짜기를 더했고, 다음 행군의 판단 근거가 두꺼워졌다.',
|
||||
rewardExp: 11
|
||||
},
|
||||
{
|
||||
id: 'record-mountain-clans',
|
||||
label: '산길 마을 이름을 적는다',
|
||||
response: '두 사람은 마을별 장로와 창고 위치를 기록했고, 무릉의 길은 점차 유비군의 말 안으로 들어왔다.',
|
||||
rewardExp: 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'yi-gong-after-wuling',
|
||||
title: '예와 귀순',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFifth],
|
||||
unitIds: ['yi-ji', 'gong-zhi'],
|
||||
bondId: 'yi-ji__gong-zhi',
|
||||
rewardExp: 24,
|
||||
lines: [
|
||||
'이적: 귀순은 무릎을 꿇는 일이 아니라, 다음날 마을이 문을 닫지 않게 하는 약속입니다.',
|
||||
'공지: 무릉 사람들은 말보다 행동을 봅니다. 약속을 글로 남기고, 창고를 지켜야 믿을 것입니다.',
|
||||
'이적: 그렇다면 공지 장군의 길과 제 문장이 함께 가야겠군요.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'write-wuling-pledge',
|
||||
label: '무릉 서약문을 쓴다',
|
||||
response: '이적은 공지의 말을 서약문으로 다듬었고, 무릉의 귀순은 전투 결과가 아니라 군영의 질서로 굳어졌다.',
|
||||
rewardExp: 10
|
||||
},
|
||||
{
|
||||
id: 'prepare-changsha-envoy',
|
||||
label: '장사 사절 예를 논한다',
|
||||
response: '두 사람은 장사로 보낼 예물과 인사 순서를 논했고, 황충을 함부로 대하지 않을 준비가 시작됐다.',
|
||||
rewardExp: 9
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -3169,6 +3269,66 @@ const campVisits: CampVisitDefinition[] = [
|
||||
itemRewards: ['상처약 2']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'wuling-road-repair',
|
||||
title: '무릉 산길 정비',
|
||||
location: '무릉 서쪽 고개',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFifth],
|
||||
bondId: 'liu-bei__gong-zhi',
|
||||
description: '유비와 공지가 전투로 망가진 산길을 정비하며, 마을 사람들이 다시 오갈 수 있는 길을 먼저 회복합니다.',
|
||||
lines: [
|
||||
'공지: 이 고개가 막히면 무릉 사람들은 곡식을 팔러 갈 수도, 약을 구하러 갈 수도 없습니다.',
|
||||
'유비: 군이 지나간 자리라면 군이 먼저 고쳐야 하오. 병사들을 나누어 길을 다듬게 하시오.',
|
||||
'마을 사람들은 처음엔 멀리서 지켜보다가, 길이 열리자 조심스럽게 삽과 바구니를 들고 나왔습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'repair-wuling-pass',
|
||||
label: '고개를 먼저 고친다',
|
||||
response: '공지가 길목을 지휘하고 유비가 병사들을 나누자, 무릉 서쪽 고개가 다시 짐수레를 받을 수 있게 되었다.',
|
||||
bondExp: 22,
|
||||
itemRewards: ['무릉 보급 기록 1']
|
||||
},
|
||||
{
|
||||
id: 'protect-returning-villagers',
|
||||
label: '귀환민을 호위한다',
|
||||
response: '돌아오는 마을 사람들을 호위하자, 무릉의 불안한 소문은 유비군이 길을 지킨다는 말로 바뀌었다.',
|
||||
bondExp: 18,
|
||||
gold: 180,
|
||||
itemRewards: ['콩 3']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'changsha-veteran-rumor',
|
||||
title: '장사 노장 소문',
|
||||
location: '무릉 군막',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFifth],
|
||||
bondId: 'yi-ji__gong-zhi',
|
||||
description: '이적과 공지가 장사로 향하기 전에 황충과 한현의 성격, 장사 성내 분위기를 정리합니다.',
|
||||
lines: [
|
||||
'이적: 장사에는 황충이라는 노장이 있습니다. 예우 없이 대하면 성문보다 마음이 먼저 닫힐 것입니다.',
|
||||
'공지: 한현은 의심이 많고, 황충은 무인의 체면을 중히 여깁니다. 싸움보다 먼저 사람을 보아야 합니다.',
|
||||
'두 사람은 장사로 보낼 사절 명단과 예물을 차례로 적었습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'prepare-huang-zhong-courtesy',
|
||||
label: '황충 예우를 준비한다',
|
||||
response: '이적은 노장을 대하는 예문을 쓰고, 공지는 장사 내부 소문을 덧붙였다. 다음 장의 첫 대면이 조금 더 조심스러워졌다.',
|
||||
bondExp: 20,
|
||||
itemRewards: ['장사 사절 문서 1']
|
||||
},
|
||||
{
|
||||
id: 'map-changsha-approach',
|
||||
label: '장사 접근로를 표시한다',
|
||||
response: '공지의 무릉 길 정보와 이적의 문서가 합쳐져, 장사로 향하는 길과 만날 사람의 순서가 정리되었다.',
|
||||
bondExp: 18,
|
||||
gold: 200,
|
||||
itemRewards: ['남부 군현 지도 1']
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -3254,6 +3414,31 @@ export class CampScene extends Phaser.Scene {
|
||||
return;
|
||||
}
|
||||
|
||||
if (battleId === campBattleIds.twentyFifth) {
|
||||
this.campaign = ensureCampaignRosterUnits(
|
||||
[
|
||||
...xuzhouRecruitUnits,
|
||||
...caoBreakRecruitUnits,
|
||||
...liuBiaoRecruitUnits,
|
||||
...zhugeRecruitUnits,
|
||||
...jingzhouRecruitUnits,
|
||||
...guiyangRecruitUnits,
|
||||
...wulingRecruitUnits
|
||||
],
|
||||
[
|
||||
...xuzhouRecruitBonds,
|
||||
...caoBreakRecruitBonds,
|
||||
...liuBiaoRecruitBonds,
|
||||
...zhugeRecruitBonds,
|
||||
...jingzhouRecruitBonds,
|
||||
...guiyangRecruitBonds,
|
||||
...wulingRecruitBonds
|
||||
]
|
||||
);
|
||||
this.report = this.campaign.firstBattleReport ?? this.report;
|
||||
return;
|
||||
}
|
||||
|
||||
if (battleId === campBattleIds.twentyFourth) {
|
||||
this.campaign = ensureCampaignRosterUnits(
|
||||
[
|
||||
@@ -3350,6 +3535,9 @@ export class CampScene extends Phaser.Scene {
|
||||
|
||||
private currentCampTitle() {
|
||||
const battleId = this.currentCampBattleId();
|
||||
if (battleId === campBattleIds.twentyFifth) {
|
||||
return '무릉 산길 확보 후 군영';
|
||||
}
|
||||
if (battleId === campBattleIds.twentyFourth) {
|
||||
return '계양 설득전 후 군영';
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
twentyFirstBattleScenario,
|
||||
twentySecondBattleScenario,
|
||||
twentyThirdBattleScenario,
|
||||
twentyFifthBattleScenario,
|
||||
twentyFourthBattleScenario,
|
||||
twentiethBattleScenario,
|
||||
twelfthBattleScenario,
|
||||
@@ -347,7 +348,8 @@ export class TitleScene extends Phaser.Scene {
|
||||
campaign.step === 'twenty-first-camp' ||
|
||||
campaign.step === 'twenty-second-camp' ||
|
||||
campaign.step === 'twenty-third-camp' ||
|
||||
campaign.step === 'twenty-fourth-camp'
|
||||
campaign.step === 'twenty-fourth-camp' ||
|
||||
campaign.step === 'twenty-fifth-camp'
|
||||
) {
|
||||
this.scene.start('CampScene');
|
||||
return;
|
||||
@@ -473,6 +475,11 @@ export class TitleScene extends Phaser.Scene {
|
||||
return;
|
||||
}
|
||||
|
||||
if (campaign.step === 'twenty-fifth-battle') {
|
||||
this.scene.start('BattleScene', { battleId: twentyFifthBattleScenario.id });
|
||||
return;
|
||||
}
|
||||
|
||||
if (campaign.step === 'first-victory-story') {
|
||||
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user