Add Lucheng pursuit chapter

This commit is contained in:
2026-06-24 17:59:05 +09:00
parent 8c446f8948
commit 84a1eccb5d
12 changed files with 892 additions and 82 deletions

View File

@@ -6571,13 +6571,15 @@ export class BattleScene extends Phaser.Scene {
'northern-fifth',
'northern-sixth',
'northern-seventh',
'northern-eighth'
'northern-eighth',
'northern-ninth'
];
if (lateNorthernPrefixes.some((prefix) => enemy.id.startsWith(prefix))) {
if (
enemy.id.includes('-scout') ||
enemy.id.includes('-cavalry') ||
enemy.id === 'northern-eighth-officer-zhang-he'
enemy.id === 'northern-eighth-officer-zhang-he' ||
enemy.id === 'northern-ninth-officer-zhang-he'
) {
return 'aggressive';
}

View File

@@ -55,6 +55,7 @@ import {
northernFifthBattleBonds,
northernFourthBattleBonds,
northernFirstBattleBonds,
northernNinthBattleBonds,
northernSecondBattleBonds,
northernSeventhBattleBonds,
northernSixthBattleBonds,
@@ -449,9 +450,10 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
'fifty-ninth-battle-chencang-siege',
'sixtieth-battle-wudu-yinping',
'sixty-first-battle-hanzhong-rain-defense',
'sixty-second-battle-qishan-renewed-offensive'
'sixty-second-battle-qishan-renewed-offensive',
'sixty-third-battle-lucheng-pursuit'
],
nextHints: ['한중 창고 정비', '출전 무장 재편', '기산 출진로', '천수·가정 방면', '강유 합류', '진창 공성 재계산', '무도·음평 확보', '한중 우로 방어', '기산 재출정']
nextHints: ['한중 창고 정비', '출전 무장 재편', '기산 출진로', '천수·가정 방면', '강유 합류', '진창 공성 재계산', '무도·음평 확보', '한중 우로 방어', '기산 재출정', '노성 추격']
}
];
@@ -517,7 +519,8 @@ const campBattleIds = {
fiftyNinth: 'fifty-ninth-battle-chencang-siege',
sixtieth: 'sixtieth-battle-wudu-yinping',
sixtyFirst: 'sixty-first-battle-hanzhong-rain-defense',
sixtySecond: 'sixty-second-battle-qishan-renewed-offensive'
sixtySecond: 'sixty-second-battle-qishan-renewed-offensive',
sixtyThird: 'sixty-third-battle-lucheng-pursuit'
} as const;
const defaultRequiredSortieUnitIds = ['liu-bei'];
@@ -1301,6 +1304,23 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
{ unitId: 'ma-dai', reason: '서량 기병의 측면 감각으로 곽회의 차단로와 동쪽 물목을 견제합니다.' }
],
note: '기산 재출정전은 추천 무장이 9명입니다. 공격로를 열 장수, 돌아올 표식을 세울 장수, 후방 장부를 맡길 장수 중 둘을 반드시 군영에 남겨야 합니다.'
},
'sixty-third-battle-lucheng-pursuit': {
maxUnits: 7,
requiredUnitIds: ['zhuge-liang'],
excludedUnitIds: ['liu-bei'],
recommended: [
{ unitId: 'zhuge-liang', reason: '노성 추격로와 회수선, 보급 장부를 동시에 조율해야 하는 총지휘관입니다.' },
{ unitId: 'jiang-wei', reason: '낮은 길을 읽어 추격로가 함정이 되기 전에 먼저 방향을 잡습니다.' },
{ unitId: 'zhao-yun', reason: '장합 기병이 뒤를 물 때 예비대를 회수선으로 묶는 안정적 기동 담당입니다.' },
{ unitId: 'wei-yan', reason: '사마의가 물러난 틈을 놓치지 않고 노성 앞 방어선을 흔들 돌파 장수입니다.' },
{ unitId: 'wang-ping', reason: '회수 표식과 정지선을 세워 추격전이 퇴로 없는 돌파가 되지 않게 합니다.' },
{ unitId: 'huang-quan', reason: '추격로가 길어질수록 수레와 군량 장부를 전장 기준으로 맞춰야 합니다.' },
{ unitId: 'li-yan', reason: '운송 지연과 예비 수레 배정을 현장에서 감당할 수 있습니다. 데려가면 보급 안정성이 커집니다.' },
{ unitId: 'ma-liang', reason: '노성 주변 마을의 안심 약속과 고시문을 추격 전에 준비합니다.' },
{ unitId: 'ma-dai', reason: '서량 기병의 측면 감각으로 곽회의 봉화 차단로와 우회 기병을 견제합니다.' }
],
note: '노성 추격전은 추천 무장이 9명입니다. 빠른 추격, 안전한 회수, 수레 운송, 마을 안심 중 둘을 군영에 남기는 선택이 필요합니다.'
}
};
@@ -1399,6 +1419,23 @@ const sortieRulesByCampaignStep: Partial<Record<CampaignStep, SortieRuleDefiniti
{ unitId: 'ma-dai', reason: '곽회 차단로 대응과 서량 기병 우회 기준을 다음 측면 운용에 반영합니다.' }
],
note: '기산 재출정 이후도 엔딩이 아니라 더 깊어진 북방 전선 준비입니다. 이긴 길을 지킬 사람과 더 깊이 들어갈 사람을 나누어 보십시오.'
},
'sixty-third-camp': {
maxUnits: 7,
requiredUnitIds: ['zhuge-liang'],
excludedUnitIds: ['liu-bei'],
recommended: [
{ unitId: 'zhuge-liang', reason: '노성 추격 이후 얻은 길과 잃지 말아야 할 나라의 숨을 다시 계산해야 합니다.' },
{ unitId: 'jiang-wei', reason: '노성 낮은 길과 봉화 차단 경험을 다음 북벌 공격로로 정리합니다.' },
{ unitId: 'zhao-yun', reason: '회수선 훈련을 예비대 기준으로 남겨 넓어진 전선을 안전하게 묶습니다.' },
{ unitId: 'wei-yan', reason: '돌파가 성과를 낸 지점과 멈춰야 했던 지점을 다음 군령으로 남깁니다.' },
{ unitId: 'wang-ping', reason: '노성 표식과 정지선을 다음 진영 배치의 안전장치로 정리합니다.' },
{ unitId: 'huang-quan', reason: '추격전에서 소모된 군량과 남은 수레를 다음 출정 가능량으로 환산합니다.' },
{ unitId: 'li-yan', reason: '운송 지연과 예비 수레 배정을 후방 기준으로 다시 세웁니다.' },
{ unitId: 'ma-liang', reason: '노성 주변 마을의 고시문을 다음 북벌 명분과 실제 약속으로 이어 줍니다.' },
{ unitId: 'ma-dai', reason: '곽회 봉화로와 서량 기병 우회 기준을 다음 측면 운용에 반영합니다.' }
],
note: '노성 추격 이후도 엔딩이 아니라 더 깊은 북방 전선의 재정비입니다. 이긴 길을 지킬 사람과 더 나아갈 사람을 다시 나누어 보십시오.'
}
};
@@ -6673,6 +6710,87 @@ const campDialogues: CampDialogue[] = [
rewardExp: 30
}
]
},
{
id: 'northern-ninth-jiangwei-zhaoyun-return',
title: '낮은 길과 회수 깃발',
availableAfterBattleIds: [campBattleIds.sixtyThird],
unitIds: ['jiang-wei', 'zhao-yun'],
bondId: 'jiang-wei__zhao-yun_northern-ninth',
rewardExp: 104,
lines: [
'강유: 노성 뒤의 낮은 길은 빠르게 보였지만, 장합이 기다린 곳도 그 길이었습니다.',
'조운: 그래서 빠른 길에는 반드시 돌아올 깃발이 따라야 합니다. 앞선 부대가 보지 못한 뒤를 예비대가 보겠습니다.',
'강유: 다음 지도에는 길의 이름뿐 아니라 돌아오는 깃발의 순서까지 함께 적겠습니다.'
],
choices: [
{
id: 'bind-low-road-to-reserve-flag',
label: '낮은 길과 예비 깃발을 묶는다',
response: '강유는 낮은 길마다 표식을 새겼고, 조운은 예비대가 어느 깃발부터 움직일지 훈련 명령으로 남겼습니다.',
rewardExp: 36
},
{
id: 'test-return-road-drill',
label: '회수로 훈련을 다시 돈다',
response: '조운의 기동대가 일부러 늦게 물러났고, 강유는 병사들이 헷갈린 낮은 길을 지도에서 다시 고쳤습니다.',
rewardExp: 33
}
]
},
{
id: 'northern-ninth-weiyan-wangping-stopline',
title: '칼끝의 정지선',
availableAfterBattleIds: [campBattleIds.sixtyThird],
unitIds: ['wei-yan', 'wang-ping'],
bondId: 'wei-yan__wang-ping_northern-ninth',
rewardExp: 102,
lines: [
'위연: 이번에는 멈출 곳을 알았기에 더 세게 찌를 수 있었소.',
'왕평: 표식이 없었다면 돌파는 길이 아니라 절벽이 되었을 것입니다.',
'위연: 다음에도 선을 그어 주시오. 나는 그 선까지 가장 빠르게 가겠소.'
],
choices: [
{
id: 'paint-breakthrough-stopline',
label: '돌파 정지선을 지도에 칠한다',
response: '왕평은 노성 추격로의 정지선을 붉게 칠했고, 위연은 그 선을 넘을 때 필요한 별도 명령을 요구했습니다.',
rewardExp: 35
},
{
id: 'reserve-after-breakthrough',
label: '돌파 뒤 예비대를 붙인다',
response: '위연은 돌파 뒤 빈틈을 인정했고, 왕평은 그 뒤를 메울 예비대 위치를 군령에 더했습니다.',
rewardExp: 32
}
]
},
{
id: 'northern-ninth-huangquan-liyan-carts',
title: '장부와 수레의 속도',
availableAfterBattleIds: [campBattleIds.sixtyThird],
unitIds: ['huang-quan', 'li-yan'],
bondId: 'huang-quan__li-yan_northern-ninth',
rewardExp: 100,
lines: [
'황권: 추격이 빨라질수록 장부는 늦어집니다. 늦어진 장부는 결국 병사의 빈 그릇으로 돌아옵니다.',
'이엄: 수레가 늦은 곳을 숨기지 않겠습니다. 늦은 길을 알아야 다음 수레가 무리하지 않습니다.',
'황권: 그럼 이번 장부에는 승리한 길과 늦어진 길을 나란히 적겠습니다.'
],
choices: [
{
id: 'pair-ledger-with-cart-delay',
label: '장부와 수레 지연을 묶는다',
response: '황권은 군량 소모 옆에 이엄의 수레 지연 시간을 적었고, 두 사람은 다음 출정의 실제 속도를 다시 계산했습니다.',
rewardExp: 34
},
{
id: 'reserve-lucheng-cart-team',
label: '노성 예비 수레를 남긴다',
response: '이엄은 노성 주변에 예비 수레를 남겼고, 황권은 그 수레가 마을 몫을 침범하지 않도록 장부에 못박았습니다.',
rewardExp: 31
}
]
}
];
@@ -9489,6 +9607,36 @@ const campVisits: CampVisitDefinition[] = [
itemRewards: ['쌀 36', '상처약 26']
}
]
},
{
id: 'northern-ninth-lucheng-return-ledger',
title: '노성 회수 장부',
location: '노성 추격 군막',
availableAfterBattleIds: [campBattleIds.sixtyThird],
bondId: 'huang-quan__li-yan_northern-ninth',
description: '황권과 이엄이 노성 추격로에서 실제로 늦어진 수레, 남긴 군량, 다음 북벌의 속도를 하나의 장부로 맞춥니다.',
lines: [
'이엄: 이번에는 늦은 수레를 늦었다고 적었습니다. 숨긴 숫자는 다음 길에서 병사를 배신합니다.',
'황권: 좋습니다. 이 장부에는 이긴 길도, 늦어진 길도 같은 먹으로 남기겠습니다.',
'군막의 탁자에는 노성 추격로와 회수 표식, 예비 수레의 날짜가 한 장으로 묶였습니다.'
],
choices: [
{
id: 'seal-lucheng-pursuit-ledger',
label: '노성 추격 장부를 봉한다',
response: '황권은 노성 추격 장부를 봉했고, 이엄은 다음 출정의 수레 속도를 장부 첫 줄에 적었습니다.',
bondExp: 86,
itemRewards: ['노성 추격 장부 1']
},
{
id: 'reserve-return-road-supplies',
label: '회수로 예비 군량을 남긴다',
response: '이엄은 회수 표식마다 예비 군량을 남겼고, 황권은 그 군량이 마을 몫을 줄이지 않도록 다시 계산했습니다.',
bondExp: 80,
gold: 2940,
itemRewards: ['쌀 38', '상처약 28']
}
]
}
];
@@ -9599,7 +9747,8 @@ export class CampScene extends Phaser.Scene {
battleId === campBattleIds.fiftyNinth ||
battleId === campBattleIds.sixtieth ||
battleId === campBattleIds.sixtyFirst ||
battleId === campBattleIds.sixtySecond
battleId === campBattleIds.sixtySecond ||
battleId === campBattleIds.sixtyThird
) {
this.campaign = ensureCampaignRosterUnits(
[
@@ -9620,7 +9769,7 @@ export class CampScene extends Phaser.Scene {
...hanzhongOpeningRecruitUnits,
...hanzhongScoutRecruitUnits,
...hanzhongMainRecruitUnits,
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond ? jiangWeiRecruitUnits : [])
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? jiangWeiRecruitUnits : [])
],
[
...xuzhouRecruitBonds,
@@ -9660,13 +9809,14 @@ export class CampScene extends Phaser.Scene {
...mengHuoFinalCaptureBonds,
...northernFirstBattleBonds,
...northernSecondBattleBonds,
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond ? northernThirdBattleBonds : []),
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond ? jiangWeiRecruitBonds : []),
...(battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond ? northernFourthBattleBonds : []),
...(battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond ? northernFifthBattleBonds : []),
...(battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond ? northernSixthBattleBonds : []),
...(battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond ? northernSeventhBattleBonds : []),
...(battleId === campBattleIds.sixtySecond ? northernEighthBattleBonds : [])
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? northernThirdBattleBonds : []),
...(battleId === campBattleIds.fiftySeventh || battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? jiangWeiRecruitBonds : []),
...(battleId === campBattleIds.fiftyEighth || battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? northernFourthBattleBonds : []),
...(battleId === campBattleIds.fiftyNinth || battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? northernFifthBattleBonds : []),
...(battleId === campBattleIds.sixtieth || battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? northernSixthBattleBonds : []),
...(battleId === campBattleIds.sixtyFirst || battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? northernSeventhBattleBonds : []),
...(battleId === campBattleIds.sixtySecond || battleId === campBattleIds.sixtyThird ? northernEighthBattleBonds : []),
...(battleId === campBattleIds.sixtyThird ? northernNinthBattleBonds : [])
]
);
this.report = this.campaign.firstBattleReport ?? this.report;
@@ -10307,6 +10457,9 @@ export class CampScene extends Phaser.Scene {
if (this.campaign?.step === 'northern-campaign-prep-camp') {
return '북벌 준비 군영';
}
if (battleId === campBattleIds.sixtyThird) {
return '노성 추격 후 군영';
}
if (battleId === campBattleIds.sixtySecond) {
return '기산 재출정 후 군영';
}

View File

@@ -18,6 +18,7 @@ import {
sixtiethBattleScenario,
sixtyFirstBattleScenario,
sixtySecondBattleScenario,
sixtyThirdBattleScenario,
fortiethBattleScenario,
fortyFirstBattleScenario,
fortySecondBattleScenario,
@@ -424,6 +425,7 @@ export class TitleScene extends Phaser.Scene {
campaign.step === 'sixtieth-camp' ||
campaign.step === 'sixty-first-camp' ||
campaign.step === 'sixty-second-camp' ||
campaign.step === 'sixty-third-camp' ||
campaign.step === 'hanzhong-king-camp' ||
campaign.step === 'shu-han-foundation-camp' ||
campaign.step === 'baidi-entrustment-camp' ||
@@ -743,6 +745,11 @@ export class TitleScene extends Phaser.Scene {
return;
}
if (campaign.step === 'sixty-third-battle') {
this.scene.start('BattleScene', { battleId: sixtyThirdBattleScenario.id });
return;
}
if (campaign.step === 'first-victory-story') {
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
return;