Add Yi Province relief road chapter
This commit is contained in:
@@ -48,16 +48,17 @@ Build a small complete tactical RPG loop that can grow into a longer Romance of
|
|||||||
- Twenty-fourth battle Guiyang persuasion route, using Ma Liang's local knowledge in sortie selection, expanding the southern-commandery map to 48x32, and recruiting Yi Ji after Zhao Fan yields
|
- Twenty-fourth battle Guiyang persuasion route, using Ma Liang's local knowledge in sortie selection, expanding the southern-commandery map to 48x32, and recruiting Yi Ji after Zhao Fan yields
|
||||||
- Twenty-fifth battle Wuling mountain road route, pushing ten recruited officers into a six-officer sortie choice across a 50x34 mountain battlefield and recruiting Gong Zhi after Jin Xuan withdraws
|
- Twenty-fifth battle Wuling mountain road route, pushing ten recruited officers into a six-officer sortie choice across a 50x34 mountain battlefield and recruiting Gong Zhi after Jin Xuan withdraws
|
||||||
- Twenty-sixth battle Changsha veteran audience route, turning the southern Jing Province finale into a 52x34 castle approach battle and recruiting Huang Zhong plus Wei Yan after Han Xuan withdraws
|
- Twenty-sixth battle Changsha veteran audience route, turning the southern Jing Province finale into a 52x34 castle approach battle and recruiting Huang Zhong plus Wei Yan after Han Xuan withdraws
|
||||||
|
- Twenty-seventh battle Yi Province relief-road route, using the thirteen-officer roster in sortie selection across a 54x36 mountain-gate battlefield and recruiting Pang Tong after Yang Huai withdraws
|
||||||
- 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 twenty-sixth battle victory, recruit sortie selection, Liu Biao visit rewards, Zhuge Liang recruitment, Bowang/Changban/Jiangdong/Red Cliffs/Jing Province/Guiyang/Wuling/Changsha camp states, campaign timeline state, and camp save state
|
- Flow verification script from title through the twenty-seventh battle victory, recruit sortie selection, Liu Biao visit rewards, Zhuge Liang recruitment, Bowang/Changban/Jiangdong/Red Cliffs/Jing Province/Guiyang/Wuling/Changsha/Yi Province camp states, campaign timeline state, and camp save state
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
1. Start the Yi Province entry arc from the stabilized Jing Province base, with larger roster pressure and political camp events
|
1. Continue the Yi Province arc into Fu Pass and Luo Castle, with Pang Tong/Zhuge Liang planning pressure and harder officer selection
|
||||||
2. Add more recruitable officers as the campaign moves through Yi Province and toward Shu Han
|
2. Add more recruitable officers as the campaign moves deeper through Yi Province and toward 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 !== 26 ||
|
progressTabState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postBowangProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postChangbanProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postJiangdongProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postRedCliffsProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postFireAttackProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postJingzhouProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postGuiyangProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
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 !== 26 ||
|
postWulingProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
postWulingProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postWulingProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postWulingProgressState.campaignProgress?.latestBattleTitle !== '무릉 산길 확보전' ||
|
postWulingProgressState.campaignProgress?.latestBattleTitle !== '무릉 산길 확보전' ||
|
||||||
postWulingProgressState.campaignProgress?.nextBattleTitle !== '장사 노장 대면전'
|
postWulingProgressState.campaignProgress?.nextBattleTitle !== '장사 노장 대면전'
|
||||||
@@ -3021,15 +3021,160 @@ try {
|
|||||||
if (
|
if (
|
||||||
postChangshaProgressState?.activeTab !== 'progress' ||
|
postChangshaProgressState?.activeTab !== 'progress' ||
|
||||||
postChangshaProgressState.campaignProgress?.completedKnown !== 26 ||
|
postChangshaProgressState.campaignProgress?.completedKnown !== 26 ||
|
||||||
postChangshaProgressState.campaignProgress?.totalKnown !== 26 ||
|
postChangshaProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
postChangshaProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
postChangshaProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
postChangshaProgressState.campaignProgress?.latestBattleTitle !== '장사 노장 대면전' ||
|
postChangshaProgressState.campaignProgress?.latestBattleTitle !== '장사 노장 대면전' ||
|
||||||
postChangshaProgressState.campaignProgress?.nextBattleTitle !== '준비 중'
|
postChangshaProgressState.campaignProgress?.nextBattleTitle !== '익주 원군로'
|
||||||
) {
|
) {
|
||||||
throw new Error(`Expected post-Changsha progress tab to complete the veteran-audience battle and pause on Yi Province preparation: ${JSON.stringify(postChangshaProgressState?.campaignProgress)}`);
|
throw new Error(`Expected post-Changsha progress tab to complete the veteran-audience battle and open Yi Province relief road: ${JSON.stringify(postChangshaProgressState?.campaignProgress)}`);
|
||||||
}
|
}
|
||||||
await page.screenshot({ path: 'dist/verification-post-changsha-progress.png', fullPage: true });
|
await page.screenshot({ path: 'dist/verification-post-changsha-progress.png', fullPage: true });
|
||||||
|
|
||||||
|
await page.mouse.click(1120, 38);
|
||||||
|
await page.waitForTimeout(180);
|
||||||
|
const yizhouSortieState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
!yizhouSortieState?.sortieVisible ||
|
||||||
|
!yizhouSortieState.sortiePlan?.objectiveLine?.includes('익주 원군로') ||
|
||||||
|
!yizhouSortieState.sortieRoster?.some((unit) => unit.id === 'zhuge-liang' && unit.recruited && unit.recommended) ||
|
||||||
|
!yizhouSortieState.sortieRoster?.some((unit) => unit.id === 'huang-zhong' && unit.recruited && unit.recommended) ||
|
||||||
|
!yizhouSortieState.sortieRoster?.some((unit) => unit.id === 'wei-yan' && unit.recruited && unit.recommended) ||
|
||||||
|
!yizhouSortieState.sortieRoster?.some((unit) => unit.id === 'zhao-yun' && unit.recruited && unit.recommended) ||
|
||||||
|
!yizhouSortieState.sortieRoster?.some((unit) => unit.id === 'mi-zhu' && unit.recommended) ||
|
||||||
|
yizhouSortieState.sortieRoster?.some((unit) => unit.id === 'pang-tong') ||
|
||||||
|
yizhouSortieState.sortieRoster?.length < 13 ||
|
||||||
|
yizhouSortieState.sortiePlan?.maxCount !== 6
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected twenty-sixth camp sortie prep to target Yi Province relief road with Huang Zhong/Wei Yan selectable and Pang Tong absent before victory: ${JSON.stringify(yizhouSortieState)}`);
|
||||||
|
}
|
||||||
|
assertSortieTacticalRoster(yizhouSortieState, [
|
||||||
|
'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'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const yizhouPriorityUnits = ['zhuge-liang', 'huang-zhong', 'wei-yan', 'zhao-yun', 'mi-zhu'];
|
||||||
|
for (const unitId of yizhouPriorityUnits) {
|
||||||
|
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' && !yizhouPriorityUnits.includes(unit.id)
|
||||||
|
);
|
||||||
|
if (removable) {
|
||||||
|
await clickSortieRosterUnit(page, removable.id);
|
||||||
|
}
|
||||||
|
await clickSortieRosterUnit(page, unitId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const yizhouSortieReadyState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
!yizhouPriorityUnits.every((unitId) =>
|
||||||
|
yizhouSortieReadyState.sortieRoster?.some((unit) => unit.id === unitId && unit.selected)
|
||||||
|
) ||
|
||||||
|
yizhouSortieReadyState.sortiePlan?.selectedCount !== 6 ||
|
||||||
|
yizhouSortieReadyState.sortiePlan?.recommendedSelectedCount < 6
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected Yi Province sortie to deploy Zhuge Liang, Huang Zhong, Wei Yan, Zhao Yun, and Mi Zhu while preserving six-officer pressure: ${JSON.stringify(yizhouSortieReadyState)}`);
|
||||||
|
}
|
||||||
|
await page.screenshot({ path: 'dist/verification-yizhou-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-yizhou-story.png', fullPage: true });
|
||||||
|
|
||||||
|
for (let i = 0; i < 50; i += 1) {
|
||||||
|
const enteredTwentySeventhBattle = await page.evaluate(() => {
|
||||||
|
const state = window.__HEROS_DEBUG__?.battle();
|
||||||
|
return state?.scene === 'BattleScene' && state?.battleId === 'twenty-seventh-battle-yizhou-relief-road';
|
||||||
|
});
|
||||||
|
if (enteredTwentySeventhBattle) {
|
||||||
|
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 === 'twenty-seventh-battle-yizhou-relief-road' && state?.battleOutcome === null && state?.phase === 'idle';
|
||||||
|
});
|
||||||
|
await page.screenshot({ path: 'dist/verification-twenty-seventh-battle.png', fullPage: true });
|
||||||
|
|
||||||
|
const twentySeventhBattleState = await page.evaluate(() => window.__HEROS_DEBUG__?.battle());
|
||||||
|
const twentySeventhEnemies = twentySeventhBattleState.units.filter((unit) => unit.faction === 'enemy');
|
||||||
|
const twentySeventhAllies = twentySeventhBattleState.units.filter((unit) => unit.faction === 'ally');
|
||||||
|
const twentySeventhEnemyBehaviors = new Set(twentySeventhEnemies.map((unit) => unit.ai));
|
||||||
|
if (
|
||||||
|
twentySeventhBattleState.camera?.mapWidth !== 54 ||
|
||||||
|
twentySeventhBattleState.camera?.mapHeight !== 36 ||
|
||||||
|
twentySeventhBattleState.victoryConditionLabel !== '양회 퇴각' ||
|
||||||
|
twentySeventhEnemies.length < 19 ||
|
||||||
|
!twentySeventhEnemyBehaviors.has('aggressive') ||
|
||||||
|
!twentySeventhEnemyBehaviors.has('guard') ||
|
||||||
|
!twentySeventhEnemyBehaviors.has('hold') ||
|
||||||
|
!twentySeventhEnemies.some((unit) => unit.id === 'yizhou-leader-yang-huai') ||
|
||||||
|
!twentySeventhAllies.some((unit) => unit.id === 'huang-zhong') ||
|
||||||
|
!twentySeventhAllies.some((unit) => unit.id === 'wei-yan') ||
|
||||||
|
twentySeventhAllies.some((unit) => unit.id === 'pang-tong')
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected twenty-seventh battle to use Yi Province map, Yang Huai objective, Huang Zhong/Wei Yan as selectable allies, mixed AI, and no Pang Tong before victory: ${JSON.stringify(twentySeventhBattleState)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 twentySeventhCampState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
twentySeventhCampState?.campBattleId !== 'twenty-seventh-battle-yizhou-relief-road' ||
|
||||||
|
twentySeventhCampState.campTitle !== '익주 원군로 후 군영' ||
|
||||||
|
twentySeventhCampState.availableDialogueIds?.length !== 3 ||
|
||||||
|
!twentySeventhCampState.availableDialogueIds.every((id) => id.endsWith('yizhou')) ||
|
||||||
|
twentySeventhCampState.availableVisitIds?.length !== 2 ||
|
||||||
|
!twentySeventhCampState.campaign?.roster?.some((unit) => unit.id === 'pang-tong') ||
|
||||||
|
!twentySeventhCampState.sortieRoster?.some((unit) => unit.id === 'pang-tong' && unit.recruited) ||
|
||||||
|
twentySeventhCampState.rosterCollection?.total < 14
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected twenty-seventh camp to recruit Pang Tong and expose Yi Province dialogue/visit sets: ${JSON.stringify(twentySeventhCampState)}`);
|
||||||
|
}
|
||||||
|
await page.screenshot({ path: 'dist/verification-yizhou-camp.png', fullPage: true });
|
||||||
|
|
||||||
|
await page.mouse.click(966, 38);
|
||||||
|
await page.waitForTimeout(180);
|
||||||
|
const postYizhouProgressState = await page.evaluate(() => window.__HEROS_DEBUG__?.camp());
|
||||||
|
if (
|
||||||
|
postYizhouProgressState?.activeTab !== 'progress' ||
|
||||||
|
postYizhouProgressState.campaignProgress?.completedKnown !== 27 ||
|
||||||
|
postYizhouProgressState.campaignProgress?.totalKnown !== 27 ||
|
||||||
|
postYizhouProgressState.campaignProgress?.activeChapter?.title !== '형주·익주 확보' ||
|
||||||
|
postYizhouProgressState.campaignProgress?.latestBattleTitle !== '익주 원군로' ||
|
||||||
|
postYizhouProgressState.campaignProgress?.nextBattleTitle !== '준비 중'
|
||||||
|
) {
|
||||||
|
throw new Error(`Expected post-Yi Province progress tab to complete the relief-road battle and pause on Fu Pass preparation: ${JSON.stringify(postYizhouProgressState?.campaignProgress)}`);
|
||||||
|
}
|
||||||
|
await page.screenshot({ path: 'dist/verification-post-yizhou-progress.png', fullPage: true });
|
||||||
|
|
||||||
await page.evaluate(() => window.__HEROS_GAME__?.scene.start('TitleScene'));
|
await page.evaluate(() => window.__HEROS_GAME__?.scene.start('TitleScene'));
|
||||||
await page.waitForFunction(() => {
|
await page.waitForFunction(() => {
|
||||||
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
const activeScenes = window.__HEROS_DEBUG__?.activeScenes() ?? [];
|
||||||
@@ -3041,7 +3186,7 @@ try {
|
|||||||
return activeScenes.includes('CampScene');
|
return activeScenes.includes('CampScene');
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Verified title-to-twenty-sixth-battle flow, recruited officer sortie selection, Ma Liang, Yi Ji, Gong Zhi, Huang Zhong, and Wei Yan joins, result states, and debug API at ${targetUrl}`);
|
console.log(`Verified title-to-twenty-seventh-battle flow, recruited officer sortie selection, Ma Liang, Yi Ji, Gong Zhi, Huang Zhong, Wei Yan, and Pang Tong joins, result states, and debug API at ${targetUrl}`);
|
||||||
} finally {
|
} finally {
|
||||||
await browser?.close();
|
await browser?.close();
|
||||||
if (serverProcess && !serverProcess.killed) {
|
if (serverProcess && !serverProcess.killed) {
|
||||||
@@ -3157,7 +3302,8 @@ async function clickSortieRosterUnit(page, unitId) {
|
|||||||
throw new Error(`Cannot click sortie unit ${unitId}; unit is missing from roster: ${JSON.stringify(state.sortieRoster)}`);
|
throw new Error(`Cannot click sortie unit ${unitId}; unit is missing from roster: ${JSON.stringify(state.sortieRoster)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const rowGap = state.sortieRoster.length > 7 ? 30 : state.sortieRoster.length > 5 ? 38 : state.sortieRoster.length > 4 ? 43 : 48;
|
const denseRoster = state.sortieRoster.length > 12;
|
||||||
await page.mouse.click(254, 314 + 50 + index * rowGap);
|
const rowGap = denseRoster ? 17 : state.sortieRoster.length > 7 ? 30 : state.sortieRoster.length > 5 ? 38 : state.sortieRoster.length > 4 ? 43 : 48;
|
||||||
|
await page.mouse.click(254, 314 + (denseRoster ? 48 : 50) + index * rowGap);
|
||||||
await page.waitForTimeout(120);
|
await page.waitForTimeout(120);
|
||||||
}
|
}
|
||||||
|
|||||||
120
src/assets/images/battle/twenty-seventh-battle-map.svg
Normal file
120
src/assets/images/battle/twenty-seventh-battle-map.svg
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="5400" height="3600" viewBox="0 0 5400 3600">
|
||||||
|
<defs>
|
||||||
|
<filter id="grain" x="-5%" y="-5%" width="110%" height="110%">
|
||||||
|
<feTurbulence type="fractalNoise" baseFrequency="0.032" numOctaves="4" seed="27" result="noise"/>
|
||||||
|
<feColorMatrix type="saturate" values="0.42"/>
|
||||||
|
<feBlend in="SourceGraphic" in2="noise" mode="multiply"/>
|
||||||
|
</filter>
|
||||||
|
<filter id="ink" x="-10%" y="-10%" width="120%" height="120%">
|
||||||
|
<feDropShadow dx="0" dy="14" stdDeviation="9" flood-color="#21150d" flood-opacity="0.38"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="plain" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#877f55"/>
|
||||||
|
<stop offset="0.48" stop-color="#a49060"/>
|
||||||
|
<stop offset="1" stop-color="#6d704e"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="road" x1="0" y1="0" x2="1" y2="1">
|
||||||
|
<stop offset="0" stop-color="#b79b68"/>
|
||||||
|
<stop offset="0.52" stop-color="#d3ba81"/>
|
||||||
|
<stop offset="1" stop-color="#92784e"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="river" x1="0" y1="0" x2="1" y2="0">
|
||||||
|
<stop offset="0" stop-color="#355a78"/>
|
||||||
|
<stop offset="0.48" stop-color="#5f8faa"/>
|
||||||
|
<stop offset="1" stop-color="#2d4b67"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="mountain" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0" stop-color="#4a4a2e"/>
|
||||||
|
<stop offset="0.55" stop-color="#2e321f"/>
|
||||||
|
<stop offset="1" stop-color="#171b14"/>
|
||||||
|
</linearGradient>
|
||||||
|
<pattern id="fieldHatch" width="180" height="180" patternUnits="userSpaceOnUse">
|
||||||
|
<path d="M-40 135 C30 92 78 88 150 40 M0 190 C70 148 126 138 205 82" fill="none" stroke="#cbb077" stroke-width="10" stroke-opacity="0.18"/>
|
||||||
|
<circle cx="38" cy="54" r="7" fill="#4c6438" opacity="0.2"/>
|
||||||
|
<circle cx="126" cy="122" r="6" fill="#4c6438" opacity="0.16"/>
|
||||||
|
</pattern>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="5400" height="3600" fill="url(#plain)"/>
|
||||||
|
<rect width="5400" height="3600" fill="url(#fieldHatch)" opacity="0.8" filter="url(#grain)"/>
|
||||||
|
|
||||||
|
<g opacity="0.42">
|
||||||
|
<path d="M70 1380 C510 980 720 440 1110 80 L1180 0 L0 0 L0 2920 C88 2500 160 1970 70 1380Z" fill="#111814"/>
|
||||||
|
<path d="M5220 2080 C4970 2480 4860 3090 5180 3600 L5400 3600 L5400 0 L4720 0 C4480 360 4540 780 4870 1040 C5150 1260 5380 1640 5220 2080Z" fill="#121812"/>
|
||||||
|
<path d="M4260 0 C4210 290 4380 535 4620 790 L4695 0Z" fill="#111814"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g filter="url(#ink)">
|
||||||
|
<path d="M1205 0 C1170 310 1215 690 1280 1040 C1355 1440 1280 1985 1190 2520" fill="none" stroke="#22384e" stroke-width="240" stroke-linecap="round"/>
|
||||||
|
<path d="M1225 0 C1198 315 1248 705 1308 1040 C1388 1455 1304 1988 1215 2515" fill="none" stroke="url(#river)" stroke-width="185" stroke-linecap="round"/>
|
||||||
|
<path d="M1288 80 C1274 420 1342 786 1395 1090 C1458 1452 1388 1890 1325 2310" fill="none" stroke="#91c5d7" stroke-width="22" stroke-linecap="round" opacity="0.52"/>
|
||||||
|
|
||||||
|
<path d="M2920 420 C2985 880 2980 1330 2920 2080" fill="none" stroke="#22384e" stroke-width="220" stroke-linecap="round"/>
|
||||||
|
<path d="M2948 430 C3008 885 3000 1330 2955 2070" fill="none" stroke="url(#river)" stroke-width="165" stroke-linecap="round"/>
|
||||||
|
<path d="M3006 500 C3042 900 3036 1290 3000 1905" fill="none" stroke="#9ccfe0" stroke-width="19" stroke-linecap="round" opacity="0.48"/>
|
||||||
|
|
||||||
|
<path d="M3650 2520 C3640 2860 3675 3240 3665 3600" fill="none" stroke="#22384e" stroke-width="220" stroke-linecap="round"/>
|
||||||
|
<path d="M3675 2530 C3668 2865 3700 3245 3692 3600" fill="none" stroke="url(#river)" stroke-width="160" stroke-linecap="round"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g opacity="0.95">
|
||||||
|
<path d="M450 3140 C980 3020 1325 3040 1795 2860 C2260 2685 2550 2505 3210 2460 C3710 2425 4185 2105 4710 1640 C4910 1465 5055 1445 5200 1510" fill="none" stroke="#604633" stroke-width="310" stroke-linecap="round" stroke-linejoin="round" opacity="0.38"/>
|
||||||
|
<path d="M455 3120 C1015 3008 1360 3012 1815 2825 C2300 2626 2585 2440 3190 2405 C3684 2378 4140 2070 4668 1596 C4880 1405 5055 1380 5215 1458" fill="none" stroke="url(#road)" stroke-width="210" stroke-linecap="round" stroke-linejoin="round" filter="url(#grain)"/>
|
||||||
|
<path d="M700 3160 C1340 3020 1710 2990 2240 2660 C2650 2405 2955 2298 3348 2300" fill="none" stroke="#ecd59d" stroke-width="32" stroke-linecap="round" stroke-opacity="0.42"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g filter="url(#ink)">
|
||||||
|
<path d="M380 720 C680 370 1010 240 1370 140 C1590 82 1765 38 2060 52 C1740 390 1480 690 1290 1120 C1055 1040 835 1000 620 1095 C495 1150 410 1060 380 720Z" fill="url(#mountain)"/>
|
||||||
|
<path d="M1980 120 C2290 0 2590 80 2850 360 C2670 610 2540 825 2480 1110 C2200 960 1925 885 1635 980 C1668 690 1780 390 1980 120Z" fill="url(#mountain)"/>
|
||||||
|
<path d="M3130 420 C3600 170 4020 255 4350 760 C4110 980 3970 1250 3870 1540 C3615 1360 3315 1310 2990 1420 C3028 1020 3085 675 3130 420Z" fill="url(#mountain)"/>
|
||||||
|
<path d="M2300 3060 C2630 2840 3080 2840 3400 3040 C3240 3230 3150 3410 3090 3600 L2220 3600 C2215 3445 2240 3260 2300 3060Z" fill="url(#mountain)"/>
|
||||||
|
<path d="M4150 2470 C4560 2320 4985 2400 5330 2790 C5090 3020 4945 3290 4890 3600 L4190 3600 C4300 3250 4275 2860 4150 2470Z" fill="url(#mountain)"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g opacity="0.94">
|
||||||
|
<g transform="translate(1750 2680)">
|
||||||
|
<path d="M0 85 L130 0 L260 85 L238 245 L26 245Z" fill="#736640" stroke="#2c2116" stroke-width="18"/>
|
||||||
|
<path d="M40 92 L130 32 L220 92 L205 128 L130 84 L55 128Z" fill="#d18a29"/>
|
||||||
|
<rect x="92" y="130" width="76" height="112" fill="#2f2116"/>
|
||||||
|
<path d="M30 250 C100 285 170 285 240 250" fill="none" stroke="#26321c" stroke-width="20"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(3180 2200) scale(0.92)">
|
||||||
|
<path d="M0 85 L130 0 L260 85 L238 245 L26 245Z" fill="#716241" stroke="#2c2116" stroke-width="18"/>
|
||||||
|
<path d="M40 92 L130 32 L220 92 L205 128 L130 84 L55 128Z" fill="#d18a29"/>
|
||||||
|
<rect x="92" y="130" width="76" height="112" fill="#2f2116"/>
|
||||||
|
</g>
|
||||||
|
<g transform="translate(3820 1600) scale(0.86)">
|
||||||
|
<path d="M0 85 L130 0 L260 85 L238 245 L26 245Z" fill="#746340" stroke="#2c2116" stroke-width="18"/>
|
||||||
|
<path d="M40 92 L130 32 L220 92 L205 128 L130 84 L55 128Z" fill="#d18a29"/>
|
||||||
|
<rect x="92" y="130" width="76" height="112" fill="#2f2116"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g filter="url(#ink)">
|
||||||
|
<path d="M4470 1200 L5140 1200 L5260 1325 L5260 2015 L4420 2015 L4420 1325Z" fill="#6e5e43" stroke="#241b14" stroke-width="32"/>
|
||||||
|
<path d="M4580 1320 L5085 1320 L5170 1400 L5170 1930 L4510 1930 L4510 1400Z" fill="#9a8155" stroke="#302014" stroke-width="22"/>
|
||||||
|
<path d="M4740 1535 L4930 1535 L4930 1930 L4740 1930Z" fill="#292017"/>
|
||||||
|
<path d="M4460 1185 L4380 1295 L4420 1325 L5260 1325 L5330 1250 L5140 1200Z" fill="#3a3429"/>
|
||||||
|
<path d="M4650 1120 L4770 1050 L4890 1120 L4890 1235 L4650 1235Z" fill="#4c4638" stroke="#241b14" stroke-width="20"/>
|
||||||
|
<path d="M4970 1135 L5070 1080 L5170 1135 L5170 1235 L4970 1235Z" fill="#4c4638" stroke="#241b14" stroke-width="20"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g opacity="0.75">
|
||||||
|
<circle cx="820" cy="1690" r="190" fill="#2e5129"/>
|
||||||
|
<circle cx="1050" cy="1505" r="230" fill="#274b26"/>
|
||||||
|
<circle cx="1370" cy="1710" r="260" fill="#355d31"/>
|
||||||
|
<circle cx="1890" cy="1280" r="230" fill="#31562e"/>
|
||||||
|
<circle cx="2220" cy="1500" r="260" fill="#294b27"/>
|
||||||
|
<circle cx="2670" cy="1900" r="220" fill="#30582f"/>
|
||||||
|
<circle cx="3370" cy="2950" r="260" fill="#315c31"/>
|
||||||
|
<circle cx="3920" cy="3040" r="300" fill="#294c2b"/>
|
||||||
|
<circle cx="4520" cy="2620" r="270" fill="#31582f"/>
|
||||||
|
<circle cx="5000" cy="3010" r="300" fill="#294b27"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g opacity="0.22">
|
||||||
|
<path d="M0 2900 C420 2810 760 2780 1160 2880" fill="none" stroke="#e8d19a" stroke-width="28"/>
|
||||||
|
<path d="M3550 1450 C3920 1230 4260 1100 4620 1120" fill="none" stroke="#e8d19a" stroke-width="24"/>
|
||||||
|
<path d="M1450 2680 C1910 2600 2350 2440 2770 2180" fill="none" stroke="#4a341f" stroke-width="18"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 7.7 KiB |
@@ -63,6 +63,10 @@ import {
|
|||||||
twentySixthBattleMap,
|
twentySixthBattleMap,
|
||||||
twentySixthBattleUnits,
|
twentySixthBattleUnits,
|
||||||
twentySixthBattleVictoryPages,
|
twentySixthBattleVictoryPages,
|
||||||
|
twentySeventhBattleBonds,
|
||||||
|
twentySeventhBattleMap,
|
||||||
|
twentySeventhBattleUnits,
|
||||||
|
twentySeventhBattleVictoryPages,
|
||||||
twentiethBattleBonds,
|
twentiethBattleBonds,
|
||||||
twentiethBattleMap,
|
twentiethBattleMap,
|
||||||
twentiethBattleUnits,
|
twentiethBattleUnits,
|
||||||
@@ -135,7 +139,8 @@ export type BattleScenarioId =
|
|||||||
| 'twenty-third-battle-jingzhou-south-entry'
|
| 'twenty-third-battle-jingzhou-south-entry'
|
||||||
| 'twenty-fourth-battle-guiyang-persuasion'
|
| 'twenty-fourth-battle-guiyang-persuasion'
|
||||||
| 'twenty-fifth-battle-wuling-mountain-road'
|
| 'twenty-fifth-battle-wuling-mountain-road'
|
||||||
| 'twenty-sixth-battle-changsha-veteran';
|
| 'twenty-sixth-battle-changsha-veteran'
|
||||||
|
| 'twenty-seventh-battle-yizhou-relief-road';
|
||||||
|
|
||||||
export type BattleObjectiveKind = 'defeat-leader' | 'keep-unit-alive' | 'secure-terrain' | 'quick-victory';
|
export type BattleObjectiveKind = 'defeat-leader' | 'keep-unit-alive' | 'secure-terrain' | 'quick-victory';
|
||||||
|
|
||||||
@@ -1633,6 +1638,69 @@ export const twentySixthBattleScenario: BattleScenarioDefinition = {
|
|||||||
nextCampScene: 'CampScene'
|
nextCampScene: 'CampScene'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const twentySeventhBattleScenario: BattleScenarioDefinition = {
|
||||||
|
id: 'twenty-seventh-battle-yizhou-relief-road',
|
||||||
|
title: '익주 원군로',
|
||||||
|
victoryConditionLabel: '양회 퇴각',
|
||||||
|
defeatConditionLabel: '유비 또는 제갈량 퇴각',
|
||||||
|
openingObjectiveLines: [
|
||||||
|
'장사까지 안정시킨 유비군은 유장의 원군 요청을 따라 익주로 향합니다. 원군로에는 양회의 전초 병력이 관문과 마을을 함께 틀어쥐고 있습니다.',
|
||||||
|
'이번 전투는 길목의 마을을 지키면서 관문 전열을 무너뜨려야 합니다. 황충과 위연까지 합류한 큰 로스터에서 산길, 돌파, 책략 역할을 직접 골라 출전하십시오.',
|
||||||
|
'유비와 제갈량을 지키며 양회를 물러나게 하면 봉추 방통이 군영에 합류합니다.'
|
||||||
|
],
|
||||||
|
map: twentySeventhBattleMap,
|
||||||
|
units: twentySeventhBattleUnits,
|
||||||
|
bonds: twentySeventhBattleBonds,
|
||||||
|
mapTextureKey: 'battle-map-twenty-seventh',
|
||||||
|
leaderUnitId: 'yizhou-leader-yang-huai',
|
||||||
|
quickVictoryTurnLimit: 36,
|
||||||
|
baseVictoryGold: 3760,
|
||||||
|
objectives: [
|
||||||
|
{
|
||||||
|
id: 'leader',
|
||||||
|
kind: 'defeat-leader',
|
||||||
|
label: '양회 퇴각',
|
||||||
|
rewardGold: 2420,
|
||||||
|
unitId: 'yizhou-leader-yang-huai'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'liu-bei',
|
||||||
|
kind: 'keep-unit-alive',
|
||||||
|
label: '유비 생존',
|
||||||
|
rewardGold: 700,
|
||||||
|
unitId: 'liu-bei'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'zhuge-liang',
|
||||||
|
kind: 'keep-unit-alive',
|
||||||
|
label: '제갈량 생존',
|
||||||
|
rewardGold: 980,
|
||||||
|
unitId: 'zhuge-liang'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-village',
|
||||||
|
kind: 'secure-terrain',
|
||||||
|
label: '익주 마을 안정',
|
||||||
|
rewardGold: 1160,
|
||||||
|
terrain: 'village'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'quick',
|
||||||
|
kind: 'quick-victory',
|
||||||
|
label: '36턴 이내 승리',
|
||||||
|
rewardGold: 920,
|
||||||
|
maxTurn: 36
|
||||||
|
}
|
||||||
|
],
|
||||||
|
defeatConditions: [
|
||||||
|
{ kind: 'unit-defeated', unitId: 'liu-bei' },
|
||||||
|
{ kind: 'unit-defeated', unitId: 'zhuge-liang' }
|
||||||
|
],
|
||||||
|
itemRewards: ['콩 12', '상처약 9', '탁주 5', '익주 길목 지도 +1', '방통 합류'],
|
||||||
|
victoryPages: twentySeventhBattleVictoryPages,
|
||||||
|
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> = {
|
||||||
@@ -1661,7 +1729,8 @@ export const battleScenarios: Record<BattleScenarioId, BattleScenarioDefinition>
|
|||||||
'twenty-third-battle-jingzhou-south-entry': twentyThirdBattleScenario,
|
'twenty-third-battle-jingzhou-south-entry': twentyThirdBattleScenario,
|
||||||
'twenty-fourth-battle-guiyang-persuasion': twentyFourthBattleScenario,
|
'twenty-fourth-battle-guiyang-persuasion': twentyFourthBattleScenario,
|
||||||
'twenty-fifth-battle-wuling-mountain-road': twentyFifthBattleScenario,
|
'twenty-fifth-battle-wuling-mountain-road': twentyFifthBattleScenario,
|
||||||
'twenty-sixth-battle-changsha-veteran': twentySixthBattleScenario
|
'twenty-sixth-battle-changsha-veteran': twentySixthBattleScenario,
|
||||||
|
'twenty-seventh-battle-yizhou-relief-road': twentySeventhBattleScenario
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultBattleScenario = battleScenarios[defaultBattleScenarioId];
|
export const defaultBattleScenario = battleScenarios[defaultBattleScenarioId];
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
twentyFifthBattleScenario,
|
twentyFifthBattleScenario,
|
||||||
twentyFourthBattleScenario,
|
twentyFourthBattleScenario,
|
||||||
twentySixthBattleScenario,
|
twentySixthBattleScenario,
|
||||||
|
twentySeventhBattleScenario,
|
||||||
twentiethBattleScenario,
|
twentiethBattleScenario,
|
||||||
twelfthBattleScenario,
|
twelfthBattleScenario,
|
||||||
thirdBattleScenario,
|
thirdBattleScenario,
|
||||||
@@ -74,6 +75,8 @@ import {
|
|||||||
twentyFourthBattleVictoryPages,
|
twentyFourthBattleVictoryPages,
|
||||||
twentySixthBattleIntroPages,
|
twentySixthBattleIntroPages,
|
||||||
twentySixthBattleVictoryPages,
|
twentySixthBattleVictoryPages,
|
||||||
|
twentySeventhBattleIntroPages,
|
||||||
|
twentySeventhBattleVictoryPages,
|
||||||
twentiethBattleIntroPages,
|
twentiethBattleIntroPages,
|
||||||
twentiethBattleVictoryPages,
|
twentiethBattleVictoryPages,
|
||||||
twelfthBattleIntroPages,
|
twelfthBattleIntroPages,
|
||||||
@@ -361,13 +364,24 @@ const sortieFlows: Record<string, SortieFlow> = {
|
|||||||
},
|
},
|
||||||
[twentySixthBattleScenario.id]: {
|
[twentySixthBattleScenario.id]: {
|
||||||
afterBattleId: twentySixthBattleScenario.id,
|
afterBattleId: twentySixthBattleScenario.id,
|
||||||
eyebrow: '다음 장 준비',
|
eyebrow: '다음 전장',
|
||||||
title: '익주 진입 준비',
|
title: twentySeventhBattleScenario.title,
|
||||||
description:
|
description:
|
||||||
'장사까지 열리며 형주 남부의 뿌리가 단단해졌습니다. 다음은 익주로 향하는 더 긴 정치와 전장의 길을 준비해야 합니다.',
|
'장사까지 열리며 형주 남부의 뿌리가 단단해졌습니다. 이제 유장의 원군 요청을 명분으로 익주 원군로를 열고, 봉추 방통의 마음까지 얻어야 합니다.',
|
||||||
rewardHint: '다음 장: 익주 진입 준비 중',
|
rewardHint: `예상 보상: ${twentySeventhBattleScenario.title} 개방 / 방통 합류`,
|
||||||
pages: twentySixthBattleVictoryPages,
|
nextBattleId: twentySeventhBattleScenario.id,
|
||||||
unavailableNotice: '익주 진입 장은 다음 작업에서 이어집니다. 군영에서 황충과 위연을 살피고 다음 대장정의 출전 후보를 정비하십시오.'
|
campaignStep: 'twenty-seventh-battle',
|
||||||
|
pages: [...twentySixthBattleVictoryPages, ...twentySeventhBattleIntroPages]
|
||||||
|
},
|
||||||
|
[twentySeventhBattleScenario.id]: {
|
||||||
|
afterBattleId: twentySeventhBattleScenario.id,
|
||||||
|
eyebrow: '다음 장 준비',
|
||||||
|
title: '부수관 진입 준비',
|
||||||
|
description:
|
||||||
|
'익주 원군로가 열리고 방통이 합류했습니다. 이제 유비군은 촉의 깊은 산문과 부수관으로 들어가기 전, 와룡과 봉추의 계책을 함께 다듬어야 합니다.',
|
||||||
|
rewardHint: '다음 장: 부수관 진입 준비 중',
|
||||||
|
pages: twentySeventhBattleVictoryPages,
|
||||||
|
unavailableNotice: '부수관 이후 익주 심부 장은 다음 작업에서 이어집니다. 군영에서 방통과 기존 무장들의 공명도를 올리고 다음 대장정의 출전 후보를 정비하십시오.'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1704,6 +1704,68 @@ export const twentySixthBattleVictoryPages: StoryPage[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const twentySeventhBattleIntroPages: StoryPage[] = [
|
||||||
|
{
|
||||||
|
id: 'twenty-seventh-yizhou-request',
|
||||||
|
bgm: 'story-dark',
|
||||||
|
chapter: '익주에서 온 요청',
|
||||||
|
background: 'story-three-heroes',
|
||||||
|
speaker: '손건',
|
||||||
|
text: '익주의 유장이 한중 장로의 압박을 두려워하여 원군을 청했습니다. 형주 남부를 안정시킨 지금, 이 요청은 촉으로 향하는 첫 문이 될 수 있습니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'twenty-seventh-pang-tong-rumor',
|
||||||
|
bgm: 'battle-prep',
|
||||||
|
chapter: '봉추의 그림자',
|
||||||
|
background: 'story-liu-bei',
|
||||||
|
speaker: '제갈량',
|
||||||
|
text: '익주로 들어가는 길목에는 봉추라 불리는 방통이 머문다는 소문이 있습니다. 그가 뜻을 함께한다면, 산과 관문으로 막힌 촉의 길도 달리 보일 것입니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'twenty-seventh-gate-warning',
|
||||||
|
bgm: 'story-dark',
|
||||||
|
chapter: '원군로의 관문',
|
||||||
|
background: 'story-militia',
|
||||||
|
speaker: '마량',
|
||||||
|
text: '양회가 이끄는 전초 병력이 원군로를 막고 있습니다. 길목의 마을을 지켜 명분을 잃지 않고, 산길의 척후를 먼저 끊어야 합니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'twenty-seventh-yizhou-sortie',
|
||||||
|
bgm: 'battle-prep',
|
||||||
|
chapter: '익주 원군로',
|
||||||
|
background: 'story-sortie',
|
||||||
|
speaker: '유비',
|
||||||
|
portrait: 'liuBei',
|
||||||
|
text: '우리는 익주를 빼앗으러 가는 것이 아니라, 백성을 지키라는 부름에 응하는 것이오. 군율을 세우고 길을 열어 봉추의 마음까지 얻어 봅시다.'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export const twentySeventhBattleVictoryPages: StoryPage[] = [
|
||||||
|
{
|
||||||
|
id: 'twenty-seventh-victory-road-open',
|
||||||
|
bgm: 'militia-theme',
|
||||||
|
chapter: '익주의 길이 열리다',
|
||||||
|
background: 'story-sortie',
|
||||||
|
text: '양회의 전초 병력이 물러나고 익주 원군로가 열렸습니다. 유비군은 마을의 창고를 건드리지 않고 길목을 안정시켜, 원군이라는 명분을 분명히 세웠습니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'twenty-seventh-pang-tong-joins',
|
||||||
|
bgm: 'battle-prep',
|
||||||
|
chapter: '방통의 합류',
|
||||||
|
background: 'story-liu-bei',
|
||||||
|
speaker: '방통',
|
||||||
|
text: '인의를 말로만 내세우는 군은 많으나, 길목의 작은 마을까지 지키며 움직이는 군은 드뭅니다. 이 방통, 촉으로 향하는 험한 길을 함께 보겠습니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'twenty-seventh-next-fu-pass',
|
||||||
|
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,
|
||||||
@@ -2005,6 +2067,12 @@ export const twentySixthBattleMap: BattleMap = {
|
|||||||
terrain: createTwentySixthBattleTerrain()
|
terrain: createTwentySixthBattleTerrain()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const twentySeventhBattleMap: BattleMap = {
|
||||||
|
width: 54,
|
||||||
|
height: 36,
|
||||||
|
terrain: createTwentySeventhBattleTerrain()
|
||||||
|
};
|
||||||
|
|
||||||
export const firstBattleUnits: UnitData[] = [
|
export const firstBattleUnits: UnitData[] = [
|
||||||
{
|
{
|
||||||
id: 'liu-bei',
|
id: 'liu-bei',
|
||||||
@@ -4078,6 +4146,30 @@ export const changshaRecruitUnits: UnitData[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const yizhouRecruitUnits: UnitData[] = [
|
||||||
|
{
|
||||||
|
id: 'pang-tong',
|
||||||
|
name: '방통',
|
||||||
|
faction: 'ally',
|
||||||
|
className: '봉추 책사',
|
||||||
|
classKey: 'strategist',
|
||||||
|
level: 16,
|
||||||
|
exp: 0,
|
||||||
|
hp: 36,
|
||||||
|
maxHp: 36,
|
||||||
|
attack: 9,
|
||||||
|
move: 4,
|
||||||
|
stats: { might: 44, intelligence: 96, leadership: 88, agility: 72, luck: 82 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'white-feather-fan', level: 2, exp: 16 },
|
||||||
|
armor: { itemId: 'cloth-armor', level: 2, exp: 12 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 8,
|
||||||
|
y: 24
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
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 },
|
||||||
@@ -10584,6 +10676,435 @@ export const twentySixthBattleUnits: UnitData[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const twentySeventhBattleAllyPositions: Record<string, { x: number; y: number }> = {
|
||||||
|
'liu-bei': { x: 4, y: 30 },
|
||||||
|
'guan-yu': { x: 6, y: 29 },
|
||||||
|
'zhang-fei': { x: 5, y: 32 },
|
||||||
|
'jian-yong': { x: 3, y: 33 },
|
||||||
|
'mi-zhu': { x: 4, y: 34 },
|
||||||
|
'sun-qian': { x: 7, y: 33 },
|
||||||
|
'zhao-yun': { x: 8, y: 30 },
|
||||||
|
'zhuge-liang': { x: 5, y: 30 },
|
||||||
|
'ma-liang': { x: 8, y: 34 },
|
||||||
|
'yi-ji': { x: 9, y: 32 },
|
||||||
|
'gong-zhi': { x: 10, y: 34 },
|
||||||
|
'huang-zhong': { x: 7, y: 31 },
|
||||||
|
'wei-yan': { x: 9, y: 29 }
|
||||||
|
};
|
||||||
|
|
||||||
|
export const twentySeventhBattleUnits: UnitData[] = [
|
||||||
|
...[
|
||||||
|
...firstBattleUnits.filter((unit) => unit.faction === 'ally'),
|
||||||
|
...xuzhouRecruitUnits,
|
||||||
|
...caoBreakRecruitUnits,
|
||||||
|
...liuBiaoRecruitUnits,
|
||||||
|
...zhugeRecruitUnits,
|
||||||
|
...jingzhouRecruitUnits,
|
||||||
|
...guiyangRecruitUnits,
|
||||||
|
...wulingRecruitUnits,
|
||||||
|
...changshaRecruitUnits
|
||||||
|
].map((unit) => placeScenarioUnit(unit, twentySeventhBattleAllyPositions[unit.id] ?? { x: unit.x, y: unit.y })),
|
||||||
|
{
|
||||||
|
id: 'yizhou-scout-a',
|
||||||
|
name: '익주 척후',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '산길 척후',
|
||||||
|
classKey: 'bandit',
|
||||||
|
level: 41,
|
||||||
|
exp: 62,
|
||||||
|
hp: 106,
|
||||||
|
maxHp: 106,
|
||||||
|
attack: 40,
|
||||||
|
move: 4,
|
||||||
|
stats: { might: 128, intelligence: 92, leadership: 104, agility: 120, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'yellow-turban-saber', level: 4, exp: 42 },
|
||||||
|
armor: { itemId: 'rebel-vest', level: 4, exp: 28 },
|
||||||
|
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 16,
|
||||||
|
y: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-scout-b',
|
||||||
|
name: '익주 척후',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '관문 척후',
|
||||||
|
classKey: 'bandit',
|
||||||
|
level: 41,
|
||||||
|
exp: 62,
|
||||||
|
hp: 106,
|
||||||
|
maxHp: 106,
|
||||||
|
attack: 40,
|
||||||
|
move: 4,
|
||||||
|
stats: { might: 128, intelligence: 92, leadership: 104, agility: 120, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'yellow-turban-saber', level: 4, exp: 42 },
|
||||||
|
armor: { itemId: 'rebel-vest', level: 4, exp: 28 },
|
||||||
|
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 20,
|
||||||
|
y: 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-infantry-a',
|
||||||
|
name: '익주 보병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '원군로 보병',
|
||||||
|
classKey: 'yellowTurban',
|
||||||
|
level: 42,
|
||||||
|
exp: 64,
|
||||||
|
hp: 140,
|
||||||
|
maxHp: 140,
|
||||||
|
attack: 44,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 142, intelligence: 92, leadership: 120, agility: 100, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 4, exp: 58 },
|
||||||
|
armor: { itemId: 'lamellar-armor', level: 4, exp: 42 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 24,
|
||||||
|
y: 27
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-infantry-b',
|
||||||
|
name: '익주 보병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '고개 보병',
|
||||||
|
classKey: 'yellowTurban',
|
||||||
|
level: 42,
|
||||||
|
exp: 64,
|
||||||
|
hp: 140,
|
||||||
|
maxHp: 140,
|
||||||
|
attack: 44,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 142, intelligence: 92, leadership: 120, agility: 100, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 4, exp: 58 },
|
||||||
|
armor: { itemId: 'lamellar-armor', level: 4, exp: 42 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 28,
|
||||||
|
y: 24
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-infantry-c',
|
||||||
|
name: '익주 보병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '관문 보병',
|
||||||
|
classKey: 'yellowTurban',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 148,
|
||||||
|
maxHp: 148,
|
||||||
|
attack: 46,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 146, intelligence: 94, leadership: 124, agility: 102, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 5, exp: 8 },
|
||||||
|
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 48 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 35,
|
||||||
|
y: 21
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-infantry-d',
|
||||||
|
name: '익주 보병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '성문 보병',
|
||||||
|
classKey: 'yellowTurban',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 148,
|
||||||
|
maxHp: 148,
|
||||||
|
attack: 46,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 146, intelligence: 94, leadership: 124, agility: 102, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 5, exp: 8 },
|
||||||
|
armor: { itemId: 'reinforced-lamellar', level: 4, exp: 48 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 40,
|
||||||
|
y: 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-archer-a',
|
||||||
|
name: '익주 궁병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '산비탈 궁병',
|
||||||
|
classKey: 'archer',
|
||||||
|
level: 42,
|
||||||
|
exp: 64,
|
||||||
|
hp: 104,
|
||||||
|
maxHp: 104,
|
||||||
|
attack: 42,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 102, intelligence: 102, leadership: 108, agility: 116, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'short-bow', level: 4, exp: 54 },
|
||||||
|
armor: { itemId: 'cloth-armor', level: 4, exp: 34 },
|
||||||
|
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 30,
|
||||||
|
y: 21
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-archer-b',
|
||||||
|
name: '익주 궁병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '관문 궁병',
|
||||||
|
classKey: 'archer',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 108,
|
||||||
|
maxHp: 108,
|
||||||
|
attack: 43,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 104, intelligence: 104, leadership: 110, agility: 118, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'short-bow', level: 5, exp: 4 },
|
||||||
|
armor: { itemId: 'cloth-armor', level: 4, exp: 38 },
|
||||||
|
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 42,
|
||||||
|
y: 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-archer-c',
|
||||||
|
name: '익주 궁병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '성루 궁병',
|
||||||
|
classKey: 'archer',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 108,
|
||||||
|
maxHp: 108,
|
||||||
|
attack: 43,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 104, intelligence: 104, leadership: 110, agility: 118, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'short-bow', level: 5, exp: 4 },
|
||||||
|
armor: { itemId: 'cloth-armor', level: 4, exp: 38 },
|
||||||
|
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 47,
|
||||||
|
y: 19
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-cavalry-a',
|
||||||
|
name: '익주 기병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '원군로 기병',
|
||||||
|
classKey: 'cavalry',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 148,
|
||||||
|
maxHp: 148,
|
||||||
|
attack: 47,
|
||||||
|
move: 5,
|
||||||
|
stats: { might: 146, intelligence: 92, leadership: 120, agility: 120, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 5, exp: 10 },
|
||||||
|
armor: { itemId: 'lamellar-armor', level: 4, exp: 44 },
|
||||||
|
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 22,
|
||||||
|
y: 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-cavalry-b',
|
||||||
|
name: '익주 기병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '산문 기병',
|
||||||
|
classKey: 'cavalry',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 148,
|
||||||
|
maxHp: 148,
|
||||||
|
attack: 47,
|
||||||
|
move: 5,
|
||||||
|
stats: { might: 146, intelligence: 92, leadership: 120, agility: 120, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 5, exp: 10 },
|
||||||
|
armor: { itemId: 'lamellar-armor', level: 4, exp: 44 },
|
||||||
|
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 37,
|
||||||
|
y: 28
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-cavalry-c',
|
||||||
|
name: '익주 기병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '성문 기병',
|
||||||
|
classKey: 'cavalry',
|
||||||
|
level: 44,
|
||||||
|
exp: 68,
|
||||||
|
hp: 154,
|
||||||
|
maxHp: 154,
|
||||||
|
attack: 49,
|
||||||
|
move: 5,
|
||||||
|
stats: { might: 150, intelligence: 94, leadership: 124, agility: 122, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 5, exp: 18 },
|
||||||
|
armor: { itemId: 'lamellar-armor', level: 5, exp: 4 },
|
||||||
|
accessory: { itemId: 'bravery-token', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 45,
|
||||||
|
y: 25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-guard-a',
|
||||||
|
name: '양회 친위대',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '관문 친위대',
|
||||||
|
classKey: 'yellowTurban',
|
||||||
|
level: 44,
|
||||||
|
exp: 68,
|
||||||
|
hp: 158,
|
||||||
|
maxHp: 158,
|
||||||
|
attack: 49,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 152, intelligence: 96, leadership: 128, agility: 104, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 5, exp: 20 },
|
||||||
|
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 8 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 46,
|
||||||
|
y: 15
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-guard-b',
|
||||||
|
name: '양회 친위대',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '성문 친위대',
|
||||||
|
classKey: 'yellowTurban',
|
||||||
|
level: 44,
|
||||||
|
exp: 68,
|
||||||
|
hp: 158,
|
||||||
|
maxHp: 158,
|
||||||
|
attack: 49,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 152, intelligence: 96, leadership: 128, agility: 104, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'iron-spear', level: 5, exp: 20 },
|
||||||
|
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 8 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 49,
|
||||||
|
y: 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-strategist-a',
|
||||||
|
name: '익주 책사',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '관문 책사',
|
||||||
|
classKey: 'archer',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 108,
|
||||||
|
maxHp: 108,
|
||||||
|
attack: 43,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 92, intelligence: 120, leadership: 112, agility: 98, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'short-bow', level: 5, exp: 4 },
|
||||||
|
armor: { itemId: 'cloth-armor', level: 4, exp: 38 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 41,
|
||||||
|
y: 13
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-strategist-b',
|
||||||
|
name: '익주 책사',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '성문 책사',
|
||||||
|
classKey: 'archer',
|
||||||
|
level: 43,
|
||||||
|
exp: 66,
|
||||||
|
hp: 108,
|
||||||
|
maxHp: 108,
|
||||||
|
attack: 43,
|
||||||
|
move: 3,
|
||||||
|
stats: { might: 92, intelligence: 120, leadership: 112, agility: 98, luck: 86 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'short-bow', level: 5, exp: 4 },
|
||||||
|
armor: { itemId: 'cloth-armor', level: 4, exp: 38 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 48,
|
||||||
|
y: 21
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-ambusher-a',
|
||||||
|
name: '복병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '숲길 복병',
|
||||||
|
classKey: 'bandit',
|
||||||
|
level: 42,
|
||||||
|
exp: 64,
|
||||||
|
hp: 112,
|
||||||
|
maxHp: 112,
|
||||||
|
attack: 42,
|
||||||
|
move: 4,
|
||||||
|
stats: { might: 132, intelligence: 94, leadership: 106, agility: 124, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'yellow-turban-saber', level: 4, exp: 48 },
|
||||||
|
armor: { itemId: 'rebel-vest', level: 4, exp: 32 },
|
||||||
|
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 31,
|
||||||
|
y: 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-ambusher-b',
|
||||||
|
name: '복병',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '고개 복병',
|
||||||
|
classKey: 'bandit',
|
||||||
|
level: 42,
|
||||||
|
exp: 64,
|
||||||
|
hp: 112,
|
||||||
|
maxHp: 112,
|
||||||
|
attack: 42,
|
||||||
|
move: 4,
|
||||||
|
stats: { might: 132, intelligence: 94, leadership: 106, agility: 124, luck: 84 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'yellow-turban-saber', level: 4, exp: 48 },
|
||||||
|
armor: { itemId: 'rebel-vest', level: 4, exp: 32 },
|
||||||
|
accessory: { itemId: 'grain-pouch', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 36,
|
||||||
|
y: 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-leader-yang-huai',
|
||||||
|
name: '양회',
|
||||||
|
faction: 'enemy',
|
||||||
|
className: '익주 관문장',
|
||||||
|
classKey: 'rebelLeader',
|
||||||
|
level: 45,
|
||||||
|
exp: 72,
|
||||||
|
hp: 204,
|
||||||
|
maxHp: 204,
|
||||||
|
attack: 50,
|
||||||
|
move: 4,
|
||||||
|
stats: { might: 150, intelligence: 112, leadership: 136, agility: 106, luck: 88 },
|
||||||
|
equipment: {
|
||||||
|
weapon: { itemId: 'leader-axe', level: 5, exp: 20 },
|
||||||
|
armor: { itemId: 'reinforced-lamellar', level: 5, exp: 16 },
|
||||||
|
accessory: { itemId: 'war-manual', level: 1, exp: 0 }
|
||||||
|
},
|
||||||
|
x: 50,
|
||||||
|
y: 16
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export const firstBattleBonds: BattleBond[] = [
|
export const firstBattleBonds: BattleBond[] = [
|
||||||
{
|
{
|
||||||
id: 'liu-bei__guan-yu',
|
id: 'liu-bei__guan-yu',
|
||||||
@@ -10827,6 +11348,33 @@ export const changshaRecruitBonds: BattleBond[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const yizhouRecruitBonds: BattleBond[] = [
|
||||||
|
{
|
||||||
|
id: 'liu-bei__pang-tong',
|
||||||
|
unitIds: ['liu-bei', 'pang-tong'],
|
||||||
|
title: '봉추가 보는 인의',
|
||||||
|
level: 36,
|
||||||
|
exp: 0,
|
||||||
|
description: '유비의 인의와 방통의 날카로운 지략이 만나, 익주의 산문을 명분과 속도로 함께 열어 갑니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'zhuge-liang__pang-tong',
|
||||||
|
unitIds: ['zhuge-liang', 'pang-tong'],
|
||||||
|
title: '와룡과 봉추',
|
||||||
|
level: 34,
|
||||||
|
exp: 0,
|
||||||
|
description: '제갈량의 큰 판과 방통의 과감한 계책이 맞물려, 어려운 지형에서도 새로운 돌파구를 찾아냅니다.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'huang-zhong__pang-tong',
|
||||||
|
unitIds: ['huang-zhong', 'pang-tong'],
|
||||||
|
title: '노궁과 기책',
|
||||||
|
level: 30,
|
||||||
|
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);
|
||||||
@@ -10858,6 +11406,7 @@ export const twentyThirdBattleBonds: BattleBond[] = [...twentySecondBattleBonds]
|
|||||||
export const twentyFourthBattleBonds: BattleBond[] = [...twentyThirdBattleBonds, ...jingzhouRecruitBonds].map(cloneBattleBondForScenario);
|
export const twentyFourthBattleBonds: BattleBond[] = [...twentyThirdBattleBonds, ...jingzhouRecruitBonds].map(cloneBattleBondForScenario);
|
||||||
export const twentyFifthBattleBonds: BattleBond[] = [...twentyFourthBattleBonds, ...guiyangRecruitBonds].map(cloneBattleBondForScenario);
|
export const twentyFifthBattleBonds: BattleBond[] = [...twentyFourthBattleBonds, ...guiyangRecruitBonds].map(cloneBattleBondForScenario);
|
||||||
export const twentySixthBattleBonds: BattleBond[] = [...twentyFifthBattleBonds, ...wulingRecruitBonds].map(cloneBattleBondForScenario);
|
export const twentySixthBattleBonds: BattleBond[] = [...twentyFifthBattleBonds, ...wulingRecruitBonds].map(cloneBattleBondForScenario);
|
||||||
|
export const twentySeventhBattleBonds: BattleBond[] = [...twentySixthBattleBonds, ...changshaRecruitBonds].map(cloneBattleBondForScenario);
|
||||||
|
|
||||||
function createEighthBattleTerrain(): TerrainType[][] {
|
function createEighthBattleTerrain(): TerrainType[][] {
|
||||||
return Array.from({ length: 22 }, (_, y) =>
|
return Array.from({ length: 22 }, (_, y) =>
|
||||||
@@ -11823,6 +12372,62 @@ function createTwentySixthBattleTerrain(): TerrainType[][] {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTwentySeventhBattleTerrain(): TerrainType[][] {
|
||||||
|
return Array.from({ length: 36 }, (_, y) =>
|
||||||
|
Array.from({ length: 54 }, (_, x): TerrainType => {
|
||||||
|
if (x <= 4 && y >= 29) {
|
||||||
|
return 'camp';
|
||||||
|
}
|
||||||
|
if ((x >= 45 && x <= 52 && y >= 12 && y <= 20) || (x >= 41 && x <= 47 && y >= 9 && y <= 14)) {
|
||||||
|
return 'fort';
|
||||||
|
}
|
||||||
|
if ((x >= 18 && x <= 20 && y >= 27 && y <= 29) || (x >= 32 && x <= 34 && y >= 22 && y <= 24) || (x >= 38 && x <= 40 && y >= 16 && y <= 18)) {
|
||||||
|
return 'village';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(y >= 31 && x >= 4 && x <= 15) ||
|
||||||
|
(x >= 14 && x <= 24 && y >= 27 && y <= 31) ||
|
||||||
|
(x >= 23 && x <= 33 && y >= 23 && y <= 27) ||
|
||||||
|
(x >= 32 && x <= 40 && y >= 18 && y <= 23) ||
|
||||||
|
(x >= 39 && x <= 48 && y >= 14 && y <= 18) ||
|
||||||
|
(x >= 47 && x <= 51 && y >= 15 && y <= 17)
|
||||||
|
) {
|
||||||
|
return 'road';
|
||||||
|
}
|
||||||
|
if ((x === 12 || x === 13) && y >= 0 && y <= 25) {
|
||||||
|
return 'river';
|
||||||
|
}
|
||||||
|
if ((x === 29 || x === 30) && y >= 4 && y <= 21) {
|
||||||
|
return 'river';
|
||||||
|
}
|
||||||
|
if ((x === 36 || x === 37) && y >= 25 && y <= 35) {
|
||||||
|
return 'river';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(x >= 4 && x <= 16 && y >= 8 && y <= 20) ||
|
||||||
|
(x >= 17 && x <= 28 && y >= 10 && y <= 21) ||
|
||||||
|
(x >= 31 && x <= 42 && y >= 25 && y <= 35) ||
|
||||||
|
(x >= 41 && x <= 53 && y >= 22 && y <= 33)
|
||||||
|
) {
|
||||||
|
return 'forest';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(x >= 0 && x <= 10 && y <= 11) ||
|
||||||
|
(x >= 20 && x <= 28 && y <= 9) ||
|
||||||
|
(x >= 31 && x <= 43 && y >= 4 && y <= 12) ||
|
||||||
|
(x >= 47 && x <= 53 && y <= 11) ||
|
||||||
|
(x >= 22 && x <= 31 && y >= 29 && y <= 35)
|
||||||
|
) {
|
||||||
|
return 'hill';
|
||||||
|
}
|
||||||
|
if ((x <= 2 && y <= 28) || (x >= 52 && y >= 20) || (x >= 43 && x <= 45 && 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),
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ const unitTexture: Record<string, string> = {
|
|||||||
'gong-zhi': 'unit-guan-yu',
|
'gong-zhi': 'unit-guan-yu',
|
||||||
'huang-zhong': 'unit-rebel-archer',
|
'huang-zhong': 'unit-rebel-archer',
|
||||||
'wei-yan': 'unit-guan-yu',
|
'wei-yan': 'unit-guan-yu',
|
||||||
|
'pang-tong': 'unit-liu-bei',
|
||||||
'rebel-a': 'unit-rebel',
|
'rebel-a': 'unit-rebel',
|
||||||
'rebel-b': 'unit-rebel',
|
'rebel-b': 'unit-rebel',
|
||||||
'rebel-c': 'unit-rebel-archer',
|
'rebel-c': 'unit-rebel-archer',
|
||||||
@@ -316,7 +317,26 @@ const unitTexture: Record<string, string> = {
|
|||||||
'changsha-strategist-b': 'unit-rebel-archer',
|
'changsha-strategist-b': 'unit-rebel-archer',
|
||||||
'changsha-veteran-huang-zhong': 'unit-rebel-archer',
|
'changsha-veteran-huang-zhong': 'unit-rebel-archer',
|
||||||
'changsha-officer-wei-yan': 'unit-guan-yu',
|
'changsha-officer-wei-yan': 'unit-guan-yu',
|
||||||
'changsha-leader-han-xuan': 'unit-rebel-leader'
|
'changsha-leader-han-xuan': 'unit-rebel-leader',
|
||||||
|
'yizhou-scout-a': 'unit-rebel',
|
||||||
|
'yizhou-scout-b': 'unit-rebel',
|
||||||
|
'yizhou-infantry-a': 'unit-rebel',
|
||||||
|
'yizhou-infantry-b': 'unit-rebel',
|
||||||
|
'yizhou-infantry-c': 'unit-rebel',
|
||||||
|
'yizhou-infantry-d': 'unit-rebel',
|
||||||
|
'yizhou-archer-a': 'unit-rebel-archer',
|
||||||
|
'yizhou-archer-b': 'unit-rebel-archer',
|
||||||
|
'yizhou-archer-c': 'unit-rebel-archer',
|
||||||
|
'yizhou-cavalry-a': 'unit-rebel-cavalry',
|
||||||
|
'yizhou-cavalry-b': 'unit-rebel-cavalry',
|
||||||
|
'yizhou-cavalry-c': 'unit-rebel-cavalry',
|
||||||
|
'yizhou-guard-a': 'unit-rebel',
|
||||||
|
'yizhou-guard-b': 'unit-rebel',
|
||||||
|
'yizhou-strategist-a': 'unit-rebel-archer',
|
||||||
|
'yizhou-strategist-b': 'unit-rebel-archer',
|
||||||
|
'yizhou-ambusher-a': 'unit-rebel',
|
||||||
|
'yizhou-ambusher-b': 'unit-rebel',
|
||||||
|
'yizhou-leader-yang-huai': 'unit-rebel-leader'
|
||||||
};
|
};
|
||||||
|
|
||||||
const unitTextureByClass: Partial<Record<UnitClassKey, string>> = {
|
const unitTextureByClass: Partial<Record<UnitClassKey, string>> = {
|
||||||
@@ -742,7 +762,8 @@ const unitStrategyIds: Record<string, string[]> = {
|
|||||||
'yi-ji': ['aid', 'encourage'],
|
'yi-ji': ['aid', 'encourage'],
|
||||||
'gong-zhi': ['encourage'],
|
'gong-zhi': ['encourage'],
|
||||||
'huang-zhong': ['encourage'],
|
'huang-zhong': ['encourage'],
|
||||||
'wei-yan': ['roar']
|
'wei-yan': ['roar'],
|
||||||
|
'pang-tong': ['aid', 'encourage', 'fireTactic']
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialItemStocks: Record<string, Record<string, number>> = {
|
const initialItemStocks: Record<string, Record<string, number>> = {
|
||||||
@@ -758,7 +779,8 @@ const initialItemStocks: Record<string, Record<string, number>> = {
|
|||||||
'yi-ji': { bean: 2, salve: 1 },
|
'yi-ji': { bean: 2, salve: 1 },
|
||||||
'gong-zhi': { bean: 2 },
|
'gong-zhi': { bean: 2 },
|
||||||
'huang-zhong': { bean: 1, wine: 1 },
|
'huang-zhong': { bean: 1, wine: 1 },
|
||||||
'wei-yan': { bean: 2 }
|
'wei-yan': { bean: 2 },
|
||||||
|
'pang-tong': { bean: 1, salve: 1, wine: 1 }
|
||||||
};
|
};
|
||||||
|
|
||||||
const attackRangeByClass: Partial<Record<UnitClassKey, number>> = {
|
const attackRangeByClass: Partial<Record<UnitClassKey, number>> = {
|
||||||
@@ -1093,7 +1115,26 @@ const enemyAiByUnitId: Record<string, EnemyAiBehavior> = {
|
|||||||
'changsha-strategist-b': 'hold',
|
'changsha-strategist-b': 'hold',
|
||||||
'changsha-veteran-huang-zhong': 'hold',
|
'changsha-veteran-huang-zhong': 'hold',
|
||||||
'changsha-officer-wei-yan': 'aggressive',
|
'changsha-officer-wei-yan': 'aggressive',
|
||||||
'changsha-leader-han-xuan': 'guard'
|
'changsha-leader-han-xuan': 'guard',
|
||||||
|
'yizhou-scout-a': 'aggressive',
|
||||||
|
'yizhou-scout-b': 'aggressive',
|
||||||
|
'yizhou-infantry-a': 'guard',
|
||||||
|
'yizhou-infantry-b': 'guard',
|
||||||
|
'yizhou-infantry-c': 'guard',
|
||||||
|
'yizhou-infantry-d': 'guard',
|
||||||
|
'yizhou-archer-a': 'hold',
|
||||||
|
'yizhou-archer-b': 'hold',
|
||||||
|
'yizhou-archer-c': 'hold',
|
||||||
|
'yizhou-cavalry-a': 'aggressive',
|
||||||
|
'yizhou-cavalry-b': 'aggressive',
|
||||||
|
'yizhou-cavalry-c': 'aggressive',
|
||||||
|
'yizhou-guard-a': 'guard',
|
||||||
|
'yizhou-guard-b': 'guard',
|
||||||
|
'yizhou-strategist-a': 'hold',
|
||||||
|
'yizhou-strategist-b': 'hold',
|
||||||
|
'yizhou-ambusher-a': 'aggressive',
|
||||||
|
'yizhou-ambusher-b': 'aggressive',
|
||||||
|
'yizhou-leader-yang-huai': 'guard'
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultEnemyAiByClass: Partial<Record<UnitClassKey, EnemyAiBehavior>> = {
|
const defaultEnemyAiByClass: Partial<Record<UnitClassKey, EnemyAiBehavior>> = {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import twentyThirdBattleMapUrl from '../../assets/images/battle/twenty-third-bat
|
|||||||
import twentyFifthBattleMapUrl from '../../assets/images/battle/twenty-fifth-battle-map.svg';
|
import twentyFifthBattleMapUrl from '../../assets/images/battle/twenty-fifth-battle-map.svg';
|
||||||
import twentyFourthBattleMapUrl from '../../assets/images/battle/twenty-fourth-battle-map.svg';
|
import twentyFourthBattleMapUrl from '../../assets/images/battle/twenty-fourth-battle-map.svg';
|
||||||
import twentySixthBattleMapUrl from '../../assets/images/battle/twenty-sixth-battle-map.svg';
|
import twentySixthBattleMapUrl from '../../assets/images/battle/twenty-sixth-battle-map.svg';
|
||||||
|
import twentySeventhBattleMapUrl from '../../assets/images/battle/twenty-seventh-battle-map.svg';
|
||||||
import twentiethBattleMapUrl from '../../assets/images/battle/twentieth-battle-map.svg';
|
import twentiethBattleMapUrl from '../../assets/images/battle/twentieth-battle-map.svg';
|
||||||
import twelfthBattleMapUrl from '../../assets/images/battle/twelfth-battle-map.svg';
|
import twelfthBattleMapUrl from '../../assets/images/battle/twelfth-battle-map.svg';
|
||||||
import guanYuPortraitUrl from '../../assets/images/portraits/guan-yu.png';
|
import guanYuPortraitUrl from '../../assets/images/portraits/guan-yu.png';
|
||||||
@@ -110,6 +111,7 @@ export class BootScene extends Phaser.Scene {
|
|||||||
this.load.image('battle-map-twenty-fourth', twentyFourthBattleMapUrl);
|
this.load.image('battle-map-twenty-fourth', twentyFourthBattleMapUrl);
|
||||||
this.load.image('battle-map-twenty-fifth', twentyFifthBattleMapUrl);
|
this.load.image('battle-map-twenty-fifth', twentyFifthBattleMapUrl);
|
||||||
this.load.image('battle-map-twenty-sixth', twentySixthBattleMapUrl);
|
this.load.image('battle-map-twenty-sixth', twentySixthBattleMapUrl);
|
||||||
|
this.load.image('battle-map-twenty-seventh', twentySeventhBattleMapUrl);
|
||||||
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);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import {
|
|||||||
wulingRecruitUnits,
|
wulingRecruitUnits,
|
||||||
xuzhouRecruitBonds,
|
xuzhouRecruitBonds,
|
||||||
xuzhouRecruitUnits,
|
xuzhouRecruitUnits,
|
||||||
|
yizhouRecruitBonds,
|
||||||
|
yizhouRecruitUnits,
|
||||||
zhugeRecruitBonds,
|
zhugeRecruitBonds,
|
||||||
zhugeRecruitUnits,
|
zhugeRecruitUnits,
|
||||||
type PortraitKey,
|
type PortraitKey,
|
||||||
@@ -307,9 +309,10 @@ const campaignTimelineChapters: CampaignTimelineChapter[] = [
|
|||||||
'twenty-third-battle-jingzhou-south-entry',
|
'twenty-third-battle-jingzhou-south-entry',
|
||||||
'twenty-fourth-battle-guiyang-persuasion',
|
'twenty-fourth-battle-guiyang-persuasion',
|
||||||
'twenty-fifth-battle-wuling-mountain-road',
|
'twenty-fifth-battle-wuling-mountain-road',
|
||||||
'twenty-sixth-battle-changsha-veteran'
|
'twenty-sixth-battle-changsha-veteran',
|
||||||
|
'twenty-seventh-battle-yizhou-relief-road'
|
||||||
],
|
],
|
||||||
nextHints: ['형주 방위', '익주 진입', '장수 대거 합류']
|
nextHints: ['형주 방위', '익주 진입', '방통 합류']
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'shu-han',
|
id: 'shu-han',
|
||||||
@@ -347,7 +350,8 @@ const campBattleIds = {
|
|||||||
twentyThird: 'twenty-third-battle-jingzhou-south-entry',
|
twentyThird: 'twenty-third-battle-jingzhou-south-entry',
|
||||||
twentyFourth: 'twenty-fourth-battle-guiyang-persuasion',
|
twentyFourth: 'twenty-fourth-battle-guiyang-persuasion',
|
||||||
twentyFifth: 'twenty-fifth-battle-wuling-mountain-road',
|
twentyFifth: 'twenty-fifth-battle-wuling-mountain-road',
|
||||||
twentySixth: 'twenty-sixth-battle-changsha-veteran'
|
twentySixth: 'twenty-sixth-battle-changsha-veteran',
|
||||||
|
twentySeventh: 'twenty-seventh-battle-yizhou-relief-road'
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
const requiredSortieUnitIds = new Set(['liu-bei']);
|
const requiredSortieUnitIds = new Set(['liu-bei']);
|
||||||
@@ -634,6 +638,18 @@ const sortieRulesByBattleId: Partial<Record<BattleScenarioId, SortieRuleDefiniti
|
|||||||
{ unitId: 'zhao-yun', reason: '장사 성문 주변 기병과 척후를 빠르게 정리하기 좋습니다.' }
|
{ unitId: 'zhao-yun', reason: '장사 성문 주변 기병과 척후를 빠르게 정리하기 좋습니다.' }
|
||||||
],
|
],
|
||||||
note: '장사 노장 대면전은 열한 명 중 여섯 명만 고르는 전장입니다. 황충과 위연은 승리 후 합류하므로, 이번에는 예우와 돌파의 균형을 먼저 준비하십시오.'
|
note: '장사 노장 대면전은 열한 명 중 여섯 명만 고르는 전장입니다. 황충과 위연은 승리 후 합류하므로, 이번에는 예우와 돌파의 균형을 먼저 준비하십시오.'
|
||||||
|
},
|
||||||
|
'twenty-seventh-battle-yizhou-relief-road': {
|
||||||
|
maxUnits: 6,
|
||||||
|
recommended: [
|
||||||
|
{ unitId: 'liu-bei', reason: '익주 원군 요청의 명분이자 패배 조건의 중심입니다.' },
|
||||||
|
{ unitId: 'zhuge-liang', reason: '원군이라는 명분과 관문 돌파 계책을 함께 잡는 핵심 책사입니다.' },
|
||||||
|
{ unitId: 'huang-zhong', reason: '산길과 성루의 궁병을 안정적으로 견제할 노련한 원거리 장수입니다.' },
|
||||||
|
{ unitId: 'wei-yan', reason: '좁은 관문 전열을 빠르게 흔들 돌파 역할입니다.' },
|
||||||
|
{ unitId: 'zhao-yun', reason: '긴 원군로에서 척후와 기병을 끊는 기동 핵심입니다.' },
|
||||||
|
{ unitId: 'mi-zhu', reason: '큰 로스터 전투에서 회복 도구와 보급 안정성이 빛납니다.' }
|
||||||
|
],
|
||||||
|
note: '익주 원군로는 열세 명 중 여섯 명만 고르는 전장입니다. 황충·위연을 시험할지, 기존 책사와 보급을 두껍게 둘지 직접 선택하십시오.'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2824,6 +2840,87 @@ const campDialogues: CampDialogue[] = [
|
|||||||
rewardExp: 9
|
rewardExp: 9
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'liu-pang-after-yizhou',
|
||||||
|
title: '봉추가 보는 인의',
|
||||||
|
availableAfterBattleIds: [campBattleIds.twentySeventh],
|
||||||
|
unitIds: ['liu-bei', 'pang-tong'],
|
||||||
|
bondId: 'liu-bei__pang-tong',
|
||||||
|
rewardExp: 30,
|
||||||
|
lines: [
|
||||||
|
'방통: 원군로를 열면서도 마을 창고를 지킨 일은 예상보다 인상적이었습니다.',
|
||||||
|
'유비: 큰 뜻이 백성을 짓밟고 지나가면, 그 뜻은 이미 비어 있는 것이오.',
|
||||||
|
'방통: 그렇다면 그 빈틈 없는 뜻에 제가 조금 날카로운 길을 보태 보겠습니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'ask-pang-tong-yizhou-map',
|
||||||
|
label: '익주 지세를 묻는다',
|
||||||
|
response: '방통은 산문과 관문의 이름을 빠르게 짚으며, 유비군이 서두를 곳과 멈출 곳을 나누었다.',
|
||||||
|
rewardExp: 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'trust-pang-tong-boldness',
|
||||||
|
label: '과감한 계책을 맡긴다',
|
||||||
|
response: '유비가 방통의 과감함을 인정하자, 봉추의 눈빛은 한층 밝아졌다.',
|
||||||
|
rewardExp: 11
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'zhuge-pang-after-yizhou',
|
||||||
|
title: '와룡과 봉추',
|
||||||
|
availableAfterBattleIds: [campBattleIds.twentySeventh],
|
||||||
|
unitIds: ['zhuge-liang', 'pang-tong'],
|
||||||
|
bondId: 'zhuge-liang__pang-tong',
|
||||||
|
rewardExp: 30,
|
||||||
|
lines: [
|
||||||
|
'제갈량: 익주는 산이 많아 큰 판만으로는 늦을 때가 있습니다.',
|
||||||
|
'방통: 그래서 때로는 바른 길보다 빠른 틈을 먼저 보아야 하지요.',
|
||||||
|
'제갈량: 큰 판과 빠른 틈이 함께 있으면, 군은 길을 잃지 않을 것입니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'divide-dragon-phoenix-plans',
|
||||||
|
label: '큰 판과 기책을 나눈다',
|
||||||
|
response: '제갈량은 전체 진군로를 맡고, 방통은 관문과 협곡을 흔드는 기책을 맡기로 했다.',
|
||||||
|
rewardExp: 12
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'compare-yizhou-people',
|
||||||
|
label: '익주 사람을 논한다',
|
||||||
|
response: '두 책사는 지도보다 먼저 사람의 마음을 살피기로 했고, 익주 공략의 색이 조금 더 깊어졌다.',
|
||||||
|
rewardExp: 10
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'huang-pang-after-yizhou',
|
||||||
|
title: '노궁과 기책',
|
||||||
|
availableAfterBattleIds: [campBattleIds.twentySeventh],
|
||||||
|
unitIds: ['huang-zhong', 'pang-tong'],
|
||||||
|
bondId: 'huang-zhong__pang-tong',
|
||||||
|
rewardExp: 26,
|
||||||
|
lines: [
|
||||||
|
'방통: 노장군의 활은 멀리서도 전열의 숨을 끊을 수 있더군요.',
|
||||||
|
'황충: 책사의 계책은 화살보다 먼저 날아간다 들었습니다. 어디를 겨눌지 알려 주시오.',
|
||||||
|
'방통: 다음 관문에서는 활이 닿는 곳부터 적의 마음을 흔들어 보겠습니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'mark-bow-kill-zone',
|
||||||
|
label: '활의 사거리를 표시한다',
|
||||||
|
response: '방통은 산길의 사각을 표시했고, 황충은 그 표시를 따라 활이 닿을 거리를 익혔다.',
|
||||||
|
rewardExp: 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prepare-feigned-retreat',
|
||||||
|
label: '유인책을 준비한다',
|
||||||
|
response: '황충은 물러서는 척하며 적을 끌어낼 거리와, 방통의 매복 신호를 함께 맞췄다.',
|
||||||
|
rewardExp: 9
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -3513,6 +3610,66 @@ const campVisits: CampVisitDefinition[] = [
|
|||||||
itemRewards: ['군영 편성 기록 1']
|
itemRewards: ['군영 편성 기록 1']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'yizhou-entry-council',
|
||||||
|
title: '익주 원군로 회의',
|
||||||
|
location: '익주 길목 군막',
|
||||||
|
availableAfterBattleIds: [campBattleIds.twentySeventh],
|
||||||
|
bondId: 'liu-bei__pang-tong',
|
||||||
|
description: '방통이 합류한 뒤, 유비와 새 책사가 익주로 들어가는 명분과 속도를 처음 조율합니다.',
|
||||||
|
lines: [
|
||||||
|
'방통: 익주는 문이 많은 땅입니다. 모든 문을 두드리면 늦고, 모든 문을 부수면 민심을 잃습니다.',
|
||||||
|
'유비: 그러니 열어야 할 문과 기다려야 할 문을 구분해야 하오.',
|
||||||
|
'군막의 촛불 아래 익주의 관문 이름과 마을 이름이 하나씩 적히기 시작했습니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'choose-yizhou-cause-first',
|
||||||
|
label: '명분을 먼저 세운다',
|
||||||
|
response: '방통은 유비의 뜻에 맞춰 원군 요청 문서를 앞세우고, 군의 이동 속도는 조금 늦추되 마을의 신뢰를 얻도록 조정했다.',
|
||||||
|
bondExp: 24,
|
||||||
|
itemRewards: ['익주 원군 문서 1']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'choose-yizhou-speed-first',
|
||||||
|
label: '관문 속도를 높인다',
|
||||||
|
response: '방통은 빠르게 흔들 수 있는 관문과 우회로를 짚었다. 유비는 백성이 다치지 않는 선을 분명히 못박았다.',
|
||||||
|
bondExp: 22,
|
||||||
|
gold: 240,
|
||||||
|
itemRewards: ['산문 정찰 기록 1']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'fu-pass-road-survey',
|
||||||
|
title: '부수관 길 정찰',
|
||||||
|
location: '익주 산길',
|
||||||
|
availableAfterBattleIds: [campBattleIds.twentySeventh],
|
||||||
|
bondId: 'zhuge-liang__pang-tong',
|
||||||
|
description: '제갈량과 방통이 다음 관문으로 이어지는 산길을 살피며, 출전 후보와 병과 조합을 논합니다.',
|
||||||
|
lines: [
|
||||||
|
'제갈량: 이 길은 한 번 막히면 보급이 끊깁니다. 전열보다 후원이 무너질 수 있습니다.',
|
||||||
|
'방통: 그렇다면 적이 막기 전에 우리가 먼저 막힌 척해야겠군요. 적을 끌어내면 관문은 비게 됩니다.',
|
||||||
|
'두 책사는 산의 그림자와 강의 굽이를 지도 위에 겹쳐 보았습니다.'
|
||||||
|
],
|
||||||
|
choices: [
|
||||||
|
{
|
||||||
|
id: 'prepare-fu-pass-supply',
|
||||||
|
label: '보급선을 먼저 살핀다',
|
||||||
|
response: '제갈량은 보급선을, 방통은 유인 지점을 표시했다. 다음 전투의 출전 선택이 한층 중요해졌다.',
|
||||||
|
bondExp: 22,
|
||||||
|
itemRewards: ['부수관 보급표 1']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prepare-feint-on-pass',
|
||||||
|
label: '유인책을 세운다',
|
||||||
|
response: '방통의 기책과 제갈량의 큰 판이 합쳐져, 적을 관문 밖으로 끌어내는 초안이 마련되었다.',
|
||||||
|
bondExp: 24,
|
||||||
|
gold: 260,
|
||||||
|
itemRewards: ['관문 유인 초안 1']
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -3598,6 +3755,35 @@ export class CampScene extends Phaser.Scene {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (battleId === campBattleIds.twentySeventh) {
|
||||||
|
this.campaign = ensureCampaignRosterUnits(
|
||||||
|
[
|
||||||
|
...xuzhouRecruitUnits,
|
||||||
|
...caoBreakRecruitUnits,
|
||||||
|
...liuBiaoRecruitUnits,
|
||||||
|
...zhugeRecruitUnits,
|
||||||
|
...jingzhouRecruitUnits,
|
||||||
|
...guiyangRecruitUnits,
|
||||||
|
...wulingRecruitUnits,
|
||||||
|
...changshaRecruitUnits,
|
||||||
|
...yizhouRecruitUnits
|
||||||
|
],
|
||||||
|
[
|
||||||
|
...xuzhouRecruitBonds,
|
||||||
|
...caoBreakRecruitBonds,
|
||||||
|
...liuBiaoRecruitBonds,
|
||||||
|
...zhugeRecruitBonds,
|
||||||
|
...jingzhouRecruitBonds,
|
||||||
|
...guiyangRecruitBonds,
|
||||||
|
...wulingRecruitBonds,
|
||||||
|
...changshaRecruitBonds,
|
||||||
|
...yizhouRecruitBonds
|
||||||
|
]
|
||||||
|
);
|
||||||
|
this.report = this.campaign.firstBattleReport ?? this.report;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (battleId === campBattleIds.twentySixth) {
|
if (battleId === campBattleIds.twentySixth) {
|
||||||
this.campaign = ensureCampaignRosterUnits(
|
this.campaign = ensureCampaignRosterUnits(
|
||||||
[
|
[
|
||||||
@@ -3746,6 +3932,9 @@ export class CampScene extends Phaser.Scene {
|
|||||||
|
|
||||||
private currentCampTitle() {
|
private currentCampTitle() {
|
||||||
const battleId = this.currentCampBattleId();
|
const battleId = this.currentCampBattleId();
|
||||||
|
if (battleId === campBattleIds.twentySeventh) {
|
||||||
|
return '익주 원군로 후 군영';
|
||||||
|
}
|
||||||
if (battleId === campBattleIds.twentySixth) {
|
if (battleId === campBattleIds.twentySixth) {
|
||||||
return '장사 노장 대면 후 군영';
|
return '장사 노장 대면 후 군영';
|
||||||
}
|
}
|
||||||
@@ -4047,15 +4236,16 @@ export class CampScene extends Phaser.Scene {
|
|||||||
const allies = this.sortieAllies();
|
const allies = this.sortieAllies();
|
||||||
const selectedCount = this.selectedSortieUnitIds.length;
|
const selectedCount = this.selectedSortieUnitIds.length;
|
||||||
const maxUnits = this.sortieMaxUnits();
|
const maxUnits = this.sortieMaxUnits();
|
||||||
const rowGap = allies.length > 7 ? 30 : allies.length > 5 ? 38 : allies.length > 4 ? 43 : 48;
|
const denseRoster = allies.length > 12;
|
||||||
const rowHeight = allies.length > 7 ? 24 : rowGap - 6;
|
const rowGap = denseRoster ? 17 : allies.length > 7 ? 30 : allies.length > 5 ? 38 : allies.length > 4 ? 43 : 48;
|
||||||
|
const rowHeight = denseRoster ? 15 : allies.length > 7 ? 24 : rowGap - 6;
|
||||||
this.trackSortie(
|
this.trackSortie(
|
||||||
this.add.text(x + 18, y + 14, `출전 무장 선택 ${selectedCount}/${maxUnits}`, this.textStyle(18, '#f2e3bf', true))
|
this.add.text(x + 18, y + 14, `출전 무장 선택 ${selectedCount}/${maxUnits}`, this.textStyle(18, '#f2e3bf', true))
|
||||||
).setDepth(depth + 1);
|
).setDepth(depth + 1);
|
||||||
this.trackSortie(this.add.text(x + width - 18, y + 17, '클릭해서 출전/대기 전환', this.textStyle(12, '#9fb0bf'))).setOrigin(1, 0).setDepth(depth + 1);
|
this.trackSortie(this.add.text(x + width - 18, y + 17, '클릭해서 출전/대기 전환', this.textStyle(12, '#9fb0bf'))).setOrigin(1, 0).setDepth(depth + 1);
|
||||||
|
|
||||||
allies.forEach((unit, index) => {
|
allies.forEach((unit, index) => {
|
||||||
const rowY = y + 50 + index * rowGap;
|
const rowY = y + (denseRoster ? 48 : 50) + index * rowGap;
|
||||||
const selected = this.isSortieSelected(unit.id);
|
const selected = this.isSortieSelected(unit.id);
|
||||||
const required = requiredSortieUnitIds.has(unit.id);
|
const required = requiredSortieUnitIds.has(unit.id);
|
||||||
const recruited = !foundingSortieUnitIds.has(unit.id);
|
const recruited = !foundingSortieUnitIds.has(unit.id);
|
||||||
@@ -4070,17 +4260,21 @@ export class CampScene extends Phaser.Scene {
|
|||||||
|
|
||||||
const marker = selected ? '출전' : '대기';
|
const marker = selected ? '출전' : '대기';
|
||||||
const markerLabel = required ? '필수' : recommendation ? '추천' : recruited ? '합류' : marker;
|
const markerLabel = required ? '필수' : recommendation ? '추천' : recruited ? '합류' : marker;
|
||||||
this.trackSortie(this.add.text(x + 30, rowY, markerLabel, this.textStyle(12, required || recommendation ? '#ffdf7b' : selected ? '#a8ffd0' : '#9fb0bf', true))).setDepth(depth + 2);
|
this.trackSortie(this.add.text(x + 30, rowY, markerLabel, this.textStyle(denseRoster ? 10 : 12, required || recommendation ? '#ffdf7b' : selected ? '#a8ffd0' : '#9fb0bf', true))).setDepth(depth + 2);
|
||||||
this.trackSortie(this.add.text(x + 84, rowY, `${unit.name} Lv ${unit.level}`, this.textStyle(14, selected ? '#f2e3bf' : '#87919c', true))).setDepth(depth + 2);
|
this.trackSortie(this.add.text(x + (denseRoster ? 78 : 84), rowY, `${unit.name} Lv ${unit.level}`, this.textStyle(denseRoster ? 12 : 14, selected ? '#f2e3bf' : '#87919c', true))).setDepth(depth + 2);
|
||||||
this.trackSortie(this.add.text(x + 214, rowY, this.compactText(`${unit.className} · ${getUnitClass(unit.classKey).role}`, 19), this.textStyle(12, selected ? '#d4dce6' : '#77818c', true))).setDepth(depth + 2);
|
this.trackSortie(this.add.text(x + (denseRoster ? 194 : 214), rowY, this.compactText(`${unit.className} · ${getUnitClass(unit.classKey).role}`, denseRoster ? 17 : 19), this.textStyle(denseRoster ? 10 : 12, selected ? '#d4dce6' : '#77818c', true))).setDepth(depth + 2);
|
||||||
this.trackSortie(this.add.text(x + 424, rowY, `병력 ${unit.hp}/${unit.maxHp}`, this.textStyle(12, selected ? '#d4dce6' : '#77818c', true))).setDepth(depth + 2);
|
this.trackSortie(this.add.text(x + 424, rowY, denseRoster ? this.compactText(summary.statLine, 23) : `병력 ${unit.hp}/${unit.maxHp}`, this.textStyle(denseRoster ? 10 : 12, selected ? '#d4dce6' : '#77818c', true))).setDepth(depth + 2);
|
||||||
this.drawSortieBar(x + 514, rowY + 6, 74, 7, unit.hp / unit.maxHp, selected ? palette.green : 0x53606c, depth + 2);
|
if (!denseRoster) {
|
||||||
this.trackSortie(this.add.text(x + 610, rowY, this.compactText(summary.terrainLine, 13), this.textStyle(11, selected ? '#d8b15f' : '#77818c', true))).setDepth(depth + 2);
|
this.drawSortieBar(x + 514, rowY + 6, 74, 7, unit.hp / unit.maxHp, selected ? palette.green : 0x53606c, depth + 2);
|
||||||
|
}
|
||||||
|
this.trackSortie(this.add.text(x + 610, rowY, this.compactText(denseRoster ? summary.bondLine : summary.terrainLine, denseRoster ? 15 : 13), this.textStyle(11, selected ? (denseRoster ? '#a8ffd0' : '#d8b15f') : '#77818c', true))).setDepth(depth + 2);
|
||||||
|
|
||||||
this.renderSortieEquipmentIcons(unit, x + 84, rowY + 19, selected, depth + 2);
|
if (!denseRoster) {
|
||||||
this.trackSortie(this.add.text(x + 162, rowY + 18, this.compactText(summary.equipmentLine, 34), this.textStyle(11, selected ? '#c8d2dd' : '#68727e'))).setDepth(depth + 2);
|
this.renderSortieEquipmentIcons(unit, x + 84, rowY + 19, selected, depth + 2);
|
||||||
this.trackSortie(this.add.text(x + 424, rowY + 18, this.compactText(summary.statLine, 23), this.textStyle(11, selected ? '#c8d2dd' : '#68727e'))).setDepth(depth + 2);
|
this.trackSortie(this.add.text(x + 162, rowY + 18, this.compactText(summary.equipmentLine, 34), this.textStyle(11, selected ? '#c8d2dd' : '#68727e'))).setDepth(depth + 2);
|
||||||
this.trackSortie(this.add.text(x + 610, rowY + 18, this.compactText(summary.bondLine, 15), this.textStyle(11, selected ? '#a8ffd0' : '#68727e', selected))).setDepth(depth + 2);
|
this.trackSortie(this.add.text(x + 424, rowY + 18, this.compactText(summary.statLine, 23), this.textStyle(11, selected ? '#c8d2dd' : '#68727e'))).setDepth(depth + 2);
|
||||||
|
this.trackSortie(this.add.text(x + 610, rowY + 18, this.compactText(summary.bondLine, 15), this.textStyle(11, selected ? '#a8ffd0' : '#68727e', selected))).setDepth(depth + 2);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
twentyFifthBattleScenario,
|
twentyFifthBattleScenario,
|
||||||
twentyFourthBattleScenario,
|
twentyFourthBattleScenario,
|
||||||
twentySixthBattleScenario,
|
twentySixthBattleScenario,
|
||||||
|
twentySeventhBattleScenario,
|
||||||
twentiethBattleScenario,
|
twentiethBattleScenario,
|
||||||
twelfthBattleScenario,
|
twelfthBattleScenario,
|
||||||
thirdBattleScenario
|
thirdBattleScenario
|
||||||
@@ -351,7 +352,8 @@ export class TitleScene extends Phaser.Scene {
|
|||||||
campaign.step === 'twenty-third-camp' ||
|
campaign.step === 'twenty-third-camp' ||
|
||||||
campaign.step === 'twenty-fourth-camp' ||
|
campaign.step === 'twenty-fourth-camp' ||
|
||||||
campaign.step === 'twenty-fifth-camp' ||
|
campaign.step === 'twenty-fifth-camp' ||
|
||||||
campaign.step === 'twenty-sixth-camp'
|
campaign.step === 'twenty-sixth-camp' ||
|
||||||
|
campaign.step === 'twenty-seventh-camp'
|
||||||
) {
|
) {
|
||||||
this.scene.start('CampScene');
|
this.scene.start('CampScene');
|
||||||
return;
|
return;
|
||||||
@@ -487,6 +489,11 @@ export class TitleScene extends Phaser.Scene {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (campaign.step === 'twenty-seventh-battle') {
|
||||||
|
this.scene.start('BattleScene', { battleId: twentySeventhBattleScenario.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;
|
||||||
|
|||||||
@@ -93,7 +93,9 @@ export type CampaignStep =
|
|||||||
| 'twenty-fifth-battle'
|
| 'twenty-fifth-battle'
|
||||||
| 'twenty-fifth-camp'
|
| 'twenty-fifth-camp'
|
||||||
| 'twenty-sixth-battle'
|
| 'twenty-sixth-battle'
|
||||||
| 'twenty-sixth-camp';
|
| 'twenty-sixth-camp'
|
||||||
|
| 'twenty-seventh-battle'
|
||||||
|
| 'twenty-seventh-camp';
|
||||||
|
|
||||||
export type CampaignUnitProgressSnapshot = {
|
export type CampaignUnitProgressSnapshot = {
|
||||||
unitId: string;
|
unitId: string;
|
||||||
@@ -181,7 +183,8 @@ const campaignBattleSteps: Record<string, { victory: CampaignStep; retry: Campai
|
|||||||
'twenty-third-battle-jingzhou-south-entry': { victory: 'twenty-third-camp', retry: 'twenty-third-battle' },
|
'twenty-third-battle-jingzhou-south-entry': { victory: 'twenty-third-camp', retry: 'twenty-third-battle' },
|
||||||
'twenty-fourth-battle-guiyang-persuasion': { victory: 'twenty-fourth-camp', retry: 'twenty-fourth-battle' },
|
'twenty-fourth-battle-guiyang-persuasion': { victory: 'twenty-fourth-camp', retry: 'twenty-fourth-battle' },
|
||||||
'twenty-fifth-battle-wuling-mountain-road': { victory: 'twenty-fifth-camp', retry: 'twenty-fifth-battle' },
|
'twenty-fifth-battle-wuling-mountain-road': { victory: 'twenty-fifth-camp', retry: 'twenty-fifth-battle' },
|
||||||
'twenty-sixth-battle-changsha-veteran': { victory: 'twenty-sixth-camp', retry: 'twenty-sixth-battle' }
|
'twenty-sixth-battle-changsha-veteran': { victory: 'twenty-sixth-camp', retry: 'twenty-sixth-battle' },
|
||||||
|
'twenty-seventh-battle-yizhou-relief-road': { victory: 'twenty-seventh-camp', retry: 'twenty-seventh-battle' }
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CampaignSaveSlotSummary = {
|
export type CampaignSaveSlotSummary = {
|
||||||
|
|||||||
Reference in New Issue
Block a user