Add Guiyang persuasion chapter
This commit is contained in:
@@ -30,6 +30,7 @@ const unitTexture: Record<string, string> = {
|
||||
'zhao-yun': 'unit-rebel-cavalry',
|
||||
'zhuge-liang': 'unit-liu-bei',
|
||||
'ma-liang': 'unit-liu-bei',
|
||||
'yi-ji': 'unit-liu-bei',
|
||||
'rebel-a': 'unit-rebel',
|
||||
'rebel-b': 'unit-rebel',
|
||||
'rebel-c': 'unit-rebel-archer',
|
||||
@@ -258,7 +259,24 @@ const unitTexture: Record<string, string> = {
|
||||
'jing-south-guard-b': 'unit-rebel',
|
||||
'jing-south-strategist-a': 'unit-rebel-archer',
|
||||
'jing-south-strategist-b': 'unit-rebel-archer',
|
||||
'jing-south-leader-xing-daorong': 'unit-rebel-leader'
|
||||
'jing-south-leader-xing-daorong': 'unit-rebel-leader',
|
||||
'guiyang-scout-a': 'unit-rebel',
|
||||
'guiyang-scout-b': 'unit-rebel',
|
||||
'guiyang-infantry-a': 'unit-rebel',
|
||||
'guiyang-infantry-b': 'unit-rebel',
|
||||
'guiyang-infantry-c': 'unit-rebel',
|
||||
'guiyang-infantry-d': 'unit-rebel',
|
||||
'guiyang-archer-a': 'unit-rebel-archer',
|
||||
'guiyang-archer-b': 'unit-rebel-archer',
|
||||
'guiyang-archer-c': 'unit-rebel-archer',
|
||||
'guiyang-cavalry-a': 'unit-rebel-cavalry',
|
||||
'guiyang-cavalry-b': 'unit-rebel-cavalry',
|
||||
'guiyang-cavalry-c': 'unit-rebel-cavalry',
|
||||
'guiyang-guard-a': 'unit-rebel',
|
||||
'guiyang-guard-b': 'unit-rebel',
|
||||
'guiyang-strategist-a': 'unit-rebel-archer',
|
||||
'guiyang-strategist-b': 'unit-rebel-archer',
|
||||
'guiyang-leader-zhao-fan': 'unit-rebel-leader'
|
||||
};
|
||||
|
||||
const unitTextureByClass: Partial<Record<UnitClassKey, string>> = {
|
||||
@@ -680,7 +698,8 @@ const unitStrategyIds: Record<string, string[]> = {
|
||||
'mi-zhu': ['aid', 'encourage'],
|
||||
'sun-qian': ['aid', 'encourage', 'fireTactic'],
|
||||
'zhuge-liang': ['aid', 'encourage', 'fireTactic'],
|
||||
'ma-liang': ['aid', 'encourage', 'fireTactic']
|
||||
'ma-liang': ['aid', 'encourage', 'fireTactic'],
|
||||
'yi-ji': ['aid', 'encourage']
|
||||
};
|
||||
|
||||
const initialItemStocks: Record<string, Record<string, number>> = {
|
||||
@@ -692,7 +711,8 @@ const initialItemStocks: Record<string, Record<string, number>> = {
|
||||
'sun-qian': { bean: 1, salve: 1 },
|
||||
'zhao-yun': { bean: 1, wine: 1 },
|
||||
'zhuge-liang': { bean: 1, wine: 1 },
|
||||
'ma-liang': { bean: 1, salve: 1 }
|
||||
'ma-liang': { bean: 1, salve: 1 },
|
||||
'yi-ji': { bean: 2, salve: 1 }
|
||||
};
|
||||
|
||||
const attackRangeByClass: Partial<Record<UnitClassKey, number>> = {
|
||||
@@ -973,7 +993,24 @@ const enemyAiByUnitId: Record<string, EnemyAiBehavior> = {
|
||||
'jing-south-guard-b': 'guard',
|
||||
'jing-south-strategist-a': 'hold',
|
||||
'jing-south-strategist-b': 'hold',
|
||||
'jing-south-leader-xing-daorong': 'guard'
|
||||
'jing-south-leader-xing-daorong': 'guard',
|
||||
'guiyang-scout-a': 'aggressive',
|
||||
'guiyang-scout-b': 'aggressive',
|
||||
'guiyang-infantry-a': 'guard',
|
||||
'guiyang-infantry-b': 'guard',
|
||||
'guiyang-infantry-c': 'guard',
|
||||
'guiyang-infantry-d': 'guard',
|
||||
'guiyang-archer-a': 'hold',
|
||||
'guiyang-archer-b': 'hold',
|
||||
'guiyang-archer-c': 'hold',
|
||||
'guiyang-cavalry-a': 'aggressive',
|
||||
'guiyang-cavalry-b': 'aggressive',
|
||||
'guiyang-cavalry-c': 'aggressive',
|
||||
'guiyang-guard-a': 'guard',
|
||||
'guiyang-guard-b': 'guard',
|
||||
'guiyang-strategist-a': 'hold',
|
||||
'guiyang-strategist-b': 'hold',
|
||||
'guiyang-leader-zhao-fan': '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 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';
|
||||
import guanYuPortraitUrl from '../../assets/images/portraits/guan-yu.png';
|
||||
@@ -104,6 +105,7 @@ export class BootScene extends Phaser.Scene {
|
||||
this.load.image('battle-map-twenty-first', twentyFirstBattleMapUrl);
|
||||
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('portrait-liu-bei', liuBeiPortraitUrl);
|
||||
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
||||
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
caoBreakRecruitUnits,
|
||||
firstBattleBonds,
|
||||
firstBattleUnits,
|
||||
guiyangRecruitBonds,
|
||||
guiyangRecruitUnits,
|
||||
jingzhouRecruitBonds,
|
||||
jingzhouRecruitUnits,
|
||||
liuBiaoRecruitBonds,
|
||||
@@ -297,7 +299,7 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
|
||||
title: '형주·익주 확보',
|
||||
period: '근본을 세우다',
|
||||
description: '형주와 익주를 기반으로 삼아 유비군이 유랑군에서 국가의 뿌리로 바뀌는 장입니다.',
|
||||
battleIds: ['twenty-third-battle-jingzhou-south-entry'],
|
||||
battleIds: ['twenty-third-battle-jingzhou-south-entry', 'twenty-fourth-battle-guiyang-persuasion'],
|
||||
nextHints: ['형주 방위', '익주 진입', '장수 대거 합류']
|
||||
},
|
||||
{
|
||||
@@ -333,7 +335,8 @@ const campBattleIds = {
|
||||
twentieth: 'twentieth-battle-jiangdong-envoy',
|
||||
twentyFirst: 'twenty-first-battle-red-cliffs-vanguard',
|
||||
twentySecond: 'twenty-second-battle-red-cliffs-fire',
|
||||
twentyThird: 'twenty-third-battle-jingzhou-south-entry'
|
||||
twentyThird: 'twenty-third-battle-jingzhou-south-entry',
|
||||
twentyFourth: 'twenty-fourth-battle-guiyang-persuasion'
|
||||
} as const;
|
||||
|
||||
const requiredSortieUnitIds = new Set(['liu-bei']);
|
||||
@@ -584,6 +587,18 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
|
||||
{ unitId: 'mi-zhu', reason: '긴 남부 길목에서 회복과 보급을 맡기 좋습니다.' }
|
||||
],
|
||||
note: '형주 남부 진입전은 마을 안정과 길목 돌파가 동시에 필요한 전장입니다. 마량은 승리 후 합류하므로 이번 전투 뒤 다음 출전 후보로 늘어납니다.'
|
||||
},
|
||||
'twenty-fourth-battle-guiyang-persuasion': {
|
||||
maxUnits: 6,
|
||||
recommended: [
|
||||
{ unitId: 'liu-bei', reason: '계양 설득의 명분이자 패배 조건의 중심입니다.' },
|
||||
{ unitId: 'ma-liang', reason: '계양 토호와 마을 사정을 읽는 핵심 문사이며 반드시 생존해야 합니다.' },
|
||||
{ unitId: 'zhuge-liang', reason: '성채를 압박하면서도 항복 명분을 유지하는 작전 핵심입니다.' },
|
||||
{ unitId: 'zhao-yun', reason: '산길 척후와 기병을 빠르게 끊어 마을을 안정시키기 좋습니다.' },
|
||||
{ unitId: 'guan-yu', reason: '조범 친위대와 성문 보병을 받아낼 주력 전열입니다.' },
|
||||
{ unitId: 'sun-qian', reason: '이적 합류로 이어질 외교 문안과 항복 조건을 정리합니다.' }
|
||||
],
|
||||
note: '계양 설득전은 마량이 처음으로 출전 압박에 들어오는 전장입니다. 아홉 명 중 여섯 명만 고르며, 문사와 무장의 균형을 직접 선택하십시오.'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2504,6 +2519,87 @@ const campDialogues: CampDialogue[] = [
|
||||
rewardExp: 9
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'liu-yi-after-guiyang',
|
||||
title: '예로 열린 계양',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFourth],
|
||||
unitIds: ['liu-bei', 'yi-ji'],
|
||||
bondId: 'liu-bei__yi-ji',
|
||||
rewardExp: 28,
|
||||
lines: [
|
||||
'이적: 계양의 문이 칼끝이 아니라 군율과 서신 앞에서 열렸습니다. 그런 군이라면 저도 따를 이유가 있습니다.',
|
||||
'유비: 형주의 사람을 얻으려면 형주의 예를 알아야 하오. 선생이 길을 알려 준다면 다음 군현도 덜 흔들릴 것이오.',
|
||||
'이적: 다음부터는 싸우기 전에 먼저 물어야 할 이름과 예가 있습니다. 제가 그 문장을 준비하겠습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'entrust-guiyang-etiquette',
|
||||
label: '계양의 예법을 맡긴다',
|
||||
response: '이적은 계양의 토호와 문사 이름을 정리했고, 유비군의 다음 설득 문서는 한층 단정해졌다.',
|
||||
rewardExp: 12
|
||||
},
|
||||
{
|
||||
id: 'share-liu-bei-cause-with-yi-ji',
|
||||
label: '유비군의 뜻을 설명한다',
|
||||
response: '유비는 백성을 먼저 세우려는 뜻을 말했고, 이적은 그 뜻을 형주 사람들이 읽을 수 있는 문장으로 다듬었다.',
|
||||
rewardExp: 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'ma-yi-after-guiyang',
|
||||
title: '백미와 문사',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFourth],
|
||||
unitIds: ['ma-liang', 'yi-ji'],
|
||||
bondId: 'ma-liang__yi-ji',
|
||||
rewardExp: 26,
|
||||
lines: [
|
||||
'마량: 계양은 열렸지만 무릉은 산길이 험합니다. 길을 아는 사람과 마음을 아는 사람이 함께 움직여야 합니다.',
|
||||
'이적: 마량 선생의 기록은 정확하고, 제 일은 그 기록을 상대가 받아들일 말로 바꾸는 것입니다.',
|
||||
'마량: 그렇다면 다음 전장부터는 길과 문장이 함께 가겠군요. 무릉으로 가기 전에 마을별 이름을 맞춰 봅시다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'align-commandery-records',
|
||||
label: '군현 기록을 맞춘다',
|
||||
response: '마량과 이적은 마을별 호칭과 길목 기록을 하나로 맞추었고, 무릉 준비의 빈칸이 줄어들었다.',
|
||||
rewardExp: 11
|
||||
},
|
||||
{
|
||||
id: 'prepare-mountain-letters',
|
||||
label: '산길 마을 서신을 준비한다',
|
||||
response: '두 문사는 무릉 산길 마을에 보낼 짧은 서신을 써 두었고, 다음 행군의 설득력이 깊어졌다.',
|
||||
rewardExp: 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'sun-yi-after-guiyang',
|
||||
title: '외교 문안',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFourth],
|
||||
unitIds: ['sun-qian', 'yi-ji'],
|
||||
bondId: 'sun-qian__yi-ji',
|
||||
rewardExp: 24,
|
||||
lines: [
|
||||
'손건: 저는 명분을 곧게 세우는 편이고, 이적 선생은 상대의 체면을 세우는 법을 아는군요.',
|
||||
'이적: 둘 중 하나만 있으면 문이 반쯤만 열립니다. 곧은 뜻과 부드러운 예가 함께 있어야 합니다.',
|
||||
'손건: 다음 군현으로 가는 서신은 둘이 함께 씁시다. 칼보다 먼저 도착해야 할 글입니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'write-joint-diplomacy-note',
|
||||
label: '공동 서신을 쓴다',
|
||||
response: '손건과 이적은 유비군의 뜻과 형주의 예를 함께 담은 서신을 만들었고, 군영의 외교 축이 넓어졌다.',
|
||||
rewardExp: 10
|
||||
},
|
||||
{
|
||||
id: 'sort-commandery-audience',
|
||||
label: '면담 순서를 정한다',
|
||||
response: '두 사람은 누구를 먼저 만나야 성문이 열릴지 순서를 세웠고, 다음 군현 접근이 조금 더 현실적인 계획이 되었다.',
|
||||
rewardExp: 9
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -3013,6 +3109,66 @@ const campVisits: CampVisitDefinition[] = [
|
||||
itemRewards: ['군현 문서 1']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'guiyang-civic-pledge',
|
||||
title: '계양 민심 서약',
|
||||
location: '계양 관청',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFourth],
|
||||
bondId: 'liu-bei__yi-ji',
|
||||
description: '유비와 이적이 계양 관청에서 군율과 세곡 처리 원칙을 밝히며 새로 열린 군현의 불안을 다독입니다.',
|
||||
lines: [
|
||||
'이적: 계양 사람들은 항복보다 그 뒤의 세금과 군율을 두려워합니다. 먼저 약속을 글로 남겨야 합니다.',
|
||||
'유비: 우리가 이곳에 머무는 동안 백성의 창고를 함부로 열지 않겠소. 필요한 것은 값을 치르고, 군율을 어기면 벌하겠소.',
|
||||
'관청 앞에 모인 사람들은 아직 조심스러웠지만, 글로 남긴 약속을 보고 처음으로 긴 숨을 내쉬었습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'publish-guiyang-discipline',
|
||||
label: '군율 서약을 내건다',
|
||||
response: '이적은 유비의 약속을 관청 문에 걸었고, 계양의 불안은 조금씩 거래와 보급으로 바뀌기 시작했다.',
|
||||
bondExp: 22,
|
||||
itemRewards: ['계양 민심 기록 1']
|
||||
},
|
||||
{
|
||||
id: 'settle-storehouse-rules',
|
||||
label: '창고 사용 원칙을 정한다',
|
||||
response: '계양 창고의 곡식 출납 원칙이 정해지자, 미축의 보급 계획도 다음 전장까지 이어질 수 있게 되었다.',
|
||||
bondExp: 18,
|
||||
gold: 180,
|
||||
itemRewards: ['콩 3']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'wuling-mountain-scouting',
|
||||
title: '무릉 산길 정찰',
|
||||
location: '무릉 길목',
|
||||
availableAfterBattleIds: [campBattleIds.twentyFourth],
|
||||
bondId: 'ma-liang__yi-ji',
|
||||
description: '마량과 이적이 무릉으로 이어지는 산길과 마을 이름을 대조하며 다음 전투의 위험을 미리 살핍니다.',
|
||||
lines: [
|
||||
'마량: 이 길은 좁고 마을은 흩어져 있습니다. 기병만 앞세우면 빠르지만, 뒤의 보급이 끊길 수 있습니다.',
|
||||
'이적: 산길 마을은 말보다 신뢰를 먼저 봅니다. 먼저 이름을 부르고, 그 다음에 도움을 청해야 합니다.',
|
||||
'두 사람은 길목마다 작은 표식을 남기고, 다음 전투에서 놓치기 쉬운 마을 이름을 지도 옆에 적었습니다.'
|
||||
],
|
||||
choices: [
|
||||
{
|
||||
id: 'mark-wuling-villages',
|
||||
label: '산길 마을을 표시한다',
|
||||
response: '무릉의 작은 마을들이 지도에 표시되자, 다음 전장에서는 이동과 보급의 선택이 더 분명해졌다.',
|
||||
bondExp: 20,
|
||||
itemRewards: ['무릉 산길 지도 1']
|
||||
},
|
||||
{
|
||||
id: 'prepare-mountain-supplies',
|
||||
label: '산길 보급을 준비한다',
|
||||
response: '이적은 마을별 필요한 물자를 적고, 마량은 안전한 우회로를 더했다. 무릉 진입 준비가 조금 더 단단해졌다.',
|
||||
bondExp: 18,
|
||||
gold: 160,
|
||||
itemRewards: ['상처약 2']
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
@@ -3098,6 +3254,29 @@ export class CampScene extends Phaser.Scene {
|
||||
return;
|
||||
}
|
||||
|
||||
if (battleId === campBattleIds.twentyFourth) {
|
||||
this.campaign = ensureCampaignRosterUnits(
|
||||
[
|
||||
...xuzhouRecruitUnits,
|
||||
...caoBreakRecruitUnits,
|
||||
...liuBiaoRecruitUnits,
|
||||
...zhugeRecruitUnits,
|
||||
...jingzhouRecruitUnits,
|
||||
...guiyangRecruitUnits
|
||||
],
|
||||
[
|
||||
...xuzhouRecruitBonds,
|
||||
...caoBreakRecruitBonds,
|
||||
...liuBiaoRecruitBonds,
|
||||
...zhugeRecruitBonds,
|
||||
...jingzhouRecruitBonds,
|
||||
...guiyangRecruitBonds
|
||||
]
|
||||
);
|
||||
this.report = this.campaign.firstBattleReport ?? this.report;
|
||||
return;
|
||||
}
|
||||
|
||||
if (battleId === campBattleIds.twentyThird) {
|
||||
this.campaign = ensureCampaignRosterUnits(
|
||||
[...xuzhouRecruitUnits, ...caoBreakRecruitUnits, ...liuBiaoRecruitUnits, ...zhugeRecruitUnits, ...jingzhouRecruitUnits],
|
||||
@@ -3171,6 +3350,9 @@ export class CampScene extends Phaser.Scene {
|
||||
|
||||
private currentCampTitle() {
|
||||
const battleId = this.currentCampBattleId();
|
||||
if (battleId === campBattleIds.twentyFourth) {
|
||||
return '계양 설득전 후 군영';
|
||||
}
|
||||
if (battleId === campBattleIds.twentyThird) {
|
||||
return '형주 남부 진입 후 군영';
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
twentyFirstBattleScenario,
|
||||
twentySecondBattleScenario,
|
||||
twentyThirdBattleScenario,
|
||||
twentyFourthBattleScenario,
|
||||
twentiethBattleScenario,
|
||||
twelfthBattleScenario,
|
||||
thirdBattleScenario
|
||||
@@ -345,7 +346,8 @@ export class TitleScene extends Phaser.Scene {
|
||||
campaign.step === 'twentieth-camp' ||
|
||||
campaign.step === 'twenty-first-camp' ||
|
||||
campaign.step === 'twenty-second-camp' ||
|
||||
campaign.step === 'twenty-third-camp'
|
||||
campaign.step === 'twenty-third-camp' ||
|
||||
campaign.step === 'twenty-fourth-camp'
|
||||
) {
|
||||
this.scene.start('CampScene');
|
||||
return;
|
||||
@@ -466,6 +468,11 @@ export class TitleScene extends Phaser.Scene {
|
||||
return;
|
||||
}
|
||||
|
||||
if (campaign.step === 'twenty-fourth-battle') {
|
||||
this.scene.start('BattleScene', { battleId: twentyFourthBattleScenario.id });
|
||||
return;
|
||||
}
|
||||
|
||||
if (campaign.step === 'first-victory-story') {
|
||||
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user