From 2f07f9f869d9203b9eba9c0b9d8f334548e8ca25 Mon Sep 17 00:00:00 2001 From: umbralOptimatum Date: Tue, 12 Nov 2024 23:46:39 -0500 Subject: [PATCH] fix(dungeons): Prevent unwinnable Dungeons when no bosses are unlocked (#5600) * Prevent Dungeon no-unlocked-boss traps * Display dungeon enemy list if bosses are locked * Ensure Dungeon Guides can't enter locked dungeons * Linting * Clean up approach, same general concept * Small fix * Adjust Golbat boss in Sealed Chamber --------- Co-authored-by: CypherX --- src/scripts/dungeons/Dungeon.ts | 6 ++- src/scripts/dungeons/DungeonGuides.ts | 13 +++++- src/scripts/dungeons/DungeonRunner.ts | 57 +++++++++++++++++---------- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/scripts/dungeons/Dungeon.ts b/src/scripts/dungeons/Dungeon.ts index 56065f8274..0e3695e013 100644 --- a/src/scripts/dungeons/Dungeon.ts +++ b/src/scripts/dungeons/Dungeon.ts @@ -179,6 +179,10 @@ class Dungeon { } } + public hasUnlockedBoss(): boolean { + return this.bossList.some(boss => boss.options?.requirement?.isCompleted() ?? true); + } + /** * Retreives the weights for all the possible bosses */ @@ -3847,7 +3851,7 @@ dungeonList['Sealed Chamber'] = new Dungeon('Sealed Chamber', }, 500000, [ - new DungeonBossPokemon('Golbat', 4500000, 20, {hide: true, requirement: new QuestLineStepCompletedRequirement('The Three Golems', 8, GameConstants.AchievementOption.less)}), + new DungeonBossPokemon('Golbat', 4500000, 20, {weight: 0.25}), new DungeonBossPokemon('Regirock', 4500000, 20, {requirement: new QuestLineStepCompletedRequirement('The Three Golems', 8)}), new DungeonBossPokemon('Regice', 4500000, 20, {requirement: new QuestLineStepCompletedRequirement('The Three Golems', 8)}), new DungeonBossPokemon('Registeel', 4500000, 20, {requirement: new QuestLineStepCompletedRequirement('The Three Golems', 8)}), diff --git a/src/scripts/dungeons/DungeonGuides.ts b/src/scripts/dungeons/DungeonGuides.ts index bd1f916d1e..950f2c747a 100644 --- a/src/scripts/dungeons/DungeonGuides.ts +++ b/src/scripts/dungeons/DungeonGuides.ts @@ -152,6 +152,7 @@ class DungeonGuides { return; } const guide = this.list[this.selected()]; + const dungeon = player.town.dungeon; // Check player has enough currency if (!this.canAfford()) { Notifier.notify({ @@ -162,6 +163,16 @@ class DungeonGuides { }); return; } + // Just in case the dungeon is locked or something + if (!DungeonRunner.canStartDungeon(dungeon)) { + Notifier.notify({ + title: `[DUNGEON GUIDE] ${guide.name}`, + message: 'You can\'t access that dungeon right now!', + type: NotificationConstants.NotificationOption.warning, + timeout: 30 * GameConstants.SECOND, + }); + return; + } // Charge the player and hire the guide guide.hire(); this.calcCost().forEach((cost) => App.game.wallet.loseAmount(cost)); @@ -169,7 +180,7 @@ class DungeonGuides { // Hide modals $('.modal.show').modal('hide'); // Start the dungeon - DungeonRunner.initializeDungeon(player.town.dungeon); + DungeonRunner.initializeDungeon(dungeon); } public static getRandomWeightedNearbyTile(nearbyTiles: DungeonTile[]): DungeonTile { diff --git a/src/scripts/dungeons/DungeonRunner.ts b/src/scripts/dungeons/DungeonRunner.ts index b707ba00ef..9f387a2c2a 100644 --- a/src/scripts/dungeons/DungeonRunner.ts +++ b/src/scripts/dungeons/DungeonRunner.ts @@ -20,32 +20,37 @@ class DungeonRunner { public static continuousInteractionInput = false; public static initializeDungeon(dungeon: Dungeon) { - if (!dungeon.isUnlocked()) { - if (dungeon.name === 'Viridian Forest') { - Notifier.notify({ - message: 'You need the Dungeon Ticket to access dungeons.\nCheck out the shop at Viridian City.', - type: NotificationConstants.NotificationOption.danger, - }); - return false; + if (!DungeonRunner.canStartDungeon(dungeon)) { + let message; + let notifType; + if (!dungeon.isUnlocked()) { + if (dungeon.name === 'Viridian Forest') { + message = 'You need the Dungeon Ticket to access dungeons.\nCheck out the shop at Viridian City.'; + notifType = NotificationConstants.NotificationOption.danger; + } else { + message = `You don't have access to this dungeon yet.\n${dungeon.getRequirementHints()}`; + notifType = NotificationConstants.NotificationOption.warning; + } + } else if (!dungeon.hasUnlockedBoss()) { + message = 'You can\'t access this dungeon right now because all of its bosses are locked.'; + notifType = NotificationConstants.NotificationOption.warning; + } else if (!DungeonGuides.hired() && !DungeonRunner.hasEnoughTokens(dungeon)) { + message = 'You don\'t have enough Dungeon Tokens.'; + notifType = NotificationConstants.NotificationOption.danger; } else { - Notifier.notify({ - message: `You don't have access to this dungeon yet.\n${dungeon.getRequirementHints()}`, - type: NotificationConstants.NotificationOption.warning, - }); - return false; + message = 'You can\'t enter this dungeon right now.'; + notifType = NotificationConstants.NotificationOption.danger; } + Notifier.notify({ + message: message, + type: notifType, + }); + return false; } DungeonRunner.dungeon = dungeon; // Only charge the player if they aren't using a dungeon guide as they are charged when they start the dungeon if (!DungeonGuides.hired()) { - if (!DungeonRunner.hasEnoughTokens()) { - Notifier.notify({ - message: 'You don\'t have enough Dungeon Tokens.', - type: NotificationConstants.NotificationOption.danger, - }); - return false; - } App.game.wallet.loseAmount(new Amount(DungeonRunner.dungeon.tokenCost, GameConstants.Currency.dungeonToken)); } // Reset any trainers/pokemon if there was one previously @@ -254,6 +259,12 @@ class DungeonRunner { return; } + if (!DungeonRunner.dungeon.hasUnlockedBoss()) { + // Prevent the player from being unable to finish the dungeon if somehow all the bosses became locked after entering + DungeonRunner.dungeonWon(); + return; + } + DungeonRunner.fightingBoss(true); DungeonBattle.generateNewBoss(); } @@ -342,8 +353,12 @@ class DungeonRunner { }); } - public static hasEnoughTokens() { - return App.game.wallet.hasAmount(new Amount(DungeonRunner.dungeon.tokenCost, GameConstants.Currency.dungeonToken)); + public static canStartDungeon(dungeon: Dungeon = DungeonRunner.dungeon) { + return (DungeonGuides.hired() || DungeonRunner.hasEnoughTokens(dungeon)) && dungeon.isUnlocked() && dungeon.hasUnlockedBoss(); + } + + public static hasEnoughTokens(dungeon: Dungeon = DungeonRunner.dungeon) { + return App.game.wallet.hasAmount(new Amount(dungeon.tokenCost, GameConstants.Currency.dungeonToken)); } public static dungeonLevel(): number {