Add Wudu Yinping campaign chapter

This commit is contained in:
2026-06-24 11:28:46 +09:00
parent 779b639bdf
commit f3d23a13be
10 changed files with 833 additions and 76 deletions

View File

@@ -55,6 +55,7 @@ import {
northernFourthBattleBonds,
northernFirstBattleBonds,
northernSecondBattleBonds,
northernSixthBattleBonds,
northernThirdBattleBonds,
wulingRecruitBonds,
wulingRecruitUnits,
@@ -443,9 +444,10 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
'fifty-sixth-battle-tianshui-advance',
'fifty-seventh-battle-jieting-crisis',
'fifty-eighth-battle-qishan-retreat',
'fifty-ninth-battle-chencang-siege'
'fifty-ninth-battle-chencang-siege',
'sixtieth-battle-wudu-yinping'
],
nextHints: ['한중 창고 정비', '출전 무장 재편', '기산 출진로', '천수·가정 방면', '강유 합류', '진창 공성 재계산']
nextHints: ['한중 창고 정비', '출전 무장 재편', '기산 출진로', '천수·가정 방면', '강유 합류', '진창 공성 재계산', '무도·음평 확보']
}
];
@@ -508,7 +510,8 @@ const campBattleIds = {
fiftySixth: 'fifty-sixth-battle-tianshui-advance',
fiftySeventh: 'fifty-seventh-battle-jieting-crisis',
fiftyEighth: 'fifty-eighth-battle-qishan-retreat',
fiftyNinth: 'fifty-ninth-battle-chencang-siege'
fiftyNinth: 'fifty-ninth-battle-chencang-siege',
sixtieth: 'sixtieth-battle-wudu-yinping'
} as const;
const defaultRequiredSortieUnitIds = ['liu-bei'];
@@ -1242,6 +1245,22 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
{ unitId: 'fa-zheng', reason: '학소의 성벽 대응과 위군 군리의 빈틈을 읽는 책략 담당입니다.' }
],
note: '진창 공성전은 강유가 계속 중요하지만, 위연의 돌파와 조운·마대의 기동, 왕평의 진영 유지, 황권·법정의 보급 판단을 7명 안에서 갈라야 합니다.'
},
'sixtieth-battle-wudu-yinping': {
maxUnits: 7,
requiredUnitIds: ['zhuge-liang'],
excludedUnitIds: ['liu-bei'],
recommended: [
{ unitId: 'zhuge-liang', reason: '무도와 음평 두 길을 동시에 조율하는 총지휘관입니다. 두 고을을 얻어도 보급이 끊기면 북벌은 좁아집니다.' },
{ unitId: 'jiang-wei', reason: '천수와 무도 사이 낮은 길을 잇는 길잡이입니다. 곽회의 구원로를 먼저 읽어야 합니다.' },
{ unitId: 'wei-yan', reason: '무도 고개를 빠르게 밀어붙일 돌파 장수입니다. 다만 깊이 들어간 뒤 돌아올 길을 함께 계산해야 합니다.' },
{ unitId: 'zhao-yun', reason: '구원 기병을 늦추고 앞선 부대를 회수하는 안정된 기동 담당입니다.' },
{ unitId: 'ma-dai', reason: '서량 기병의 우회 감각으로 음평 물목과 곽회 기병의 측면을 견제합니다.' },
{ unitId: 'wang-ping', reason: '산길 진영과 정지선을 표시해 두 갈래 전선이 흩어지지 않게 합니다.' },
{ unitId: 'huang-quan', reason: '무도와 음평으로 갈라지는 군량 장부를 맞춰 장기전 붕괴를 막습니다.' },
{ unitId: 'ma-liang', reason: '새로 확보할 고을의 백성에게 승리보다 약속을 먼저 설명할 문서 담당입니다.' }
],
note: '무도·음평 확보전은 추천 무장이 출전 제한보다 많습니다. 강유와 위연의 전진성, 조운·마대의 기동, 왕평·황권·마량의 제동과 안정 중 하나를 반드시 포기해야 합니다.'
}
};
@@ -1290,6 +1309,22 @@ const sortieRulesByCampaignStep: Partial<Record<CampaignStep, SortieRuleDefiniti
{ unitId: 'fa-zheng', reason: '학소의 수비와 위군 대응을 계책 관점에서 정리합니다.' }
],
note: '진창 공성전 이후도 엔딩이 아니라 북벌 보급 재계산 단계입니다. 강유와 위연의 역할, 왕평과 황권의 제동 장치를 함께 보십시오.'
},
'sixtieth-camp': {
maxUnits: 7,
requiredUnitIds: ['zhuge-liang'],
excludedUnitIds: ['liu-bei'],
recommended: [
{ unitId: 'zhuge-liang', reason: '두 고을 확보 뒤 다음 북벌 계산을 다시 세워야 하는 중심입니다.' },
{ unitId: 'jiang-wei', reason: '무도와 음평의 새 길을 다음 전선의 기준으로 정리해야 합니다.' },
{ unitId: 'wei-yan', reason: '돌파가 실제 성과를 냈을 때 어디까지 허용할지 평가해야 합니다.' },
{ unitId: 'wang-ping', reason: '산길 진영 표식과 정지선을 다음 출정 군령으로 정리합니다.' },
{ unitId: 'huang-quan', reason: '두 고을을 얻은 뒤 늘어난 보급 책임을 장부로 다시 계산합니다.' },
{ unitId: 'ma-liang', reason: '새 고을의 민심과 고시문을 정리해 점령이 약탈처럼 보이지 않게 합니다.' },
{ unitId: 'zhao-yun', reason: '깊어진 전선에서 회수 기동 기준을 다시 훈련시킵니다.' },
{ unitId: 'ma-dai', reason: '음평 물목과 서량 기병 운용을 다음 우회 전선 기준으로 남깁니다.' }
],
note: '무도·음평 확보 이후도 엔딩이 아니라 더 넓어진 북벌 책임입니다. 새 고을 안정과 다음 전진 기준을 함께 점검하십시오.'
}
};
@@ -6321,6 +6356,87 @@ const campDialogues: CampDialogue[] = [
rewardExp: 28
}
]
},
{
id: 'northern-sixth-jiangwei-weiyan-pass',
title: '열린 길과 멈출 선',
availableAfterBattleIds: [campBattleIds.sixtieth],
unitIds: ['jiang-wei', 'wei-yan'],
bondId: 'jiang-wei__wei-yan_northern-sixth',
rewardExp: 96,
lines: [
'위연: 이번에는 길이 열렸소. 진창 앞에서 멈춘 답답함을 무도 고개에서 조금은 풀었습니다.',
'강유: 길이 열렸기에 더 조심해야 합니다. 한 번 열렸다고 다음 길도 같은 모양은 아닙니다.',
'위연: 좋소. 다음에는 그대가 멈출 선을 말하시오. 나는 그 선이 보일 때까지 먼저 들어가겠소.'
],
choices: [
{
id: 'mark-breakthrough-return-line',
label: '돌파 귀환선을 표시한다',
response: '강유는 위연의 돌파 지점마다 돌아올 표식을 그렸고, 위연은 그 표식이 공격을 늦추는 것이 아니라 살리는 것임을 받아들였습니다.',
rewardExp: 32
},
{
id: 'test-fast-pass-entry',
label: '빠른 고개 진입을 시험한다',
response: '위연은 빠르게 고개를 찌르는 훈련을 맡았고, 강유는 진입 뒤 본대와 다시 붙는 길을 함께 적었습니다.',
rewardExp: 30
}
]
},
{
id: 'northern-sixth-zhao-madai-cavalry',
title: '두 기병의 회수선',
availableAfterBattleIds: [campBattleIds.sixtieth],
unitIds: ['zhao-yun', 'ma-dai'],
bondId: 'zhao-yun__ma-dai_northern-sixth',
rewardExp: 92,
lines: [
'마대: 곽회의 기병은 성급하지 않았습니다. 물러나는 척하며 다시 물목을 노렸습니다.',
'조운: 그래서 기병은 이기는 속도보다 돌아오는 속도를 먼저 알아야 하오.',
'마대: 다음에는 우회로를 넓히기 전에 돌아올 신호부터 맞추겠습니다.'
],
choices: [
{
id: 'align-cavalry-recall-flags',
label: '기병 회수 깃발을 맞춘다',
response: '조운과 마대는 회수 깃발 순서를 맞추었고, 기병대는 깊이 들어가도 본대의 신호를 놓치지 않게 되었습니다.',
rewardExp: 31
},
{
id: 'map-yinping-flank',
label: '음평 우회로를 그린다',
response: '마대는 음평 물목의 우회로를 그렸고, 조운은 그 길마다 예비대가 붙을 자리를 표시했습니다.',
rewardExp: 29
}
]
},
{
id: 'northern-sixth-wangping-huangquan-ledger',
title: '산길과 장부',
availableAfterBattleIds: [campBattleIds.sixtieth],
unitIds: ['wang-ping', 'huang-quan'],
bondId: 'wang-ping__huang-quan_northern-sixth',
rewardExp: 90,
lines: [
'왕평: 두 고을을 얻었지만 산길 표식이 흐려지면 얻은 땅도 하루 만에 멀어집니다.',
'황권: 장부도 같습니다. 무도와 음평으로 나뉜 군량을 한 장부처럼 움직이게 해야 합니다.',
'왕평: 그러면 저는 고개마다 멈출 자리를 세우겠습니다. 황권 장군은 그 자리마다 며칠을 버틸 수 있는지 적어 주십시오.'
],
choices: [
{
id: 'pair-ridge-posts-ledgers',
label: '고개 표식과 장부를 묶는다',
response: '왕평의 고개 표식 옆에 황권의 군량 날짜가 붙었습니다. 병사들은 어디서 멈추고 얼마나 버틸지 한눈에 알게 되었습니다.',
rewardExp: 30
},
{
id: 'audit-two-commandery-grain',
label: '두 고을 군량을 대조한다',
response: '황권은 무도와 음평의 군량을 따로 세고, 왕평은 수레가 오갈 수 있는 고개만 골라 장부 옆에 표시했습니다.',
rewardExp: 28
}
]
}
];
@@ -9047,6 +9163,36 @@ const campVisits: CampVisitDefinition[] = [
itemRewards: ['콩 30', '상처약 20']
}
]
},
{
id: 'northern-sixth-commandery-ledger',
title: '무도·음평 장부막',
location: '무도 남쪽 산길 장부막',
availableAfterBattleIds: [campBattleIds.sixtieth],
bondId: 'wang-ping__huang-quan_northern-sixth',
description: '왕평과 황권이 무도와 음평의 산길 표식, 군량 수레 날짜, 마을 안심 약속을 하나의 북방 장부로 묶습니다.',
lines: [
'왕평: 여기서 고개 하나를 잘못 잡으면 무도와 음평이 서로를 돕지 못합니다.',
'황권: 그래서 군량 날짜를 고개 표식 옆에 붙였습니다. 길과 장부가 따로 움직이면 새 고을은 금세 부담이 됩니다.',
'장부막에는 두 고을의 수레 길, 마을 배분표, 다음 북벌 때 다시 열어야 할 산길이 함께 적혔습니다.'
],
choices: [
{
id: 'seal-two-commandery-ledger',
label: '두 고을 장부를 봉한다',
response: '왕평은 고개 표식을 확인했고, 황권은 두 고을 군량 장부를 봉해 다음 북벌 보급의 기준으로 남겼습니다.',
bondExp: 74,
itemRewards: ['무도·음평 장부 1']
},
{
id: 'assign-commandery-reassurance',
label: '마을 안심 약속을 붙인다',
response: '마량의 고시문이 장부막 앞에 붙었고, 황권은 약속한 배분을 실제 군량표에 반영했습니다.',
bondExp: 70,
gold: 2680,
itemRewards: ['콩 32', '상처약 22']
}
]
}
];
@@ -9154,7 +9300,8 @@ export class CampScene extends Phaser.Scene {
battleId === campBattleIds.fiftySixth ||
battleId === campBattleIds.fiftySeventh ||
battleId === campBattleIds.fiftyEighth ||
battleId === campBattleIds.fiftyNinth
battleId === campBattleIds.fiftyNinth ||
battleId === campBattleIds.sixtieth
) {
this.campaign = ensureCampaignRosterUnits(
[
@@ -9175,7 +9322,7 @@ export class CampScene extends Phaser.Scene {
...hanzhongOpeningRecruitUnits,
...hanzhongScoutRecruitUnits,
...hanzhongMainRecruitUnits,
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth ? jiangWeiRecruitUnits : [])
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth ? jiangWeiRecruitUnits : [])
],
[
...xuzhouRecruitBonds,
@@ -9215,10 +9362,11 @@ export class CampScene extends Phaser.Scene {
...mengHuoFinalCaptureBonds,
...northernFirstBattleBonds,
...northernSecondBattleBonds,
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth ? northernThirdBattleBonds : []),
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth ? jiangWeiRecruitBonds : []),
...(battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth ? northernFourthBattleBonds : []),
...(battleId === campBattleIds.fiftyNinth ? northernFifthBattleBonds : [])
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth ? northernThirdBattleBonds : []),
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth ? jiangWeiRecruitBonds : []),
...(battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth ? northernFourthBattleBonds : []),
...(battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth ? northernFifthBattleBonds : []),
...(battleId === campBattleIds.sixtieth ? northernSixthBattleBonds : [])
]
);
this.report = this.campaign.firstBattleReport ?? this.report;
@@ -9859,6 +10007,9 @@ export class CampScene extends Phaser.Scene {
if (this.campaign?.step === 'northern-campaign-prep-camp') {
return '북벌 준비 군영';
}
if (battleId === campBattleIds.sixtieth) {
return '무도·음평 확보 후 군영';
}
if (battleId === campBattleIds.fiftyNinth) {
return '진창 공성전 후 군영';
}

View File

@@ -15,6 +15,7 @@ import {
fiftySeventhBattleScenario,
fiftyEighthBattleScenario,
fiftyNinthBattleScenario,
sixtiethBattleScenario,
fortiethBattleScenario,
fortyFirstBattleScenario,
fortySecondBattleScenario,
@@ -418,6 +419,7 @@ export class TitleScene extends Phaser.Scene {
campaign.step === 'fifty-seventh-camp' ||
campaign.step === 'fifty-eighth-camp' ||
campaign.step === 'fifty-ninth-camp' ||
campaign.step === 'sixtieth-camp' ||
campaign.step === 'hanzhong-king-camp' ||
campaign.step === 'shu-han-foundation-camp' ||
campaign.step === 'baidi-entrustment-camp' ||
@@ -722,6 +724,11 @@ export class TitleScene extends Phaser.Scene {
return;
}
if (campaign.step === 'sixtieth-battle') {
this.scene.start('BattleScene', { battleId: sixtiethBattleScenario.id });
return;
}
if (campaign.step === 'first-victory-story') {
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
return;