Add Guiyang persuasion chapter

This commit is contained in:
2026-06-23 07:58:54 +09:00
parent d26a16d2a1
commit f8cc2f329c
11 changed files with 1134 additions and 28 deletions

View File

@@ -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 '형주 남부 진입 후 군영';
}