Add Yangping Pass scout chapter

This commit is contained in:
2026-06-23 13:22:13 +09:00
parent edbdaac773
commit c724d9d48f
12 changed files with 890 additions and 39 deletions

View File

@@ -21,6 +21,8 @@ import {
guiyangRecruitUnits,
hanzhongOpeningRecruitBonds,
hanzhongOpeningRecruitUnits,
hanzhongScoutRecruitBonds,
hanzhongScoutRecruitUnits,
jingzhouRecruitBonds,
jingzhouRecruitUnits,
liuBiaoRecruitBonds,
@@ -339,7 +341,7 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
title: '촉한 건국',
period: '한실을 잇는 깃발',
description: '여러 전장을 지나 촉한을 세우고 북벌을 바라보는 장기 목표입니다.',
battleIds: ['thirty-fourth-battle-jiameng-pass'],
battleIds: ['thirty-fourth-battle-jiameng-pass', 'thirty-fifth-battle-yangping-scout'],
nextHints: ['왕업 선언', '오호대장군', '북벌 준비']
}
];
@@ -378,7 +380,8 @@ const campBattleIds = {
thirtyFirst: 'thirty-first-battle-luo-main-gate',
thirtySecond: 'thirty-second-battle-mianzhu-gate',
thirtyThird: 'thirty-third-battle-chengdu-surrender',
thirtyFourth: 'thirty-fourth-battle-jiameng-pass'
thirtyFourth: 'thirty-fourth-battle-jiameng-pass',
thirtyFifth: 'thirty-fifth-battle-yangping-scout'
} as const;
const requiredSortieUnitIds = new Set(['liu-bei']);
@@ -761,6 +764,18 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
{ unitId: 'huang-zhong', reason: '산허리 궁병과 기병 돌격을 안정적으로 견제합니다.' }
],
note: '가맹관 마초 대면전은 열아홉 명 중 여섯 명만 출전합니다. 황권을 데려가면 한중 길 해석과 생존 목표가 맞물리고, 기병 대응 무장을 고르는 압박이 커집니다.'
},
'thirty-fifth-battle-yangping-scout': {
maxUnits: 6,
recommended: [
{ unitId: 'liu-bei', reason: '한중 전선의 첫 명분과 패배 조건의 중심입니다.' },
{ unitId: 'ma-chao', reason: '마초의 첫 아군 출전입니다. 서량 기병으로 양평관 선봉을 흔들 수 있습니다.' },
{ unitId: 'huang-quan', reason: '양평관 지형과 창고 확보 목표를 읽는 현지 참모입니다.' },
{ unitId: 'zhuge-liang', reason: '한중 본전으로 이어질 전선 설계와 적 책사 견제가 필요합니다.' },
{ unitId: 'zhao-yun', reason: '마초가 너무 깊게 들어가지 않도록 기병 후속을 안정시킵니다.' },
{ unitId: 'fa-zheng', reason: '장로군의 내부 동요와 염포의 책략을 읽는 데 강합니다.' }
],
note: '양평관 정찰전은 스무 명 중 여섯 명만 출전합니다. 마초가 첫 아군 출전 후보로 들어오며, 기병 선봉과 책사 후원을 동시에 고르는 압박이 생깁니다.'
}
};
@@ -3599,6 +3614,87 @@ const campDialogues: CampDialogue[] = [
rewardExp: 12
}
]
},
{
id: 'ma-chao-dai-after-yangping',
title: '서량의 두 창',
availableAfterBattleIds: [campBattleIds.thirtyFifth],
unitIds: ['ma-chao', 'ma-dai'],
bondId: 'ma-chao__ma-dai',
rewardExp: 40,
lines: [
'마대: 형님, 흩어진 서량 기병을 데려왔습니다. 이제는 원한이 아니라 새 깃발을 보고 달리게 하겠습니다.',
'마초: 잘 왔다. 내가 너무 앞서 달리면 네가 뒤를 거두어라. 서량의 말발굽이 이번에는 흩어지지 않아야 한다.',
'마대: 명심하겠습니다. 형님이 길을 열면 저는 그 길이 무너지지 않게 지키겠습니다.'
],
choices: [
{
id: 'bind-western-cavalry',
label: '서량 기병을 묶는다',
response: '마초와 마대가 함께 기병 명단을 정리하자, 흩어진 서량 병사들이 하나의 대오로 서기 시작했습니다.',
rewardExp: 15
},
{
id: 'divide-cavalry-charge',
label: '돌격과 회수를 나눈다',
response: '마초는 전열 돌파를, 마대는 후속 회수를 맡기로 하며 서량 기병의 움직임이 한층 안정되었습니다.',
rewardExp: 14
}
]
},
{
id: 'liu-ma-dai-after-yangping',
title: '새 깃발의 기병',
availableAfterBattleIds: [campBattleIds.thirtyFifth],
unitIds: ['liu-bei', 'ma-dai'],
bondId: 'liu-bei__ma-dai',
rewardExp: 34,
lines: [
'유비: 그대는 마초를 따라왔으나, 이제는 백성과 병사를 지키는 길도 함께 보아야 하오.',
'마대: 서량의 병사는 빠르게 달리는 법은 알지만, 오래 머무는 법은 서툽니다. 유비공의 군율을 배우겠습니다.',
'유비: 빠른 말도 돌아올 곳이 있어야 힘을 쓰는 법이오. 우리 깃발 아래에서 그 자리를 찾으시오.'
],
choices: [
{
id: 'accept-ma-dai-oath',
label: '마대의 맹세를 받는다',
response: '마대가 창을 세워 예를 올리자, 유비군의 장수들은 새 기병장의 이름을 출전 명단에 적었습니다.',
rewardExp: 13
},
{
id: 'teach-camp-discipline',
label: '군영 군율을 일러준다',
response: '마대는 서량 기병에게 약탈 금지와 보급 질서를 먼저 전하며, 새 군영에 맞춰 움직이기 시작했습니다.',
rewardExp: 12
}
]
},
{
id: 'zhao-ma-dai-after-yangping',
title: '침착한 말발굽',
availableAfterBattleIds: [campBattleIds.thirtyFifth],
unitIds: ['zhao-yun', 'ma-dai'],
bondId: 'zhao-yun__ma-dai',
rewardExp: 32,
lines: [
'조운: 마초 장군의 창이 앞을 열면, 그 뒤의 길은 누군가 차분히 지켜야 합니다.',
'마대: 제가 그 일을 맡겠습니다. 형님의 기세를 살리되, 병사가 흩어지지 않도록 하겠습니다.',
'조운: 좋습니다. 빠른 말발굽에도 조용한 박자가 있어야 오래 싸울 수 있습니다.'
],
choices: [
{
id: 'practice-follow-up-line',
label: '후속 대열을 연습한다',
response: '조운과 마대는 돌격 뒤 흩어진 병사를 다시 모으는 훈련을 반복하며 기병대의 안정감을 높였습니다.',
rewardExp: 12
},
{
id: 'mark-safe-return',
label: '귀환로를 표시한다',
response: '두 장수는 양평관 지도에 돌파로와 귀환로를 함께 표시해 다음 한중 전투의 위험을 줄였습니다.',
rewardExp: 12
}
]
}
];
@@ -4768,6 +4864,66 @@ const campVisits: CampVisitDefinition[] = [
itemRewards: ['가맹관 전술 기록 1']
}
]
},
{
id: 'yangping-storehouse-survey',
title: '양평관 창고 조사',
location: '양평관 외곽 창고',
availableAfterBattleIds: [campBattleIds.thirtyFifth],
bondId: 'liu-bei__ma-dai',
description: '유비와 마대가 양평관 창고의 말먹이, 군량, 통행로를 확인해 한중 본전을 준비합니다.',
lines: [
'마대: 말은 창보다 먼저 먹이를 찾습니다. 이 창고를 잡으면 서량 기병도 한중 길에서 오래 버틸 수 있습니다.',
'유비: 군량이 있어야 백성의 것을 빼앗지 않소. 창고를 얻었다면 먼저 장부를 세워야 하오.',
'창고 안에는 말먹이와 마른 군량이 쌓여 있었고, 한중 본전을 위한 첫 보급 기준이 마련되었습니다.'
],
choices: [
{
id: 'sort-horse-feed',
label: '말먹이를 분류한다',
response: '마대가 말먹이와 기병 장비를 따로 정리하자 서량 기병의 다음 출전 준비가 빨라졌습니다.',
bondExp: 30,
itemRewards: ['서량 말먹이 2', '콩 8']
},
{
id: 'record-storehouse-ledger',
label: '창고 장부를 남긴다',
response: '유비는 창고 장부를 군영에 공개해 양평관에서 얻은 물자가 약탈이 아니라 군율 아래 쓰이게 했습니다.',
bondExp: 28,
gold: 560,
itemRewards: ['양평관 창고 장부 1']
}
]
},
{
id: 'yangping-ridge-drill',
title: '양평관 능선 훈련',
location: '양평관 서쪽 능선',
availableAfterBattleIds: [campBattleIds.thirtyFifth],
bondId: 'ma-chao__ma-dai',
description: '마초와 마대가 좁은 능선에서 기병 돌격과 회수 훈련을 맞추며 다음 한중 전투의 기동 축을 만듭니다.',
lines: [
'마초: 넓은 벌판이 아니라도 말은 달릴 수 있다. 대신 멈출 때를 알아야 한다.',
'마대: 형님이 뚫으면 제가 뒤를 묶겠습니다. 능선에서는 한 걸음 늦은 창이 대오를 살립니다.',
'서쪽 능선에는 짧은 돌격과 빠른 회수가 반복되며, 서량 기병의 새 박자가 자리 잡았습니다.'
],
choices: [
{
id: 'drill-ridge-charge',
label: '능선 돌격을 연습한다',
response: '마초의 선봉과 마대의 후속 대열이 맞물리며 좁은 능선에서도 기병이 흔들리지 않게 되었습니다.',
bondExp: 32,
itemRewards: ['기병 훈련 기록 1']
},
{
id: 'set-recovery-signal',
label: '회수 신호를 정한다',
response: '깃발과 북소리로 회수 신호를 정하자, 서량 기병은 돌격 후 돌아오는 박자를 배웠습니다.',
bondExp: 30,
gold: 580,
itemRewards: ['탁주 4']
}
]
}
];
@@ -4854,6 +5010,49 @@ export class CampScene extends Phaser.Scene {
return;
}
if (battleId === campBattleIds.thirtyFifth) {
this.campaign = ensureCampaignRosterUnits(
[
...xuzhouRecruitUnits,
...caoBreakRecruitUnits,
...liuBiaoRecruitUnits,
...zhugeRecruitUnits,
...jingzhouRecruitUnits,
...guiyangRecruitUnits,
...wulingRecruitUnits,
...changshaRecruitUnits,
...yizhouRecruitUnits,
...fuPassRecruitUnits,
...luoCastleRecruitUnits,
...luoCastleProperRecruitUnits,
...chengduPressureRecruitUnits,
...chengduSurrenderRecruitUnits,
...hanzhongOpeningRecruitUnits,
...hanzhongScoutRecruitUnits
],
[
...xuzhouRecruitBonds,
...caoBreakRecruitBonds,
...liuBiaoRecruitBonds,
...zhugeRecruitBonds,
...jingzhouRecruitBonds,
...guiyangRecruitBonds,
...wulingRecruitBonds,
...changshaRecruitBonds,
...yizhouRecruitBonds,
...fuPassRecruitBonds,
...luoCastleRecruitBonds,
...luoCastleProperRecruitBonds,
...chengduPressureRecruitBonds,
...chengduSurrenderRecruitBonds,
...hanzhongOpeningRecruitBonds,
...hanzhongScoutRecruitBonds
]
);
this.report = this.campaign.firstBattleReport ?? this.report;
return;
}
if (battleId === campBattleIds.thirtyFourth) {
this.campaign = ensureCampaignRosterUnits(
[
@@ -5247,6 +5446,9 @@ export class CampScene extends Phaser.Scene {
private currentCampTitle() {
const battleId = this.currentCampBattleId();
if (battleId === campBattleIds.thirtyFifth) {
return '양평관 정찰 후 군영';
}
if (battleId === campBattleIds.thirtyFourth) {
return '가맹관 대면전 후 군영';
}