Add Chengdu surrender chapter
This commit is contained in:
@@ -54,16 +54,17 @@ Build a small complete tactical RPG loop that can grow into a longer Romance of
|
|||||||
- Thirtieth battle Luofeng Slope ambush route, using Wu Yi's first sortie choice and Pang Tong survival pressure across a 60x42 ambush battlefield
|
- Thirtieth battle Luofeng Slope ambush route, using Wu Yi's first sortie choice and Pang Tong survival pressure across a 60x42 ambush battlefield
|
||||||
- Thirty-first battle Luo Castle main-gate route, turning the siege into a 62x44 castle assault with Zhang Ren's final defense and Yan Yan recruitment after the gate opens
|
- Thirty-first battle Luo Castle main-gate route, turning the siege into a 62x44 castle assault with Zhang Ren's final defense and Yan Yan recruitment after the gate opens
|
||||||
- Thirty-second battle Mianzhu Gate pressure route, using Yan Yan's first sortie choice across a 64x46 Chengdu approach battlefield and recruiting Li Yan after the defense line yields
|
- Thirty-second battle Mianzhu Gate pressure route, using Yan Yan's first sortie choice across a 64x46 Chengdu approach battlefield and recruiting Li Yan after the defense line yields
|
||||||
|
- Thirty-third battle Chengdu surrender route, using Li Yan's first sortie choice across a 66x48 Chengdu outer-wall battlefield and recruiting Huang Quan after Yi Province yields
|
||||||
- Camp progress timeline tab that summarizes Liu Bei's long campaign arc, completed battles, current chapter, latest battle, and next major chapter
|
- Camp progress timeline tab that summarizes Liu Bei's long campaign arc, completed battles, current chapter, latest battle, and next major chapter
|
||||||
- Tactical sortie preparation panel with battle-specific sortie limits, recommended officers with reasons, class role, named equipment, core stats, bond partner, next-map terrain suitability, deployment preview, formation roles, active bond count, recruited-officer count, reserve roster summary, and readiness advice for each deployable officer
|
- Tactical sortie preparation panel with battle-specific sortie limits, recommended officers with reasons, class role, named equipment, core stats, bond partner, next-map terrain suitability, deployment preview, formation roles, active bond count, recruited-officer count, reserve roster summary, and readiness advice for each deployable officer
|
||||||
- Officer collection support in camp, including full roster status, selected/reserve/recommended markers, and post-battle reserve training growth for benched officers
|
- Officer collection support in camp, including full roster status, selected/reserve/recommended markers, and post-battle reserve training growth for benched officers
|
||||||
- Treasure equipment effects wired into battle previews and resolution, including named weapon damage bonuses, defensive treasure mitigation, support recovery bonuses, turn-start charm recovery, and equipment-specific growth bonuses
|
- Treasure equipment effects wired into battle previews and resolution, including named weapon damage bonuses, defensive treasure mitigation, support recovery bonuses, turn-start charm recovery, and equipment-specific growth bonuses
|
||||||
- Flow verification script from title through the thirty-second battle victory, recruit sortie selection, Liu Biao visit rewards, Zhuge Liang recruitment, Bowang/Changban/Jiangdong/Red Cliffs/Jing Province/Guiyang/Wuling/Changsha/Yi Province/Fu Pass/Luo Castle/Luofeng/Luo main gate/Mianzhu camp states, campaign timeline state, and camp save state
|
- Flow verification script from title through the thirty-third battle victory, recruit sortie selection, Liu Biao visit rewards, Zhuge Liang recruitment, Bowang/Changban/Jiangdong/Red Cliffs/Jing Province/Guiyang/Wuling/Changsha/Yi Province/Fu Pass/Luo Castle/Luofeng/Luo main gate/Mianzhu/Chengdu camp states, campaign timeline state, and camp save state
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
1. Continue the Yi Province arc into the final Chengdu surrender and Shu-Han foundation setup, using Yan Yan and Li Yan's local legitimacy plus Pang Tong/Fa Zheng/Wu Yi planning pressure
|
1. Open the Shu-Han foundation setup after Yi Province, turning the new Chengdu camp into policy, title, and northward Hanzhong preparation choices
|
||||||
2. Add more recruitable officers as the campaign moves deeper through Yi Province and toward Shu Han
|
2. Add more recruitable officers as the campaign moves from Yi Province stabilization toward Hanzhong and Shu Han
|
||||||
3. Expand reserve training into explicit drill choices, class practice, and bond-focused camp assignments
|
3. Expand reserve training into explicit drill choices, class practice, and bond-focused camp assignments
|
||||||
4. Add a dedicated treasure/equipment management view so players can compare special effects and growth
|
4. Add a dedicated treasure/equipment management view so players can compare special effects and growth
|
||||||
5. Keep expanding scenarios through the long campaign instead of treating the current slice as an ending
|
5. Keep expanding scenarios through the long campaign instead of treating the current slice as an ending
|
||||||
|
|||||||
@@ -1736,7 +1736,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
progressTabState?.activeTab !== 'progress' ||
|
progressTabState?.activeTab !== 'progress' ||
|
||||||
progressTabState.campaignProgress?.completedKnown !== 17 ||
|
progressTabState.campaignProgress?.completedKnown !== 17 ||
|
||||||
progressTabState.campaignProgress?.totalKnown !== 32 ||
|
progressTabState.campaignProgress?.totalKnown !== 33 ||
|
||||||
progressTabState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
progressTabState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
||||||
progressTabState.campaignProgress?.latestBattleTitle !== '융중 방문로' ||
|
progressTabState.campaignProgress?.latestBattleTitle !== '융중 방문로' ||
|
||||||
progressTabState.campaignProgress?.nextBattleTitle !== '박망파 매복전'
|
progressTabState.campaignProgress?.nextBattleTitle !== '박망파 매복전'
|
||||||
@@ -1888,7 +1888,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postBowangProgressState?.activeTab !== 'progress' ||
|
postBowangProgressState?.activeTab !== 'progress' ||
|
||||||
postBowangProgressState.campaignProgress?.completedKnown !== 18 ||
|
postBowangProgressState.campaignProgress?.completedKnown !== 18 ||
|
||||||
postBowangProgressState.campaignProgress?.totalKnown !== 32 ||
|
postBowangProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postBowangProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
postBowangProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
||||||
postBowangProgressState.campaignProgress?.latestBattleTitle !== '박망파 매복전' ||
|
postBowangProgressState.campaignProgress?.latestBattleTitle !== '박망파 매복전' ||
|
||||||
postBowangProgressState.campaignProgress?.nextBattleTitle !== '장판파 피난로'
|
postBowangProgressState.campaignProgress?.nextBattleTitle !== '장판파 피난로'
|
||||||
@@ -2026,7 +2026,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postChangbanProgressState?.activeTab !== 'progress' ||
|
postChangbanProgressState?.activeTab !== 'progress' ||
|
||||||
postChangbanProgressState.campaignProgress?.completedKnown !== 19 ||
|
postChangbanProgressState.campaignProgress?.completedKnown !== 19 ||
|
||||||
postChangbanProgressState.campaignProgress?.totalKnown !== 32 ||
|
postChangbanProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postChangbanProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
postChangbanProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
||||||
postChangbanProgressState.campaignProgress?.latestBattleTitle !== '장판파 피난로' ||
|
postChangbanProgressState.campaignProgress?.latestBattleTitle !== '장판파 피난로' ||
|
||||||
postChangbanProgressState.campaignProgress?.nextBattleTitle !== '강동 사절로'
|
postChangbanProgressState.campaignProgress?.nextBattleTitle !== '강동 사절로'
|
||||||
@@ -2164,7 +2164,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postJiangdongProgressState?.activeTab !== 'progress' ||
|
postJiangdongProgressState?.activeTab !== 'progress' ||
|
||||||
postJiangdongProgressState.campaignProgress?.completedKnown !== 20 ||
|
postJiangdongProgressState.campaignProgress?.completedKnown !== 20 ||
|
||||||
postJiangdongProgressState.campaignProgress?.totalKnown !== 32 ||
|
postJiangdongProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postJiangdongProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
postJiangdongProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
||||||
postJiangdongProgressState.campaignProgress?.latestBattleTitle !== '강동 사절로' ||
|
postJiangdongProgressState.campaignProgress?.latestBattleTitle !== '강동 사절로' ||
|
||||||
postJiangdongProgressState.campaignProgress?.nextBattleTitle !== '적벽 전초전'
|
postJiangdongProgressState.campaignProgress?.nextBattleTitle !== '적벽 전초전'
|
||||||
@@ -2304,7 +2304,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postRedCliffsProgressState?.activeTab !== 'progress' ||
|
postRedCliffsProgressState?.activeTab !== 'progress' ||
|
||||||
postRedCliffsProgressState.campaignProgress?.completedKnown !== 21 ||
|
postRedCliffsProgressState.campaignProgress?.completedKnown !== 21 ||
|
||||||
postRedCliffsProgressState.campaignProgress?.totalKnown !== 32 ||
|
postRedCliffsProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postRedCliffsProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
postRedCliffsProgressState.campaignProgress?.activeChapter?.title !== '적벽대전' ||
|
||||||
postRedCliffsProgressState.campaignProgress?.latestBattleTitle !== '적벽 전초전' ||
|
postRedCliffsProgressState.campaignProgress?.latestBattleTitle !== '적벽 전초전' ||
|
||||||
postRedCliffsProgressState.campaignProgress?.nextBattleTitle !== '적벽 화공전'
|
postRedCliffsProgressState.campaignProgress?.nextBattleTitle !== '적벽 화공전'
|
||||||
@@ -2444,7 +2444,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postFireAttackProgressState?.activeTab !== 'progress' ||
|
postFireAttackProgressState?.activeTab !== 'progress' ||
|
||||||
postFireAttackProgressState.campaignProgress?.completedKnown !== 22 ||
|
postFireAttackProgressState.campaignProgress?.completedKnown !== 22 ||
|
||||||
postFireAttackProgressState.campaignProgress?.totalKnown !== 32 ||
|
postFireAttackProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postFireAttackProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postFireAttackProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postFireAttackProgressState.campaignProgress?.latestBattleTitle !== '적벽 화공전' ||
|
postFireAttackProgressState.campaignProgress?.latestBattleTitle !== '적벽 화공전' ||
|
||||||
postFireAttackProgressState.campaignProgress?.nextBattleTitle !== '형주 남부 진입전'
|
postFireAttackProgressState.campaignProgress?.nextBattleTitle !== '형주 남부 진입전'
|
||||||
@@ -2587,7 +2587,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postJingzhouProgressState?.activeTab !== 'progress' ||
|
postJingzhouProgressState?.activeTab !== 'progress' ||
|
||||||
postJingzhouProgressState.campaignProgress?.completedKnown !== 23 ||
|
postJingzhouProgressState.campaignProgress?.completedKnown !== 23 ||
|
||||||
postJingzhouProgressState.campaignProgress?.totalKnown !== 32 ||
|
postJingzhouProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postJingzhouProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postJingzhouProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postJingzhouProgressState.campaignProgress?.latestBattleTitle !== '형주 남부 진입전' ||
|
postJingzhouProgressState.campaignProgress?.latestBattleTitle !== '형주 남부 진입전' ||
|
||||||
postJingzhouProgressState.campaignProgress?.nextBattleTitle !== '계양 설득전'
|
postJingzhouProgressState.campaignProgress?.nextBattleTitle !== '계양 설득전'
|
||||||
@@ -2729,7 +2729,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postGuiyangProgressState?.activeTab !== 'progress' ||
|
postGuiyangProgressState?.activeTab !== 'progress' ||
|
||||||
postGuiyangProgressState.campaignProgress?.completedKnown !== 24 ||
|
postGuiyangProgressState.campaignProgress?.completedKnown !== 24 ||
|
||||||
postGuiyangProgressState.campaignProgress?.totalKnown !== 32 ||
|
postGuiyangProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postGuiyangProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postGuiyangProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postGuiyangProgressState.campaignProgress?.latestBattleTitle !== '계양 설득전' ||
|
postGuiyangProgressState.campaignProgress?.latestBattleTitle !== '계양 설득전' ||
|
||||||
postGuiyangProgressState.campaignProgress?.nextBattleTitle !== '무릉 산길 확보전'
|
postGuiyangProgressState.campaignProgress?.nextBattleTitle !== '무릉 산길 확보전'
|
||||||
@@ -2872,7 +2872,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postWulingProgressState?.activeTab !== 'progress' ||
|
postWulingProgressState?.activeTab !== 'progress' ||
|
||||||
postWulingProgressState.campaignProgress?.completedKnown !== 25 ||
|
postWulingProgressState.campaignProgress?.completedKnown !== 25 ||
|
||||||
postWulingProgressState.campaignProgress?.totalKnown !== 32 ||
|
postWulingProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postWulingProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postWulingProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postWulingProgressState.campaignProgress?.latestBattleTitle !== '무릉 산길 확보전' ||
|
postWulingProgressState.campaignProgress?.latestBattleTitle !== '무릉 산길 확보전' ||
|
||||||
postWulingProgressState.campaignProgress?.nextBattleTitle !== '장사 노장 대면전'
|
postWulingProgressState.campaignProgress?.nextBattleTitle !== '장사 노장 대면전'
|
||||||
@@ -3021,7 +3021,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postChangshaProgressState?.activeTab !== 'progress' ||
|
postChangshaProgressState?.activeTab !== 'progress' ||
|
||||||
postChangshaProgressState.campaignProgress?.completedKnown !== 26 ||
|
postChangshaProgressState.campaignProgress?.completedKnown !== 26 ||
|
||||||
postChangshaProgressState.campaignProgress?.totalKnown !== 32 ||
|
postChangshaProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postChangshaProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postChangshaProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postChangshaProgressState.campaignProgress?.latestBattleTitle !== '장사 노장 대면전' ||
|
postChangshaProgressState.campaignProgress?.latestBattleTitle !== '장사 노장 대면전' ||
|
||||||
postChangshaProgressState.campaignProgress?.nextBattleTitle !== '익주 원군로'
|
postChangshaProgressState.campaignProgress?.nextBattleTitle !== '익주 원군로'
|
||||||
@@ -3166,7 +3166,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postYizhouProgressState?.activeTab !== 'progress' ||
|
postYizhouProgressState?.activeTab !== 'progress' ||
|
||||||
postYizhouProgressState.campaignProgress?.completedKnown !== 27 ||
|
postYizhouProgressState.campaignProgress?.completedKnown !== 27 ||
|
||||||
postYizhouProgressState.campaignProgress?.totalKnown !== 32 ||
|
postYizhouProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postYizhouProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postYizhouProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postYizhouProgressState.campaignProgress?.latestBattleTitle !== '익주 원군로' ||
|
postYizhouProgressState.campaignProgress?.latestBattleTitle !== '익주 원군로' ||
|
||||||
postYizhouProgressState.campaignProgress?.nextBattleTitle !== '부수관 진입전'
|
postYizhouProgressState.campaignProgress?.nextBattleTitle !== '부수관 진입전'
|
||||||
@@ -3313,7 +3313,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postFuPassProgressState?.activeTab !== 'progress' ||
|
postFuPassProgressState?.activeTab !== 'progress' ||
|
||||||
postFuPassProgressState.campaignProgress?.completedKnown !== 28 ||
|
postFuPassProgressState.campaignProgress?.completedKnown !== 28 ||
|
||||||
postFuPassProgressState.campaignProgress?.totalKnown !== 32 ||
|
postFuPassProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postFuPassProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postFuPassProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postFuPassProgressState.campaignProgress?.latestBattleTitle !== '부수관 진입전' ||
|
postFuPassProgressState.campaignProgress?.latestBattleTitle !== '부수관 진입전' ||
|
||||||
postFuPassProgressState.campaignProgress?.nextBattleTitle !== '낙성 외곽전'
|
postFuPassProgressState.campaignProgress?.nextBattleTitle !== '낙성 외곽전'
|
||||||
@@ -3462,7 +3462,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postLuoProgressState?.activeTab !== 'progress' ||
|
postLuoProgressState?.activeTab !== 'progress' ||
|
||||||
postLuoProgressState.campaignProgress?.completedKnown !== 29 ||
|
postLuoProgressState.campaignProgress?.completedKnown !== 29 ||
|
||||||
postLuoProgressState.campaignProgress?.totalKnown !== 32 ||
|
postLuoProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postLuoProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postLuoProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postLuoProgressState.campaignProgress?.latestBattleTitle !== '낙성 외곽전' ||
|
postLuoProgressState.campaignProgress?.latestBattleTitle !== '낙성 외곽전' ||
|
||||||
postLuoProgressState.campaignProgress?.nextBattleTitle !== '낙봉파 매복전'
|
postLuoProgressState.campaignProgress?.nextBattleTitle !== '낙봉파 매복전'
|
||||||
@@ -3608,7 +3608,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postLuofengProgressState?.activeTab !== 'progress' ||
|
postLuofengProgressState?.activeTab !== 'progress' ||
|
||||||
postLuofengProgressState.campaignProgress?.completedKnown !== 30 ||
|
postLuofengProgressState.campaignProgress?.completedKnown !== 30 ||
|
||||||
postLuofengProgressState.campaignProgress?.totalKnown !== 32 ||
|
postLuofengProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postLuofengProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postLuofengProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postLuofengProgressState.campaignProgress?.latestBattleTitle !== '낙봉파 매복전' ||
|
postLuofengProgressState.campaignProgress?.latestBattleTitle !== '낙봉파 매복전' ||
|
||||||
postLuofengProgressState.campaignProgress?.nextBattleTitle !== '낙성 본성 공략'
|
postLuofengProgressState.campaignProgress?.nextBattleTitle !== '낙성 본성 공략'
|
||||||
@@ -3760,7 +3760,7 @@ try {
|
|||||||
if (
|
if (
|
||||||
postLuoMainProgressState?.activeTab !== 'progress' ||
|
postLuoMainProgressState?.activeTab !== 'progress' ||
|
||||||
postLuoMainProgressState.campaignProgress?.completedKnown !== 31 ||
|
postLuoMainProgressState.campaignProgress?.completedKnown !== 31 ||
|
||||||
postLuoMainProgressState.campaignProgress?.totalKnown !== 32 ||
|
postLuoMainProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postLuoMainProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postLuoMainProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postLuoMainProgressState.campaignProgress?.latestBattleTitle !== '낙성 본성 공략' ||
|
postLuoMainProgressState.campaignProgress?.latestBattleTitle !== '낙성 본성 공략' ||
|
||||||
postLuoMainProgressState.campaignProgress?.nextBattleTitle !== '면죽관 압박전'
|
postLuoMainProgressState.campaignProgress?.nextBattleTitle !== '면죽관 압박전'
|
||||||
@@ -3913,15 +3913,169 @@ try {
|
|||||||
if (
|
if (
|
||||||
postMianzhuProgressState?.activeTab !== 'progress' ||
|
postMianzhuProgressState?.activeTab !== 'progress' ||
|
||||||
postMianzhuProgressState.campaignProgress?.completedKnown !== 32 ||
|
postMianzhuProgressState.campaignProgress?.completedKnown !== 32 ||
|
||||||
postMianzhuProgressState.campaignProgress?.totalKnown !== 32 ||
|
postMianzhuProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
postMianzhuProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postMianzhuProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postMianzhuProgressState.campaignProgress?.latestBattleTitle !== '면죽관 압박전' ||
|
postMianzhuProgressState.campaignProgress?.latestBattleTitle !== '면죽관 압박전' ||
|
||||||
postMianzhuProgressState.campaignProgress?.nextBattleTitle !== '준비 중'
|
postMianzhuProgressState.campaignProgress?.nextBattleTitle !== '성도 항복 권고전'
|
||||||
) {
|
) {
|
||||||
throw new Error(`Expected post-Mianzhu progress tab to complete Mianzhu pressure and pause before Chengdu surrender: ${JSON.stringify(postMianzhuProgressState?.campaignProgress)}`);
|
throw new Error(`Expected post-Mianzhu progress tab to complete Mianzhu pressure and open Chengdu surrender: ${JSON.stringify(postMianzhuProgressState?.campaignProgress)}`);
|
||||||
}
|
}
|
||||||
await page.screenshot({ path: 'dist/verification-post-mianzhu-progress.png', fullPage: true });
|
await page.screenshot({ path: 'dist/verification-post-mianzhu-progress.png', fullPage: true });
|
||||||
|
|
||||||
|
await page.mouse.click(1120, 38);
|
||||||
|
await page.waitForTimeout(180);
|
||||||
|
const chengduSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
!chengduSortieState?.sortieVisible ||
|
||||||
|
!chengduSortieState.sortiePlan?.objectiveLine?.includes('성도 항복 권고전') ||
|
||||||
|
!chengduSortieState.sortieRoster?.some((unit) => unit.id === 'li-yan' && unit.recruited && unit.recommended) ||
|
||||||
|
!chengduSortieState.sortieRoster?.some((unit) => unit.id === 'zhuge-liang' && unit.recruited && unit.recommended) ||
|
||||||
|
!chengduSortieState.sortieRoster?.some((unit) => unit.id === 'fa-zheng' && unit.recruited && unit.recommended) ||
|
||||||
|
!chengduSortieState.sortieRoster?.some((unit) => unit.id === 'yan-yan' && unit.recruited && unit.recommended) ||
|
||||||
|
!chengduSortieState.sortieRoster?.some((unit) => unit.id === 'wu-yi' && unit.recruited && unit.recommended) ||
|
||||||
|
chengduSortieState.sortieRoster?.some((unit) => unit.id === 'huang-quan') ||
|
||||||
|
chengduSortieState.sortieRoster?.length < 18 ||
|
||||||
|
chengduSortieState.sortiePlan?.maxCount !== 6
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected Mianzhu camp sortie prep to target Chengdu surrender with Li Yan selectable and Huang Quan absent before victory: ${JSON.stringify(chengduSortieState)}`);
|
||||||
|
}
|
||||||
|
assertSortieTacticalRoster(chengduSortieState, [
|
||||||
|
'liu-bei',
|
||||||
|
'guan-yu',
|
||||||
|
'zhang-fei',
|
||||||
|
'jian-yong',
|
||||||
|
'mi-zhu',
|
||||||
|
'sun-qian',
|
||||||
|
'zhao-yun',
|
||||||
|
'zhuge-liang',
|
||||||
|
'ma-liang',
|
||||||
|
'yi-ji',
|
||||||
|
'gong-zhi',
|
||||||
|
'huang-zhong',
|
||||||
|
'wei-yan',
|
||||||
|
'pang-tong',
|
||||||
|
'fa-zheng',
|
||||||
|
'wu-yi',
|
||||||
|
'yan-yan',
|
||||||
|
'li-yan'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const chengduPriorityUnits = ['li-yan', 'zhuge-liang', 'fa-zheng', 'yan-yan', 'wu-yi'];
|
||||||
|
for (const unitId of chengduPriorityUnits) {
|
||||||
|
const currentSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (!currentSortieState.sortieRoster?.some((unit) => unit.id === unitId && unit.selected)) {
|
||||||
|
const removable = currentSortieState.sortieRoster?.find(
|
||||||
|
(unit) => unit.selected && unit.id !== 'liu-bei' && !chengduPriorityUnits.includes(unit.id)
|
||||||
|
);
|
||||||
|
if (removable) {
|
||||||
|
await clickSortieRosterUnit(page, removable.id);
|
||||||
|
}
|
||||||
|
await clickSortieRosterUnit(page, unitId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const chengduSortieReadyState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
!chengduPriorityUnits.every((unitId) =>
|
||||||
|
chengduSortieReadyState.sortieRoster?.some((unit) => unit.id === unitId && unit.selected)
|
||||||
|
) ||
|
||||||
|
chengduSortieReadyState.sortiePlan?.selectedCount !== 6 ||
|
||||||
|
chengduSortieReadyState.sortiePlan?.recommendedSelectedCount < 6
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected Chengdu surrender sortie to deploy Li Yan, Zhuge Liang, Fa Zheng, Yan Yan, and Wu Yi while preserving six-officer pressure: ${JSON.stringify(chengduSortieReadyState)}`);
|
||||||
|
}
|
||||||
|
await page.screenshot({ path: 'dist/verification-chengdu-sortie.png', fullPage: true });
|
||||||
|
|
||||||
|
await page.mouse.click(1068, 646);
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
||||||
|
return activeScenes.includes('StoryScene');
|
||||||
|
});
|
||||||
|
await page.screenshot({ path: 'dist/verification-chengdu-story.png', fullPage: true });
|
||||||
|
|
||||||
|
for (let i = 0; i < 50; i += 1) {
|
||||||
|
const enteredThirtyThirdBattle = await page.evaluate(() => {
|
||||||
|
const state = window.__HEROS_DEBUG__?.battle();
|
||||||
|
return state?.scene === 'BattleScene' && state?.battleId === 'thirty-third-battle-chengdu-surrender';
|
||||||
|
});
|
||||||
|
if (enteredThirtyThirdBattle) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await page.keyboard.press('Space');
|
||||||
|
await page.waitForTimeout(320);
|
||||||
|
}
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const state = window.__HEROS_DEBUG__?.battle();
|
||||||
|
return state?.scene === 'BattleScene' && state?.battleId === 'thirty-third-battle-chengdu-surrender' && state?.battleOutcome === null && state?.phase === 'idle';
|
||||||
|
});
|
||||||
|
await page.screenshot({ path: 'dist/verification-thirty-third-battle.png', fullPage: true });
|
||||||
|
|
||||||
|
const thirtyThirdBattleState = await page.evaluate(() => window.__HEROS_DEBUG__?.battle());
|
||||||
|
const thirtyThirdEnemies = thirtyThirdBattleState.units.filter((unit) => unit.faction === 'enemy');
|
||||||
|
const thirtyThirdAllies = thirtyThirdBattleState.units.filter((unit) => unit.faction === 'ally');
|
||||||
|
const thirtyThirdEnemyBehaviors = new Set(thirtyThirdEnemies.map((unit) => unit.ai));
|
||||||
|
if (
|
||||||
|
thirtyThirdBattleState.camera?.mapWidth !== 66 ||
|
||||||
|
thirtyThirdBattleState.camera?.mapHeight !== 48 ||
|
||||||
|
thirtyThirdBattleState.victoryConditionLabel !== '황권 방어선 격파' ||
|
||||||
|
thirtyThirdEnemies.length < 26 ||
|
||||||
|
!thirtyThirdEnemyBehaviors.has('aggressive') ||
|
||||||
|
!thirtyThirdEnemyBehaviors.has('guard') ||
|
||||||
|
!thirtyThirdEnemyBehaviors.has('hold') ||
|
||||||
|
!thirtyThirdEnemies.some((unit) => unit.id === 'chengdu-final-leader-huang-quan') ||
|
||||||
|
!thirtyThirdEnemies.some((unit) => unit.id === 'chengdu-final-officer-a') ||
|
||||||
|
!thirtyThirdAllies.some((unit) => unit.id === 'li-yan') ||
|
||||||
|
!thirtyThirdAllies.some((unit) => unit.id === 'zhuge-liang') ||
|
||||||
|
!thirtyThirdAllies.some((unit) => unit.id === 'fa-zheng') ||
|
||||||
|
!thirtyThirdAllies.some((unit) => unit.id === 'yan-yan') ||
|
||||||
|
!thirtyThirdAllies.some((unit) => unit.id === 'wu-yi') ||
|
||||||
|
thirtyThirdAllies.some((unit) => unit.id === 'huang-quan')
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected thirty-third battle to use Chengdu surrender map, Huang Quan objective, Li Yan first sortie, selected allied officers, and mixed AI: ${JSON.stringify(thirtyThirdBattleState)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.evaluate(() => window.__HEROS_DEBUG__?.forceBattleOutcome('victory'));
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const state = window.__HEROS_DEBUG__?.battle();
|
||||||
|
return state?.battleOutcome === 'victory' && state?.phase === 'resolved' && state?.resultVisible === true;
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.mouse.click(738, 642);
|
||||||
|
await page.waitForFunction(() => {
|
||||||
|
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
||||||
|
return activeScenes.includes('CampScene');
|
||||||
|
});
|
||||||
|
|
||||||
|
const thirtyThirdCampState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
thirtyThirdCampState?.campBattleId !== 'thirty-third-battle-chengdu-surrender' ||
|
||||||
|
thirtyThirdCampState.campTitle !== '성도 항복 후 군영' ||
|
||||||
|
thirtyThirdCampState.availableDialogueIds?.length !== 3 ||
|
||||||
|
!thirtyThirdCampState.availableDialogueIds.every((id) => id.endsWith('chengdu')) ||
|
||||||
|
thirtyThirdCampState.availableVisitIds?.length !== 2 ||
|
||||||
|
!thirtyThirdCampState.campaign?.roster?.some((unit) => unit.id === 'huang-quan') ||
|
||||||
|
!thirtyThirdCampState.sortieRoster?.some((unit) => unit.id === 'huang-quan' && unit.recruited) ||
|
||||||
|
thirtyThirdCampState.rosterCollection?.total < 19
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected thirty-third camp to recruit Huang Quan and expose Chengdu dialogue/visit sets: ${JSON.stringify(thirtyThirdCampState)}`);
|
||||||
|
}
|
||||||
|
await page.screenshot({ path: 'dist/verification-chengdu-camp.png', fullPage: true });
|
||||||
|
|
||||||
|
await page.mouse.click(966, 38);
|
||||||
|
await page.waitForTimeout(180);
|
||||||
|
const postChengduProgressState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
postChengduProgressState?.activeTab !== 'progress' ||
|
||||||
|
postChengduProgressState.campaignProgress?.completedKnown !== 33 ||
|
||||||
|
postChengduProgressState.campaignProgress?.totalKnown !== 33 ||
|
||||||
|
postChengduProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
|
postChengduProgressState.campaignProgress?.latestBattleTitle !== '성도 항복 권고전' ||
|
||||||
|
postChengduProgressState.campaignProgress?.nextBattleTitle !== '준비 중'
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected post-Chengdu progress tab to complete Yi Province capture and pause before Shu-Han foundation: ${JSON.stringify(postChengduProgressState?.campaignProgress)}`);
|
||||||
|
}
|
||||||
|
await page.screenshot({ path: 'dist/verification-post-chengdu-progress.png', fullPage: true });
|
||||||
|
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
const game = window.__HEROS_GAME__;
|
const game = window.__HEROS_GAME__;
|
||||||
game?.scene.stop('CampScene');
|
game?.scene.stop('CampScene');
|
||||||
@@ -3940,7 +4094,7 @@ try {
|
|||||||
return activeScenes.includes('CampScene');
|
return activeScenes.includes('CampScene');
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Verified title-to-thirty-second-battle flow, recruited officer sortie selection, Ma Liang, Yi Ji, Gong Zhi, Huang Zhong, Wei Yan, Pang Tong, Fa Zheng, Wu Yi, Yan Yan, Li Yan joins, Luofeng, Luo main gate, and Mianzhu battles, result states, and debug API at ${targetUrl}`);
|
console.log(`Verified title-to-thirty-third-battle flow, recruited officer sortie selection, Ma Liang, Yi Ji, Gong Zhi, Huang Zhong, Wei Yan, Pang Tong, Fa Zheng, Wu Yi, Yan Yan, Li Yan, Huang Quan joins, Luofeng, Luo main gate, Mianzhu, and Chengdu surrender battles, result states, and debug API at ${targetUrl}`);
|
||||||
} finally {
|
} finally {
|
||||||
await browser?.close();
|
await browser?.close();
|
||||||
if (serverProcess && !serverProcess.killed) {
|
if (serverProcess && !serverProcess.killed) {
|
||||||
|
|||||||
85
src/assets/images/battle/thirty-third-battle-map.svg
Normal file
85
src/assets/images/battle/thirty-third-battle-map.svg
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6600 4800" width="6600" height="4800">
|
||||||
|
<defs>
|
||||||
|
<filter id="grain" x="-10%" y="-10%" width="120%" height="120%">
|
||||||
|
<feTurbulence type="fractalNoise" baseFrequency="0.014 0.022" numOctaves="5" seed="33"/>
|
||||||
|
<feColorMatrix type="saturate" values="0.36"/>
|
||||||
|
<feBlend mode="multiply" in2="SourceGraphic"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="earth" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#6f7651"/>
|
||||||
|
<stop offset="0.46" stop-color="#a08458"/>
|
||||||
|
<stop offset="1" stop-color="#455a44"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="wall" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#c2a77c"/>
|
||||||
|
<stop offset="0.52" stop-color="#76634d"/>
|
||||||
|
<stop offset="1" stop-color="#2f302b"/>
|
||||||
|
</linearGradient>
|
||||||
|
<pattern id="fieldDots" width="78" height="78" patternUnits="userSpaceOnUse">
|
||||||
|
<circle cx="12" cy="18" r="7" fill="#355a36" opacity="0.25"/>
|
||||||
|
<circle cx="54" cy="55" r="5" fill="#c8a46f" opacity="0.35"/>
|
||||||
|
<circle cx="64" cy="22" r="4" fill="#283f2f" opacity="0.2"/>
|
||||||
|
</pattern>
|
||||||
|
<pattern id="masonry" width="190" height="126" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M0 63H190M95 0V63M48 63v63M142 63v63" stroke="#231e19" stroke-width="10" opacity="0.28"/>
|
||||||
|
<path d="M0 0H190V126H0Z" fill="none" stroke="#e0c08c" stroke-width="8" opacity="0.14"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
<rect width="6600" height="4800" fill="url(#earth)"/>
|
||||||
|
<rect width="6600" height="4800" fill="url(#fieldDots)" opacity="0.66"/>
|
||||||
|
<g filter="url(#grain)">
|
||||||
|
<path d="M0 0H1340C1170 710 1130 1500 1260 2150C1380 2740 1060 3470 520 4040H0Z" fill="#24392d" opacity="0.73"/>
|
||||||
|
<path d="M2240 0H3540C3310 690 3260 1330 3400 1900C3540 2490 3240 3160 2660 3640C2380 3880 2300 4300 2290 4800H1380C1680 4240 1880 3610 1930 2990C1990 2180 1780 1300 2240 0Z" fill="#405437" opacity="0.53"/>
|
||||||
|
<path d="M3700 3540C4250 3220 4850 3140 5510 3290C6040 3410 6370 3220 6600 2950V4800H3700Z" fill="#304a31" opacity="0.6"/>
|
||||||
|
<path d="M0 4290C830 4020 1580 3940 2320 4090C2970 4220 3560 4070 4100 3740L4380 4800H0Z" fill="#56683d" opacity="0.43"/>
|
||||||
|
|
||||||
|
<path d="M40 4390C820 4120 1560 3970 2260 3780C3060 3560 3680 3220 4320 2800C4920 2400 5540 2100 6560 1920" fill="none" stroke="#b99a67" stroke-width="270" opacity="0.84"/>
|
||||||
|
<path d="M470 4400C1210 4150 1930 3980 2600 3720C3390 3410 4020 2960 4620 2580C5160 2220 5740 1990 6540 1840" fill="none" stroke="#6d563c" stroke-width="74" opacity="0.32"/>
|
||||||
|
<path d="M1700 3910C1980 3640 2190 3360 2310 3020C2440 2630 2740 2400 3220 2330C3710 2260 4050 2040 4340 1650" fill="none" stroke="#b79865" stroke-width="150" opacity="0.48"/>
|
||||||
|
|
||||||
|
<path d="M1460 0C1620 740 1580 1450 1430 2150C1300 2800 1370 3380 1640 3890" fill="none" stroke="#2c6687" stroke-width="180" opacity="0.74"/>
|
||||||
|
<path d="M3650 420C3500 1130 3540 1860 3720 2590C3860 3140 3770 3630 3540 4140" fill="none" stroke="#2c6687" stroke-width="155" opacity="0.68"/>
|
||||||
|
<path d="M4800 3500C5000 3920 5090 4380 5040 4800" fill="none" stroke="#2c6687" stroke-width="160" opacity="0.66"/>
|
||||||
|
|
||||||
|
<g opacity="0.92">
|
||||||
|
<path d="M4740 720L5550 460L6450 790L6550 2840L6020 3200L5080 3050L4580 2460L4560 1060Z" fill="url(#wall)"/>
|
||||||
|
<path d="M4740 720L5550 460L6450 790L6550 2840L6020 3200L5080 3050L4580 2460L4560 1060Z" fill="url(#masonry)" opacity="0.78"/>
|
||||||
|
<path d="M4920 1010L5560 820L6220 1070V2560L5850 2810L5210 2700L4860 2320V1220Z" fill="#3f342b" opacity="0.76"/>
|
||||||
|
<path d="M5320 2580H5940V3180H5320Z" fill="#2d2b29"/>
|
||||||
|
<path d="M5320 2580H5940V3180H5320Z" fill="url(#masonry)" opacity="0.57"/>
|
||||||
|
<path d="M4640 1290H4930M4630 1680H4920M4680 2170H4990M6230 1280H6570M6200 1780H6570M6150 2330H6510" stroke="#d7bd8a" stroke-width="58" stroke-linecap="round" opacity="0.58"/>
|
||||||
|
<path d="M5480 2760C5620 2640 5780 2640 5940 2760V3190H5480Z" fill="#211b17" opacity="0.86"/>
|
||||||
|
<path d="M5050 880L5250 720L5450 880M5920 910L6150 740L6400 930" fill="none" stroke="#d2b476" stroke-width="60" stroke-linecap="round" opacity="0.54"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g opacity="0.65">
|
||||||
|
<rect x="1780" y="3820" width="410" height="270" fill="#8c6a42"/>
|
||||||
|
<path d="M1720 3820L1980 3610L2250 3820Z" fill="#5d432e"/>
|
||||||
|
<rect x="3620" y="3280" width="330" height="230" fill="#8c6a42"/>
|
||||||
|
<path d="M3570 3280L3790 3110L4010 3280Z" fill="#5d432e"/>
|
||||||
|
<rect x="4620" y="2780" width="330" height="240" fill="#8c6a42"/>
|
||||||
|
<path d="M4570 2780L4790 2600L5010 2780Z" fill="#5d432e"/>
|
||||||
|
<rect x="5720" y="3100" width="390" height="260" fill="#947248"/>
|
||||||
|
<path d="M5660 3100L5910 2900L6170 3100Z" fill="#604631"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g opacity="0.5">
|
||||||
|
<circle cx="720" cy="680" r="190" fill="#1d2e24"/>
|
||||||
|
<circle cx="1040" cy="1010" r="230" fill="#233a2a"/>
|
||||||
|
<circle cx="760" cy="1580" r="240" fill="#1f3327"/>
|
||||||
|
<circle cx="2360" cy="1160" r="260" fill="#273923"/>
|
||||||
|
<circle cx="2840" cy="1810" r="230" fill="#33472d"/>
|
||||||
|
<circle cx="4100" cy="3730" r="280" fill="#263b2b"/>
|
||||||
|
<circle cx="5480" cy="3690" r="270" fill="#263b2b"/>
|
||||||
|
<circle cx="6240" cy="3560" r="240" fill="#203223"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g opacity="0.42">
|
||||||
|
<path d="M0 0H1120V180H0Z" fill="#26231f"/>
|
||||||
|
<path d="M2320 0H3370V180H2320Z" fill="#26231f"/>
|
||||||
|
<path d="M3660 0H5200V150H3660Z" fill="#26231f"/>
|
||||||
|
<path d="M6330 0H6600V1460H6330Z" fill="#24211e"/>
|
||||||
|
<path d="M0 0H200V3460H0Z" fill="#24211e"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.4 KiB |
@@ -51,6 +51,10 @@ import {
|
|||||||
thirtySecondBattleMap,
|
thirtySecondBattleMap,
|
||||||
thirtySecondBattleUnits,
|
thirtySecondBattleUnits,
|
||||||
thirtySecondBattleVictoryPages,
|
thirtySecondBattleVictoryPages,
|
||||||
|
thirtyThirdBattleBonds,
|
||||||
|
thirtyThirdBattleMap,
|
||||||
|
thirtyThirdBattleUnits,
|
||||||
|
thirtyThirdBattleVictoryPages,
|
||||||
thirteenthBattleBonds,
|
thirteenthBattleBonds,
|
||||||
thirteenthBattleMap,
|
thirteenthBattleMap,
|
||||||
thirteenthBattleUnits,
|
thirteenthBattleUnits,
|
||||||
@@ -165,7 +169,8 @@ export type BattleScenarioId =
|
|||||||
| 'twenty-ninth-battle-luo-outer-wall'
|
| 'twenty-ninth-battle-luo-outer-wall'
|
||||||
| 'thirtieth-battle-luofeng-ambush'
|
| 'thirtieth-battle-luofeng-ambush'
|
||||||
| 'thirty-first-battle-luo-main-gate'
|
| 'thirty-first-battle-luo-main-gate'
|
||||||
| 'thirty-second-battle-mianzhu-gate';
|
| 'thirty-second-battle-mianzhu-gate'
|
||||||
|
| 'thirty-third-battle-chengdu-surrender';
|
||||||
|
|
||||||
export type BattleObjectiveKind = 'defeat-leader' | 'keep-unit-alive' | 'secure-terrain' | 'quick-victory';
|
export type BattleObjectiveKind = 'defeat-leader' | 'keep-unit-alive' | 'secure-terrain' | 'quick-victory';
|
||||||
|
|
||||||
@@ -2041,6 +2046,69 @@ export const thirtySecondBattleScenario: BattleScenarioDefinition = {
|
|||||||
nextCampScene: 'CampScene'
|
nextCampScene: 'CampScene'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const thirtyThirdBattleScenario: BattleScenarioDefinition = {
|
||||||
|
id: 'thirty-third-battle-chengdu-surrender',
|
||||||
|
title: '성도 항복 권고전',
|
||||||
|
victoryConditionLabel: '황권 방어선 격파',
|
||||||
|
defeatConditionLabel: '유비 또는 이엄 퇴각',
|
||||||
|
openingObjectiveLines: [
|
||||||
|
'성도 외곽의 마지막 방어선입니다. 황권을 물리치면 유장의 항복 권고가 실제 선택지로 바뀝니다.',
|
||||||
|
'이엄은 방금 합류한 익주 장수로, 이번 전장에서 유비군의 군율을 증언하는 핵심 인물입니다. 이엄을 반드시 지키십시오.',
|
||||||
|
'성도 창고와 마을을 안정시키면 항복 조건에 대한 신뢰가 커지고, 익주 확보 이후의 수습이 쉬워집니다.'
|
||||||
|
],
|
||||||
|
map: thirtyThirdBattleMap,
|
||||||
|
units: thirtyThirdBattleUnits,
|
||||||
|
bonds: thirtyThirdBattleBonds,
|
||||||
|
mapTextureKey: 'battle-map-thirty-third',
|
||||||
|
leaderUnitId: 'chengdu-final-leader-huang-quan',
|
||||||
|
quickVictoryTurnLimit: 48,
|
||||||
|
baseVictoryGold: 5340,
|
||||||
|
objectives: [
|
||||||
|
{
|
||||||
|
id: 'leader',
|
||||||
|
kind: 'defeat-leader',
|
||||||
|
label: '황권 방어선 격파',
|
||||||
|
rewardGold: 3360,
|
||||||
|
unitId: 'chengdu-final-leader-huang-quan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'liu-bei',
|
||||||
|
kind: 'keep-unit-alive',
|
||||||
|
label: '유비 생존',
|
||||||
|
rewardGold: 980,
|
||||||
|
unitId: 'liu-bei'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'li-yan',
|
||||||
|
kind: 'keep-unit-alive',
|
||||||
|
label: '이엄 생존',
|
||||||
|
rewardGold: 1320,
|
||||||
|
unitId: 'li-yan'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'chengdu-storehouse',
|
||||||
|
kind: 'secure-terrain',
|
||||||
|
label: '성도 창고 확보',
|
||||||
|
rewardGold: 1620,
|
||||||
|
terrain: 'village'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'quick',
|
||||||
|
kind: 'quick-victory',
|
||||||
|
label: '48턴 이내 승리',
|
||||||
|
rewardGold: 1240,
|
||||||
|
maxTurn: 48
|
||||||
|
}
|
||||||
|
],
|
||||||
|
defeatConditions: [
|
||||||
|
{ kind: 'unit-defeated', unitId: 'liu-bei' },
|
||||||
|
{ kind: 'unit-defeated', unitId: 'li-yan' }
|
||||||
|
],
|
||||||
|
itemRewards: ['콩 20', '상처약 15', '탁주 8', '익주 안정문 +1', '황권 합류'],
|
||||||
|
victoryPages: thirtyThirdBattleVictoryPages,
|
||||||
|
nextCampScene: 'CampScene'
|
||||||
|
};
|
||||||
|
|
||||||
export const defaultBattleScenarioId: BattleScenarioId = firstBattleScenario.id;
|
export const defaultBattleScenarioId: BattleScenarioId = firstBattleScenario.id;
|
||||||
|
|
||||||
export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition> = {
|
export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition> = {
|
||||||
@@ -2075,7 +2143,8 @@ export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition>
|
|||||||
'twenty-ninth-battle-luo-outer-wall': twentyNinthBattleScenario,
|
'twenty-ninth-battle-luo-outer-wall': twentyNinthBattleScenario,
|
||||||
'thirtieth-battle-luofeng-ambush': thirtiethBattleScenario,
|
'thirtieth-battle-luofeng-ambush': thirtiethBattleScenario,
|
||||||
'thirty-first-battle-luo-main-gate': thirtyFirstBattleScenario,
|
'thirty-first-battle-luo-main-gate': thirtyFirstBattleScenario,
|
||||||
'thirty-second-battle-mianzhu-gate': thirtySecondBattleScenario
|
'thirty-second-battle-mianzhu-gate': thirtySecondBattleScenario,
|
||||||
|
'thirty-third-battle-chengdu-surrender': thirtyThirdBattleScenario
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultBattleScenario = battleScenarios[defaultBattleScenarioId];
|
export const defaultBattleScenario = battleScenarios[defaultBattleScenarioId];
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
thirteenthBattleScenario,
|
thirteenthBattleScenario,
|
||||||
thirtyFirstBattleScenario,
|
thirtyFirstBattleScenario,
|
||||||
thirtySecondBattleScenario,
|
thirtySecondBattleScenario,
|
||||||
|
thirtyThirdBattleScenario,
|
||||||
thirtiethBattleScenario,
|
thirtiethBattleScenario,
|
||||||
twentyEighthBattleScenario,
|
twentyEighthBattleScenario,
|
||||||
twentyNinthBattleScenario,
|
twentyNinthBattleScenario,
|
||||||
@@ -72,6 +73,8 @@ import {
|
|||||||
thirtyFirstBattleVictoryPages,
|
thirtyFirstBattleVictoryPages,
|
||||||
thirtySecondBattleIntroPages,
|
thirtySecondBattleIntroPages,
|
||||||
thirtySecondBattleVictoryPages,
|
thirtySecondBattleVictoryPages,
|
||||||
|
thirtyThirdBattleIntroPages,
|
||||||
|
thirtyThirdBattleVictoryPages,
|
||||||
thirtiethBattleIntroPages,
|
thirtiethBattleIntroPages,
|
||||||
thirtiethBattleVictoryPages,
|
thirtiethBattleVictoryPages,
|
||||||
twentyEighthBattleIntroPages,
|
twentyEighthBattleIntroPages,
|
||||||
@@ -445,13 +448,24 @@ const sortieFlows: Record<string, SortieFlow> = {
|
|||||||
},
|
},
|
||||||
[thirtySecondBattleScenario.id]: {
|
[thirtySecondBattleScenario.id]: {
|
||||||
afterBattleId: thirtySecondBattleScenario.id,
|
afterBattleId: thirtySecondBattleScenario.id,
|
||||||
eyebrow: '다음 장 준비',
|
eyebrow: '다음 전장',
|
||||||
title: '성도 항복 권고 준비',
|
title: thirtyThirdBattleScenario.title,
|
||||||
description:
|
description:
|
||||||
'면죽관이 열리고 이엄이 합류했습니다. 이제 성도 포위와 유장의 항복 권고, 그리고 익주 통치의 첫 선택을 준비해야 합니다.',
|
'면죽관이 열리고 이엄이 합류했습니다. 이제 이엄의 증언과 법정·제갈량의 권고문을 앞세워 성도 외곽의 마지막 방어선을 눌러야 합니다.',
|
||||||
rewardHint: '다음 장: 성도 항복 권고 준비 중',
|
rewardHint: `예상 보상: ${thirtyThirdBattleScenario.title} 개방 / 황권 합류`,
|
||||||
pages: thirtySecondBattleVictoryPages,
|
nextBattleId: thirtyThirdBattleScenario.id,
|
||||||
unavailableNotice: '성도 항복 권고와 익주 최종 수습은 다음 작업에서 이어집니다. 새로 합류한 이엄을 포함해 보급과 내정형 무장 조합을 준비하십시오.'
|
campaignStep: 'thirty-third-battle',
|
||||||
|
pages: [...thirtySecondBattleVictoryPages, ...thirtyThirdBattleIntroPages]
|
||||||
|
},
|
||||||
|
[thirtyThirdBattleScenario.id]: {
|
||||||
|
afterBattleId: thirtyThirdBattleScenario.id,
|
||||||
|
eyebrow: '다음 장 준비',
|
||||||
|
title: '촉한 건국 준비',
|
||||||
|
description:
|
||||||
|
'성도가 항복하고 황권이 합류했습니다. 형주와 익주의 기반이 마련되었으니, 다음 장은 나라의 틀을 세우고 한중과 촉한 건국으로 향하는 준비입니다.',
|
||||||
|
rewardHint: '다음 장: 촉한 건국 준비 중',
|
||||||
|
pages: thirtyThirdBattleVictoryPages,
|
||||||
|
unavailableNotice: '촉한 건국과 한중 방면 전개는 다음 작업에서 이어집니다. 새로 합류한 황권을 포함해 내정과 방어형 무장 조합을 준비하십시오.'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2076,6 +2076,68 @@ export const thirtySecondBattleVictoryPages: StoryPage[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const thirtyThirdBattleIntroPages: StoryPage[] = [
|
||||||
|
{
|
||||||
|
id: 'thirty-third-chengdu-encircled',
|
||||||
|
bgm: 'story-dark',
|
||||||
|
chapter: '성도 앞의 마지막 권고',
|
||||||
|
background: 'story-three-heroes',
|
||||||
|
speaker: '제갈량',
|
||||||
|
text: '면죽관이 열렸고 이엄까지 합류했습니다. 성도는 아직 문을 닫았으나, 이제 유장은 싸움과 항복 사이에서 더 이상 오래 머물 수 없습니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'thirty-third-li-yan-counsel',
|
||||||
|
bgm: 'battle-prep',
|
||||||
|
chapter: '이엄의 증언',
|
||||||
|
background: 'story-liu-bei',
|
||||||
|
speaker: '이엄',
|
||||||
|
text: '황권은 성도의 체면과 백성의 안위를 함께 보는 장수입니다. 그가 물러서면 유장공도 백성을 살리는 길을 택할 수 있습니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'thirty-third-final-plan',
|
||||||
|
bgm: 'battle-prep',
|
||||||
|
chapter: '성도 항복 권고전',
|
||||||
|
background: 'story-militia',
|
||||||
|
speaker: '법정',
|
||||||
|
text: '성도 성문을 부수는 것이 능사가 아닙니다. 황권의 방어선을 눌러 항복 조건을 받아들이게 하고, 마을과 창고를 지켜 유비군의 약속을 증명해야 합니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'thirty-third-sortie',
|
||||||
|
bgm: 'battle-prep',
|
||||||
|
chapter: '익주를 얻는 길',
|
||||||
|
background: 'story-sortie',
|
||||||
|
speaker: '유비',
|
||||||
|
portrait: 'liuBei',
|
||||||
|
text: '오늘의 승리는 성도의 문을 여는 것보다, 문 안의 백성이 두려움을 내려놓게 하는 데 있소. 칼을 들되 마음을 먼저 잃지 마시오.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const thirtyThirdBattleVictoryPages: StoryPage[] = [
|
||||||
|
{
|
||||||
|
id: 'thirty-third-victory-chengdu-yields',
|
||||||
|
bgm: 'militia-theme',
|
||||||
|
chapter: '성도 항복',
|
||||||
|
background: 'story-sortie',
|
||||||
|
text: '성도 외곽의 방어선은 무너졌지만 성문 안쪽은 불타지 않았습니다. 유장에게 보내진 마지막 권고문은 백성을 보전하고 장수의 체면을 남기는 조건을 담았습니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'thirty-third-huang-quan-joins',
|
||||||
|
bgm: 'battle-prep',
|
||||||
|
chapter: '황권의 귀순',
|
||||||
|
background: 'story-liu-bei',
|
||||||
|
speaker: '황권',
|
||||||
|
text: '익주를 지키려던 마음은 변하지 않았습니다. 다만 이제는 백성을 살리는 길이 유비공의 군율 아래 있다는 것을 보았습니다. 황권이 그 길에 서겠습니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'thirty-third-yizhou-secured',
|
||||||
|
bgm: 'story-dark',
|
||||||
|
chapter: '익주 확보',
|
||||||
|
background: 'story-three-heroes',
|
||||||
|
speaker: '제갈량',
|
||||||
|
text: '형주와 익주의 기반이 마련되었습니다. 유비군은 이제 유랑군이 아니라 나라의 뿌리를 갖춘 세력으로 바뀌며, 촉한 건국을 향한 다음 장을 준비합니다.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export const firstBattleMap: BattleMap = {
|
export const firstBattleMap: BattleMap = {
|
||||||
width: 20,
|
width: 20,
|
||||||
height: 18,
|
height: 18,
|
||||||
@@ -2413,6 +2475,12 @@ export const thirtySecondBattleMap: BattleMap = {
|
|||||||
terrain: createThirtySecondBattleTerrain()
|
terrain: createThirtySecondBattleTerrain()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const thirtyThirdBattleMap: BattleMap = {
|
||||||
|
width: 66,
|
||||||
|
height: 48,
|
||||||
|
terrain: createThirtyThirdBattleTerrain()
|
||||||
|
};
|
||||||
|
|
||||||
export const firstBattleUnits: UnitData[] = [
|
export const firstBattleUnits: UnitData[] = [
|
||||||
{
|
{
|
||||||
id: 'liu-bei',
|
id: 'liu-bei',
|
||||||
@@ -4606,6 +4674,30 @@ export const chengduPressureRecruitUnits: UnitData[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const chengduSurrenderRecruitUnits: UnitData[] = [
|
||||||
|
{
|
||||||
|
id: 'huang-quan',
|
||||||
|
name: '황권',
|
||||||
|
faction: 'ally',
|
||||||
|
className: '익주 참모',
|
||||||
|
classKey: 'strategist',
|
||||||
|
level: 21,
|
||||||
|
exp: 0,
|
||||||
|
hp: 38,
|
||||||
|
maxHp: 38,
|
||||||
|
attack: 13,
|
||||||
|
move: 4,
|
||||||
|
stats: { might: 67, intelligence: 86, leadership: 82, agility: 64, luck: 74 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'white-feather-fan', level: 3, exp: 18 },
|
||||||
|
armor: { itemId: 'oath-robe', level: 3, exp: 8 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 2, exp: 12 }
|
||||||
|
},
|
||||||
|
x: 12,
|
||||||
|
y: 30
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const eighthBattleAllyPositions: Record<string, { x: number; y: number }> = {
|
const eighthBattleAllyPositions: Record<string, { x: number; y: number }> = {
|
||||||
'liu-bei': { x: 2, y: 17 },
|
'liu-bei': { x: 2, y: 17 },
|
||||||
'guan-yu': { x: 3, y: 17 },
|
'guan-yu': { x: 3, y: 17 },
|
||||||
@@ -13193,6 +13285,72 @@ export const thirtySecondBattleUnits: UnitData[] = [
|
|||||||
createThirtyFirstEnemyUnit('chengdu-leader-li-yan', '이엄', '면죽관 수비장', 'rebelLeader', 61, 306, 86, 60, 18)
|
createThirtyFirstEnemyUnit('chengdu-leader-li-yan', '이엄', '면죽관 수비장', 'rebelLeader', 61, 306, 86, 60, 18)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const thirtyThirdBattleAllyPositions: Record<string, { x: number; y: number }> = {
|
||||||
|
'liu-bei': { x: 4, y: 42 },
|
||||||
|
'guan-yu': { x: 5, y: 40 },
|
||||||
|
'zhang-fei': { x: 4, y: 44 },
|
||||||
|
'jian-yong': { x: 3, y: 45 },
|
||||||
|
'mi-zhu': { x: 5, y: 46 },
|
||||||
|
'sun-qian': { x: 7, y: 45 },
|
||||||
|
'zhao-yun': { x: 8, y: 41 },
|
||||||
|
'zhuge-liang': { x: 6, y: 40 },
|
||||||
|
'ma-liang': { x: 8, y: 46 },
|
||||||
|
'yi-ji': { x: 9, y: 43 },
|
||||||
|
'gong-zhi': { x: 10, y: 46 },
|
||||||
|
'huang-zhong': { x: 7, y: 42 },
|
||||||
|
'wei-yan': { x: 10, y: 40 },
|
||||||
|
'pang-tong': { x: 9, y: 41 },
|
||||||
|
'fa-zheng': { x: 11, y: 43 },
|
||||||
|
'wu-yi': { x: 12, y: 41 },
|
||||||
|
'yan-yan': { x: 11, y: 39 },
|
||||||
|
'li-yan': { x: 13, y: 40 }
|
||||||
|
};
|
||||||
|
|
||||||
|
export const thirtyThirdBattleUnits: UnitData[] = [
|
||||||
|
...[
|
||||||
|
...firstBattleUnits.filter((unit) => unit.faction === 'ally'),
|
||||||
|
...xuzhouRecruitUnits,
|
||||||
|
...caoBreakRecruitUnits,
|
||||||
|
...liuBiaoRecruitUnits,
|
||||||
|
...zhugeRecruitUnits,
|
||||||
|
...jingzhouRecruitUnits,
|
||||||
|
...guiyangRecruitUnits,
|
||||||
|
...wulingRecruitUnits,
|
||||||
|
...changshaRecruitUnits,
|
||||||
|
...yizhouRecruitUnits,
|
||||||
|
...fuPassRecruitUnits,
|
||||||
|
...luoCastleRecruitUnits,
|
||||||
|
...luoCastleProperRecruitUnits,
|
||||||
|
...chengduPressureRecruitUnits
|
||||||
|
].map((unit) => placeScenarioUnit(unit, thirtyThirdBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-scout-a', '성도 척후', '남문 척후', 'bandit', 60, 182, 66, 19, 41),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-scout-b', '성도 척후', '서문 척후', 'bandit', 60, 182, 66, 25, 39),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-scout-c', '성도 척후', '수로 척후', 'bandit', 61, 190, 68, 32, 37),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-infantry-a', '성도 보병', '외곽 보병', 'yellowTurban', 61, 266, 82, 31, 34),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-infantry-b', '성도 보병', '외곽 보병', 'yellowTurban', 61, 266, 82, 36, 33),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-infantry-c', '성도 보병', '남문 보병', 'yellowTurban', 62, 276, 84, 43, 28),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-infantry-d', '성도 보병', '남문 보병', 'yellowTurban', 62, 276, 84, 47, 27),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-infantry-e', '유장 친위대', '성문 친위대', 'yellowTurban', 63, 288, 86, 55, 23),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-infantry-f', '유장 친위대', '창고 친위대', 'yellowTurban', 63, 288, 86, 60, 25),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-archer-a', '성도 궁병', '성벽 궁병', 'archer', 61, 182, 66, 34, 31),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-archer-b', '성도 궁병', '남문 궁병', 'archer', 62, 190, 68, 46, 23),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-archer-c', '성도 궁병', '고지 궁병', 'archer', 62, 190, 68, 52, 18),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-archer-d', '성도 궁병', '창고 궁병', 'archer', 63, 198, 70, 61, 20),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-cavalry-a', '성도 기병', '우익 기병', 'cavalry', 62, 276, 86, 28, 43),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-cavalry-b', '성도 기병', '좌익 기병', 'cavalry', 62, 276, 86, 43, 38),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-cavalry-c', '성도 기병', '내성 기병', 'cavalry', 63, 288, 88, 58, 31),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-guard-a', '황권 친위대', '권고 반대파', 'yellowTurban', 63, 300, 88, 50, 22),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-guard-b', '황권 친위대', '권고 반대파', 'yellowTurban', 63, 300, 88, 54, 21),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-guard-c', '황권 친위대', '성도 친위대', 'yellowTurban', 64, 312, 90, 60, 18),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-strategist-a', '성도 책사', '항복 반대파', 'strategist', 62, 190, 68, 48, 19),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-strategist-b', '성도 책사', '유장 사자', 'strategist', 63, 198, 70, 57, 16),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-ambusher-a', '성도 복병', '남벽 복병', 'bandit', 61, 190, 70, 37, 41),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-ambusher-b', '성도 복병', '동문 복병', 'bandit', 61, 190, 70, 51, 35),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-ambusher-c', '성도 복병', '북벽 복병', 'bandit', 62, 198, 72, 62, 12),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-officer-a', '성도 부장', '남문 부장', 'rebelLeader', 64, 318, 92, 58, 22),
|
||||||
|
createThirtyFirstEnemyUnit('chengdu-final-leader-huang-quan', '황권', '성도 수비 참모', 'rebelLeader', 65, 336, 94, 62, 18)
|
||||||
|
];
|
||||||
|
|
||||||
export const firstBattleBonds: BattleBond[] = [
|
export const firstBattleBonds: BattleBond[] = [
|
||||||
{
|
{
|
||||||
id: 'liu-bei__guan-yu',
|
id: 'liu-bei__guan-yu',
|
||||||
@@ -13571,6 +13729,33 @@ export const chengduPressureRecruitBonds: BattleBond[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const chengduSurrenderRecruitBonds: BattleBond[] = [
|
||||||
|
{
|
||||||
|
id: 'liu-bei__huang-quan',
|
||||||
|
unitIds: ['liu-bei', 'huang-quan'],
|
||||||
|
title: '항복과 보전',
|
||||||
|
level: 31,
|
||||||
|
exp: 0,
|
||||||
|
description: '유비의 보전 약속과 황권의 현실적인 판단이 맞닿아, 성도 항복 뒤 백성과 장수의 불안을 낮춥니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'zhuge-liang__huang-quan',
|
||||||
|
unitIds: ['zhuge-liang', 'huang-quan'],
|
||||||
|
title: '성도 수습책',
|
||||||
|
level: 29,
|
||||||
|
exp: 0,
|
||||||
|
description: '제갈량의 큰 내정 구상과 황권의 현지 행정 감각이 만나, 익주 통치의 첫 질서를 세웁니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fa-zheng__huang-quan',
|
||||||
|
unitIds: ['fa-zheng', 'huang-quan'],
|
||||||
|
title: '익주 책사들의 균형',
|
||||||
|
level: 28,
|
||||||
|
exp: 0,
|
||||||
|
description: '법정의 결단력과 황권의 신중함이 서로를 보완하며, 익주 장수들을 다루는 말길을 넓힙니다.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export const secondBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
|
export const secondBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
|
||||||
export const thirdBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
|
export const thirdBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
|
||||||
export const fourthBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
|
export const fourthBattleBonds: BattleBond[] = firstBattleBonds.map(cloneBattleBondForScenario);
|
||||||
@@ -13610,6 +13795,9 @@ export const thirtyFirstBattleBonds: BattleBond[] = [...thirtiethBattleBonds].ma
|
|||||||
export const thirtySecondBattleBonds: BattleBond[] = [...thirtyFirstBattleBonds, ...luoCastleProperRecruitBonds].map(
|
export const thirtySecondBattleBonds: BattleBond[] = [...thirtyFirstBattleBonds, ...luoCastleProperRecruitBonds].map(
|
||||||
cloneBattleBondForScenario
|
cloneBattleBondForScenario
|
||||||
);
|
);
|
||||||
|
export const thirtyThirdBattleBonds: BattleBond[] = [...thirtySecondBattleBonds, ...chengduPressureRecruitBonds].map(
|
||||||
|
cloneBattleBondForScenario
|
||||||
|
);
|
||||||
|
|
||||||
function createEighthBattleTerrain(): TerrainType[][] {
|
function createEighthBattleTerrain(): TerrainType[][] {
|
||||||
return Array.from({ length: 22 }, (_, y) =>
|
return Array.from({ length: 22 }, (_, y) =>
|
||||||
@@ -14937,6 +15125,72 @@ function createThirtySecondBattleTerrain(): TerrainType[][] {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createThirtyThirdBattleTerrain(): TerrainType[][] {
|
||||||
|
return Array.from({ length: 48 }, (_, y) =>
|
||||||
|
Array.from({ length: 66 }, (_, x): TerrainType => {
|
||||||
|
if (x <= 4 && y >= 41) {
|
||||||
|
return 'camp';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(x >= 50 && x <= 64 && y >= 10 && y <= 27) ||
|
||||||
|
(x >= 46 && x <= 56 && y >= 7 && y <= 18) ||
|
||||||
|
(x >= 56 && x <= 65 && y >= 18 && y <= 32) ||
|
||||||
|
(x >= 53 && x <= 61 && y >= 29 && y <= 35)
|
||||||
|
) {
|
||||||
|
return 'fort';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(x >= 18 && x <= 22 && y >= 39 && y <= 41) ||
|
||||||
|
(x >= 36 && x <= 39 && y >= 33 && y <= 35) ||
|
||||||
|
(x >= 46 && x <= 48 && y >= 28 && y <= 30) ||
|
||||||
|
(x >= 57 && x <= 60 && y >= 31 && y <= 34)
|
||||||
|
) {
|
||||||
|
return 'village';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(y >= 43 && x >= 4 && x <= 20) ||
|
||||||
|
(x >= 19 && x <= 32 && y >= 39 && y <= 43) ||
|
||||||
|
(x >= 31 && x <= 44 && y >= 34 && y <= 39) ||
|
||||||
|
(x >= 43 && x <= 55 && y >= 28 && y <= 34) ||
|
||||||
|
(x >= 54 && x <= 63 && y >= 22 && y <= 28) ||
|
||||||
|
(x >= 59 && x <= 65 && y >= 19 && y <= 23)
|
||||||
|
) {
|
||||||
|
return 'road';
|
||||||
|
}
|
||||||
|
if ((x === 15 || x === 16) && y >= 0 && y <= 37) {
|
||||||
|
return 'river';
|
||||||
|
}
|
||||||
|
if ((x === 36 || x === 37) && y >= 4 && y <= 32) {
|
||||||
|
return 'river';
|
||||||
|
}
|
||||||
|
if ((x === 48 || x === 49) && y >= 34 && y <= 47) {
|
||||||
|
return 'river';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(x >= 4 && x <= 22 && y >= 8 && y <= 32) ||
|
||||||
|
(x >= 23 && x <= 35 && y >= 14 && y <= 33) ||
|
||||||
|
(x >= 38 && x <= 52 && y >= 35 && y <= 47) ||
|
||||||
|
(x >= 51 && x <= 65 && y >= 31 && y <= 46)
|
||||||
|
) {
|
||||||
|
return 'forest';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(x >= 0 && x <= 13 && y <= 17) ||
|
||||||
|
(x >= 23 && x <= 35 && y <= 13) ||
|
||||||
|
(x >= 37 && x <= 54 && y >= 2 && y <= 14) ||
|
||||||
|
(x >= 57 && x <= 65 && y <= 12) ||
|
||||||
|
(x >= 27 && x <= 40 && y >= 40 && y <= 47)
|
||||||
|
) {
|
||||||
|
return 'hill';
|
||||||
|
}
|
||||||
|
if ((x <= 2 && y <= 40) || (x >= 64 && y >= 28) || (x >= 50 && x <= 53 && y <= 8)) {
|
||||||
|
return 'cliff';
|
||||||
|
}
|
||||||
|
return 'plain';
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
|
function placeScenarioUnit(unit: UnitData, position: { x: number; y: number }): UnitData {
|
||||||
return {
|
return {
|
||||||
...cloneUnitForScenario(unit),
|
...cloneUnitForScenario(unit),
|
||||||
|
|||||||
@@ -454,7 +454,34 @@ const unitTexture: Record<string, string> = {
|
|||||||
'chengdu-ambusher-b': 'unit-rebel',
|
'chengdu-ambusher-b': 'unit-rebel',
|
||||||
'chengdu-ambusher-c': 'unit-rebel',
|
'chengdu-ambusher-c': 'unit-rebel',
|
||||||
'chengdu-officer-fei-guan': 'unit-rebel-leader',
|
'chengdu-officer-fei-guan': 'unit-rebel-leader',
|
||||||
'chengdu-leader-li-yan': 'unit-rebel-leader'
|
'chengdu-leader-li-yan': 'unit-rebel-leader',
|
||||||
|
'huang-quan': 'unit-liu-bei',
|
||||||
|
'chengdu-final-scout-a': 'unit-rebel',
|
||||||
|
'chengdu-final-scout-b': 'unit-rebel',
|
||||||
|
'chengdu-final-scout-c': 'unit-rebel',
|
||||||
|
'chengdu-final-infantry-a': 'unit-rebel',
|
||||||
|
'chengdu-final-infantry-b': 'unit-rebel',
|
||||||
|
'chengdu-final-infantry-c': 'unit-rebel',
|
||||||
|
'chengdu-final-infantry-d': 'unit-rebel',
|
||||||
|
'chengdu-final-infantry-e': 'unit-rebel',
|
||||||
|
'chengdu-final-infantry-f': 'unit-rebel',
|
||||||
|
'chengdu-final-archer-a': 'unit-rebel-archer',
|
||||||
|
'chengdu-final-archer-b': 'unit-rebel-archer',
|
||||||
|
'chengdu-final-archer-c': 'unit-rebel-archer',
|
||||||
|
'chengdu-final-archer-d': 'unit-rebel-archer',
|
||||||
|
'chengdu-final-cavalry-a': 'unit-rebel-cavalry',
|
||||||
|
'chengdu-final-cavalry-b': 'unit-rebel-cavalry',
|
||||||
|
'chengdu-final-cavalry-c': 'unit-rebel-cavalry',
|
||||||
|
'chengdu-final-guard-a': 'unit-rebel',
|
||||||
|
'chengdu-final-guard-b': 'unit-rebel',
|
||||||
|
'chengdu-final-guard-c': 'unit-rebel',
|
||||||
|
'chengdu-final-strategist-a': 'unit-rebel-archer',
|
||||||
|
'chengdu-final-strategist-b': 'unit-rebel-archer',
|
||||||
|
'chengdu-final-ambusher-a': 'unit-rebel',
|
||||||
|
'chengdu-final-ambusher-b': 'unit-rebel',
|
||||||
|
'chengdu-final-ambusher-c': 'unit-rebel',
|
||||||
|
'chengdu-final-officer-a': 'unit-rebel-leader',
|
||||||
|
'chengdu-final-leader-huang-quan': 'unit-rebel-leader'
|
||||||
};
|
};
|
||||||
|
|
||||||
const unitTextureByClass: Partial<Record<UnitClassKey, string>> = {
|
const unitTextureByClass: Partial<Record<UnitClassKey, string>> = {
|
||||||
@@ -1370,7 +1397,33 @@ const enemyAiByUnitId: Record<string, EnemyAiBehavior> = {
|
|||||||
'chengdu-ambusher-b': 'aggressive',
|
'chengdu-ambusher-b': 'aggressive',
|
||||||
'chengdu-ambusher-c': 'aggressive',
|
'chengdu-ambusher-c': 'aggressive',
|
||||||
'chengdu-officer-fei-guan': 'guard',
|
'chengdu-officer-fei-guan': 'guard',
|
||||||
'chengdu-leader-li-yan': 'guard'
|
'chengdu-leader-li-yan': 'guard',
|
||||||
|
'chengdu-final-scout-a': 'aggressive',
|
||||||
|
'chengdu-final-scout-b': 'aggressive',
|
||||||
|
'chengdu-final-scout-c': 'aggressive',
|
||||||
|
'chengdu-final-infantry-a': 'guard',
|
||||||
|
'chengdu-final-infantry-b': 'guard',
|
||||||
|
'chengdu-final-infantry-c': 'guard',
|
||||||
|
'chengdu-final-infantry-d': 'guard',
|
||||||
|
'chengdu-final-infantry-e': 'guard',
|
||||||
|
'chengdu-final-infantry-f': 'guard',
|
||||||
|
'chengdu-final-archer-a': 'hold',
|
||||||
|
'chengdu-final-archer-b': 'hold',
|
||||||
|
'chengdu-final-archer-c': 'hold',
|
||||||
|
'chengdu-final-archer-d': 'hold',
|
||||||
|
'chengdu-final-cavalry-a': 'aggressive',
|
||||||
|
'chengdu-final-cavalry-b': 'aggressive',
|
||||||
|
'chengdu-final-cavalry-c': 'aggressive',
|
||||||
|
'chengdu-final-guard-a': 'guard',
|
||||||
|
'chengdu-final-guard-b': 'guard',
|
||||||
|
'chengdu-final-guard-c': 'guard',
|
||||||
|
'chengdu-final-strategist-a': 'hold',
|
||||||
|
'chengdu-final-strategist-b': 'hold',
|
||||||
|
'chengdu-final-ambusher-a': 'aggressive',
|
||||||
|
'chengdu-final-ambusher-b': 'aggressive',
|
||||||
|
'chengdu-final-ambusher-c': 'aggressive',
|
||||||
|
'chengdu-final-officer-a': 'guard',
|
||||||
|
'chengdu-final-leader-huang-quan': 'guard'
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultEnemyAiByClass: Partial<Record<UnitClassKey, EnemyAiBehavior>> = {
|
const defaultEnemyAiByClass: Partial<Record<UnitClassKey, EnemyAiBehavior>> = {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import tenthBattleMapUrl from '../../assets/images/battle/tenth-battle-map.svg';
|
|||||||
import thirteenthBattleMapUrl from '../../assets/images/battle/thirteenth-battle-map.svg';
|
import thirteenthBattleMapUrl from '../../assets/images/battle/thirteenth-battle-map.svg';
|
||||||
import thirtyFirstBattleMapUrl from '../../assets/images/battle/thirty-first-battle-map.svg';
|
import thirtyFirstBattleMapUrl from '../../assets/images/battle/thirty-first-battle-map.svg';
|
||||||
import thirtySecondBattleMapUrl from '../../assets/images/battle/thirty-second-battle-map.svg';
|
import thirtySecondBattleMapUrl from '../../assets/images/battle/thirty-second-battle-map.svg';
|
||||||
|
import thirtyThirdBattleMapUrl from '../../assets/images/battle/thirty-third-battle-map.svg';
|
||||||
import thirtiethBattleMapUrl from '../../assets/images/battle/thirtieth-battle-map.svg';
|
import thirtiethBattleMapUrl from '../../assets/images/battle/thirtieth-battle-map.svg';
|
||||||
import thirdBattleMapUrl from '../../assets/images/battle/third-battle-map.svg';
|
import thirdBattleMapUrl from '../../assets/images/battle/third-battle-map.svg';
|
||||||
import twentyEighthBattleMapUrl from '../../assets/images/battle/twenty-eighth-battle-map.svg';
|
import twentyEighthBattleMapUrl from '../../assets/images/battle/twenty-eighth-battle-map.svg';
|
||||||
@@ -122,6 +123,7 @@ export class BootScene extends Phaser.Scene {
|
|||||||
this.load.image('battle-map-thirtieth', thirtiethBattleMapUrl);
|
this.load.image('battle-map-thirtieth', thirtiethBattleMapUrl);
|
||||||
this.load.image('battle-map-thirty-first', thirtyFirstBattleMapUrl);
|
this.load.image('battle-map-thirty-first', thirtyFirstBattleMapUrl);
|
||||||
this.load.image('battle-map-thirty-second', thirtySecondBattleMapUrl);
|
this.load.image('battle-map-thirty-second', thirtySecondBattleMapUrl);
|
||||||
|
this.load.image('battle-map-thirty-third', thirtyThirdBattleMapUrl);
|
||||||
this.load.image('portrait-liu-bei', liuBeiPortraitUrl);
|
this.load.image('portrait-liu-bei', liuBeiPortraitUrl);
|
||||||
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
this.load.image('portrait-guan-yu', guanYuPortraitUrl);
|
||||||
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
this.load.image('portrait-zhang-fei', zhangFeiPortraitUrl);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import {
|
|||||||
caoBreakRecruitUnits,
|
caoBreakRecruitUnits,
|
||||||
chengduPressureRecruitBonds,
|
chengduPressureRecruitBonds,
|
||||||
chengduPressureRecruitUnits,
|
chengduPressureRecruitUnits,
|
||||||
|
chengduSurrenderRecruitBonds,
|
||||||
|
chengduSurrenderRecruitUnits,
|
||||||
changshaRecruitBonds,
|
changshaRecruitBonds,
|
||||||
changshaRecruitUnits,
|
changshaRecruitUnits,
|
||||||
firstBattleBonds,
|
firstBattleBonds,
|
||||||
@@ -325,9 +327,10 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
|
|||||||
'twenty-ninth-battle-luo-outer-wall',
|
'twenty-ninth-battle-luo-outer-wall',
|
||||||
'thirtieth-battle-luofeng-ambush',
|
'thirtieth-battle-luofeng-ambush',
|
||||||
'thirty-first-battle-luo-main-gate',
|
'thirty-first-battle-luo-main-gate',
|
||||||
'thirty-second-battle-mianzhu-gate'
|
'thirty-second-battle-mianzhu-gate',
|
||||||
|
'thirty-third-battle-chengdu-surrender'
|
||||||
],
|
],
|
||||||
nextHints: ['형주 방위', '익주 진입', '방통·법정 합류']
|
nextHints: ['형주 방위', '익주 진입', '성도 항복']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'shu-han',
|
id: 'shu-han',
|
||||||
@@ -371,7 +374,8 @@ const campBattleIds = {
|
|||||||
twentyNinth: 'twenty-ninth-battle-luo-outer-wall',
|
twentyNinth: 'twenty-ninth-battle-luo-outer-wall',
|
||||||
thirtieth: 'thirtieth-battle-luofeng-ambush',
|
thirtieth: 'thirtieth-battle-luofeng-ambush',
|
||||||
thirtyFirst: 'thirty-first-battle-luo-main-gate',
|
thirtyFirst: 'thirty-first-battle-luo-main-gate',
|
||||||
thirtySecond: 'thirty-second-battle-mianzhu-gate'
|
thirtySecond: 'thirty-second-battle-mianzhu-gate',
|
||||||
|
thirtyThird: 'thirty-third-battle-chengdu-surrender'
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const requiredSortieUnitIds = new Set(['liu-bei']);
|
const requiredSortieUnitIds = new Set(['liu-bei']);
|
||||||
@@ -730,6 +734,18 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
|
|||||||
{ unitId: 'huang-zhong', reason: '관문 궁병과 고지 수비를 안정적으로 견제합니다.' }
|
{ unitId: 'huang-zhong', reason: '관문 궁병과 고지 수비를 안정적으로 견제합니다.' }
|
||||||
],
|
],
|
||||||
note: '면죽관 압박전은 열일곱 명 중 여섯 명만 출전합니다. 엄안이 새로 후보에 들어오며, 이엄을 설득하려면 익주 출신 장수와 책사를 함께 고르는 편이 좋습니다.'
|
note: '면죽관 압박전은 열일곱 명 중 여섯 명만 출전합니다. 엄안이 새로 후보에 들어오며, 이엄을 설득하려면 익주 출신 장수와 책사를 함께 고르는 편이 좋습니다.'
|
||||||
|
},
|
||||||
|
'thirty-third-battle-chengdu-surrender': {
|
||||||
|
maxUnits: 6,
|
||||||
|
recommended: [
|
||||||
|
{ unitId: 'liu-bei', reason: '성도 항복 권고와 익주 확보 명분의 중심입니다.' },
|
||||||
|
{ unitId: 'li-yan', reason: '이엄의 첫 출전입니다. 성도 수비장들에게 유비군의 군율을 증언할 수 있습니다.' },
|
||||||
|
{ unitId: 'zhuge-liang', reason: '항복 조건과 익주 수습책을 동시에 설계하는 핵심 책사입니다.' },
|
||||||
|
{ unitId: 'fa-zheng', reason: '유장 진영의 속사정과 황권의 신중한 판단을 읽는 역할입니다.' },
|
||||||
|
{ unitId: 'yan-yan', reason: '엄안의 항복 선례가 황권과 익주 장수들의 마음을 흔듭니다.' },
|
||||||
|
{ unitId: 'wu-yi', reason: '성도 외곽 길과 창고 위치를 아는 현지 장수입니다.' }
|
||||||
|
],
|
||||||
|
note: '성도 항복 권고전은 열여덟 명 중 여섯 명만 출전합니다. 새로 합류한 이엄을 데려가면 설득과 생존 목표가 맞물리고, 책사 비중을 늘리면 항복 명분이 강해집니다.'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -3406,6 +3422,87 @@ const campDialogues: CampDialogue[] = [
|
|||||||
rewardExp: 12
|
rewardExp: 12
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'liu-huang-after-chengdu',
|
||||||
|
title: '항복과 보전',
|
||||||
|
availableAfterBattleIds: [campBattleIds.thirtyThird],
|
||||||
|
unitIds: ['liu-bei', 'huang-quan'],
|
||||||
|
bondId: 'liu-bei__huang-quan',
|
||||||
|
rewardExp: 38,
|
||||||
|
lines: [
|
||||||
|
'황권: 패배한 장수에게 가장 두려운 것은 처벌보다, 백성이 새 질서 아래에서 버려지는 일입니다.',
|
||||||
|
'유비: 그 두려움을 알기에 그대를 맞이하오. 익주의 사람을 익주의 길로 안정시키는 일에 힘을 빌려 주시오.',
|
||||||
|
'황권: 그렇다면 저는 성도 안의 창고와 관직 명단을 정리해, 항복이 혼란으로 번지지 않게 하겠습니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'entrust-chengdu-order',
|
||||||
|
label: '성도 질서를 맡긴다',
|
||||||
|
response: '황권은 항복한 관료와 병사들의 이름을 정리하며, 유비군의 명령이 보복이 아니라 질서임을 보였습니다.',
|
||||||
|
rewardExp: 14
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'promise-yizhou-protection',
|
||||||
|
label: '익주 보전을 약속한다',
|
||||||
|
response: '유비의 약속이 군영 안에 전해지자 황권은 처음으로 긴 숨을 내쉬고 고개를 숙였습니다.',
|
||||||
|
rewardExp: 13
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'zhuge-huang-after-chengdu',
|
||||||
|
title: '성도 수습책',
|
||||||
|
availableAfterBattleIds: [campBattleIds.thirtyThird],
|
||||||
|
unitIds: ['zhuge-liang', 'huang-quan'],
|
||||||
|
bondId: 'zhuge-liang__huang-quan',
|
||||||
|
rewardExp: 36,
|
||||||
|
lines: [
|
||||||
|
'제갈량: 성도는 얻었으나 아직 다스린 것은 아닙니다. 관문보다 장부가 더 오래 남는 법이지요.',
|
||||||
|
'황권: 익주의 장부에는 사람의 두려움도 함께 적혀 있습니다. 급히 고치면 반발만 커질 것입니다.',
|
||||||
|
'제갈량: 그래서 그대의 신중함이 필요합니다. 새 나라는 칼보다 제도 위에 오래 서야 합니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'sort-yizhou-ledgers',
|
||||||
|
label: '익주 장부를 정리한다',
|
||||||
|
response: '제갈량과 황권은 군량, 관직, 백성 명부를 나누어 보며 다음 장의 내정 과제를 분명히 했습니다.',
|
||||||
|
rewardExp: 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'draft-shu-foundation-policy',
|
||||||
|
label: '건국 준비안을 쓴다',
|
||||||
|
response: '두 사람은 아직 나라 이름을 말하지 않았지만, 이미 나라가 버틸 기둥을 하나씩 적기 시작했습니다.',
|
||||||
|
rewardExp: 13
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fa-huang-after-chengdu',
|
||||||
|
title: '익주 책사들의 균형',
|
||||||
|
availableAfterBattleIds: [campBattleIds.thirtyThird],
|
||||||
|
unitIds: ['fa-zheng', 'huang-quan'],
|
||||||
|
bondId: 'fa-zheng__huang-quan',
|
||||||
|
rewardExp: 35,
|
||||||
|
lines: [
|
||||||
|
'법정: 성도가 열렸으니 이제 빠르게 마음을 잡아야 합니다. 머뭇거리면 오래된 의심이 다시 살아납니다.',
|
||||||
|
'황권: 빠름이 항상 안정은 아닙니다. 익주 사람은 강제로 바뀌는 것을 오래 기억합니다.',
|
||||||
|
'법정: 좋습니다. 저는 흔들 곳을 짚고, 그대는 무너지지 않게 받치십시오.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'divide-policy-tempo',
|
||||||
|
label: '정책 속도를 나눈다',
|
||||||
|
response: '법정은 급히 처리할 군사 문제를, 황권은 천천히 설득할 내정 문제를 맡아 균형을 잡았습니다.',
|
||||||
|
rewardExp: 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'appease-yizhou-officers',
|
||||||
|
label: '익주 장수를 달랜다',
|
||||||
|
response: '두 책사는 항복 장수들의 자리를 함부로 흔들지 않는 대신, 군율 위반은 단호히 다루기로 했습니다.',
|
||||||
|
rewardExp: 12
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -4455,6 +4552,66 @@ const campVisits: CampVisitDefinition[] = [
|
|||||||
itemRewards: ['성도 장수 명단 1']
|
itemRewards: ['성도 장수 명단 1']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'chengdu-storehouse-order',
|
||||||
|
title: '성도 창고 정리',
|
||||||
|
location: '성도 남창',
|
||||||
|
availableAfterBattleIds: [campBattleIds.thirtyThird],
|
||||||
|
bondId: 'liu-bei__huang-quan',
|
||||||
|
description: '유비와 황권이 성도 창고를 열어 항복 병사와 백성의 배급 질서를 바로잡습니다.',
|
||||||
|
lines: [
|
||||||
|
'황권: 성도 창고는 익주의 배를 붙잡는 곳입니다. 여기서 불공평하다는 말이 나오면 항복은 금세 원망이 됩니다.',
|
||||||
|
'유비: 그렇다면 전리품보다 장부를 먼저 세우시오. 우리 병사도 그 질서를 따르게 하겠소.',
|
||||||
|
'창고 문 앞에는 칼보다 붓이 먼저 놓였고, 성도 백성은 새 군령이 약탈이 아님을 확인하기 시작했습니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'share-chengdu-rations',
|
||||||
|
label: '배급 기준을 공개한다',
|
||||||
|
response: '배급 기준이 성도 남창에 붙자 항복 병사와 백성의 불안이 조금씩 가라앉았습니다.',
|
||||||
|
bondExp: 30,
|
||||||
|
itemRewards: ['콩 8', '상처약 4']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'preserve-chengdu-ledger',
|
||||||
|
label: '창고 장부를 보전한다',
|
||||||
|
response: '황권은 기존 장부를 태우지 않고 그대로 보전해, 유비군이 익주를 빼앗기만 한 것이 아님을 보였습니다.',
|
||||||
|
bondExp: 28,
|
||||||
|
gold: 480,
|
||||||
|
itemRewards: ['성도 창고 장부 1']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'shu-foundation-council',
|
||||||
|
title: '촉한 건국 준비 회의',
|
||||||
|
location: '성도 임시 관청',
|
||||||
|
availableAfterBattleIds: [campBattleIds.thirtyThird],
|
||||||
|
bondId: 'zhuge-liang__huang-quan',
|
||||||
|
description: '제갈량과 황권이 익주 수습 뒤 유비군이 국가의 틀로 나아가기 위한 첫 행정 과제를 정합니다.',
|
||||||
|
lines: [
|
||||||
|
'제갈량: 형주와 익주가 이어졌으니 이제 군영의 규칙만으로는 부족합니다. 법, 창고, 장수의 자리를 세워야 합니다.',
|
||||||
|
'황권: 익주 사람은 새 이름보다 오늘의 질서를 먼저 봅니다. 세금을 낮추고 병사의 약탈을 막는 것이 첫걸음입니다.',
|
||||||
|
'임시 관청의 등불 아래에서 다음 장의 목적은 전장 하나가 아니라 나라의 틀로 넓어졌습니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'draft-yizhou-stabilization',
|
||||||
|
label: '익주 안정책을 쓴다',
|
||||||
|
response: '제갈량과 황권은 세금, 군량, 항복 장수의 배치를 나누어 적으며 촉한 건국의 첫 초안을 만들었습니다.',
|
||||||
|
bondExp: 30,
|
||||||
|
itemRewards: ['익주 안정책 1']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prepare-hanzhong-road',
|
||||||
|
label: '한중 길을 검토한다',
|
||||||
|
response: '아직 이른 이야기였지만, 북쪽 관문과 한중으로 향하는 길이 지도 가장자리에 조용히 표시되었습니다.',
|
||||||
|
bondExp: 28,
|
||||||
|
gold: 500,
|
||||||
|
itemRewards: ['한중 길 초안 1']
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -4541,6 +4698,45 @@ export class CampScene extends Phaser.Scene {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (battleId === campBattleIds.thirtyThird) {
|
||||||
|
this.campaign = ensureCampaignRosterUnits(
|
||||||
|
[
|
||||||
|
...xuzhouRecruitUnits,
|
||||||
|
...caoBreakRecruitUnits,
|
||||||
|
...liuBiaoRecruitUnits,
|
||||||
|
...zhugeRecruitUnits,
|
||||||
|
...jingzhouRecruitUnits,
|
||||||
|
...guiyangRecruitUnits,
|
||||||
|
...wulingRecruitUnits,
|
||||||
|
...changshaRecruitUnits,
|
||||||
|
...yizhouRecruitUnits,
|
||||||
|
...fuPassRecruitUnits,
|
||||||
|
...luoCastleRecruitUnits,
|
||||||
|
...luoCastleProperRecruitUnits,
|
||||||
|
...chengduPressureRecruitUnits,
|
||||||
|
...chengduSurrenderRecruitUnits
|
||||||
|
],
|
||||||
|
[
|
||||||
|
...xuzhouRecruitBonds,
|
||||||
|
...caoBreakRecruitBonds,
|
||||||
|
...liuBiaoRecruitBonds,
|
||||||
|
...zhugeRecruitBonds,
|
||||||
|
...jingzhouRecruitBonds,
|
||||||
|
...guiyangRecruitBonds,
|
||||||
|
...wulingRecruitBonds,
|
||||||
|
...changshaRecruitBonds,
|
||||||
|
...yizhouRecruitBonds,
|
||||||
|
...fuPassRecruitBonds,
|
||||||
|
...luoCastleRecruitBonds,
|
||||||
|
...luoCastleProperRecruitBonds,
|
||||||
|
...chengduPressureRecruitBonds,
|
||||||
|
...chengduSurrenderRecruitBonds
|
||||||
|
]
|
||||||
|
);
|
||||||
|
this.report = this.campaign.firstBattleReport ?? this.report;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (battleId === campBattleIds.thirtySecond) {
|
if (battleId === campBattleIds.thirtySecond) {
|
||||||
this.campaign = ensureCampaignRosterUnits(
|
this.campaign = ensureCampaignRosterUnits(
|
||||||
[
|
[
|
||||||
@@ -4854,6 +5050,9 @@ export class CampScene extends Phaser.Scene {
|
|||||||
|
|
||||||
private currentCampTitle() {
|
private currentCampTitle() {
|
||||||
const battleId = this.currentCampBattleId();
|
const battleId = this.currentCampBattleId();
|
||||||
|
if (battleId === campBattleIds.thirtyThird) {
|
||||||
|
return '성도 항복 후 군영';
|
||||||
|
}
|
||||||
if (battleId === campBattleIds.thirtySecond) {
|
if (battleId === campBattleIds.thirtySecond) {
|
||||||
return '면죽관 압박전 후 군영';
|
return '면죽관 압박전 후 군영';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
thirteenthBattleScenario,
|
thirteenthBattleScenario,
|
||||||
thirtyFirstBattleScenario,
|
thirtyFirstBattleScenario,
|
||||||
thirtySecondBattleScenario,
|
thirtySecondBattleScenario,
|
||||||
|
thirtyThirdBattleScenario,
|
||||||
thirtiethBattleScenario,
|
thirtiethBattleScenario,
|
||||||
twentyEighthBattleScenario,
|
twentyEighthBattleScenario,
|
||||||
twentyFirstBattleScenario,
|
twentyFirstBattleScenario,
|
||||||
@@ -363,7 +364,8 @@ export class TitleScene extends Phaser.Scene {
|
|||||||
campaign.step === 'twenty-ninth-camp' ||
|
campaign.step === 'twenty-ninth-camp' ||
|
||||||
campaign.step === 'thirtieth-camp' ||
|
campaign.step === 'thirtieth-camp' ||
|
||||||
campaign.step === 'thirty-first-camp' ||
|
campaign.step === 'thirty-first-camp' ||
|
||||||
campaign.step === 'thirty-second-camp'
|
campaign.step === 'thirty-second-camp' ||
|
||||||
|
campaign.step === 'thirty-third-camp'
|
||||||
) {
|
) {
|
||||||
this.scene.start('CampScene');
|
this.scene.start('CampScene');
|
||||||
return;
|
return;
|
||||||
@@ -529,6 +531,11 @@ export class TitleScene extends Phaser.Scene {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (campaign.step === 'thirty-third-battle') {
|
||||||
|
this.scene.start('BattleScene', { battleId: thirtyThirdBattleScenario.id });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (campaign.step === 'first-victory-story') {
|
if (campaign.step === 'first-victory-story') {
|
||||||
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
|
this.scene.start('StoryScene', { pages: firstBattleVictoryPages, nextScene: 'TitleScene' });
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -105,7 +105,9 @@ export type CampaignStep =
|
|||||||
| 'thirty-first-battle'
|
| 'thirty-first-battle'
|
||||||
| 'thirty-first-camp'
|
| 'thirty-first-camp'
|
||||||
| 'thirty-second-battle'
|
| 'thirty-second-battle'
|
||||||
| 'thirty-second-camp';
|
| 'thirty-second-camp'
|
||||||
|
| 'thirty-third-battle'
|
||||||
|
| 'thirty-third-camp';
|
||||||
|
|
||||||
export type CampaignUnitProgressSnapshot = {
|
export type CampaignUnitProgressSnapshot = {
|
||||||
unitId: string;
|
unitId: string;
|
||||||
@@ -199,7 +201,8 @@ const campaignBattleSteps: Record<string, { victory: CampaignStep; retry: Campai
|
|||||||
'twenty-ninth-battle-luo-outer-wall': { victory: 'twenty-ninth-camp', retry: 'twenty-ninth-battle' },
|
'twenty-ninth-battle-luo-outer-wall': { victory: 'twenty-ninth-camp', retry: 'twenty-ninth-battle' },
|
||||||
'thirtieth-battle-luofeng-ambush': { victory: 'thirtieth-camp', retry: 'thirtieth-battle' },
|
'thirtieth-battle-luofeng-ambush': { victory: 'thirtieth-camp', retry: 'thirtieth-battle' },
|
||||||
'thirty-first-battle-luo-main-gate': { victory: 'thirty-first-camp', retry: 'thirty-first-battle' },
|
'thirty-first-battle-luo-main-gate': { victory: 'thirty-first-camp', retry: 'thirty-first-battle' },
|
||||||
'thirty-second-battle-mianzhu-gate': { victory: 'thirty-second-camp', retry: 'thirty-second-battle' }
|
'thirty-second-battle-mianzhu-gate': { victory: 'thirty-second-camp', retry: 'thirty-second-battle' },
|
||||||
|
'thirty-third-battle-chengdu-surrender': { victory: 'thirty-third-camp', retry: 'thirty-third-battle' }
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CampaignSaveSlotSummary = {
|
export type CampaignSaveSlotSummary = {
|
||||||
|
|||||||
Reference in New Issue
Block a user