From d05b2ead53a44ab41eb8b313959705f1ec9d420f Mon Sep 17 00:00:00 2001 From: megaproxy Date: Tue, 15 Jul 2025 16:58:18 +0100 Subject: [PATCH] Fix exploration and battle system state management bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed exploration bug: prevent multiple \!explore when encounter is active - Fixed battle bug: prevent starting multiple battles from exploration encounters - Enforced exploration encounter workflow: must choose fight/capture/flee before exploring again - Fixed \!gym challenge to use player's current location instead of requiring location parameter - Added proper state management to prevent race conditions - Improved user experience with clear error messages for active encounters 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- modules/battle_system.py | 6 ++++ modules/exploration.py | 59 ++++++++++++++++++++++++++++++++++++++-- modules/gym_battles.py | 46 ++++++++++++++++++++----------- 3 files changed, 92 insertions(+), 19 deletions(-) diff --git a/modules/battle_system.py b/modules/battle_system.py index 4e3e626..911392f 100644 --- a/modules/battle_system.py +++ b/modules/battle_system.py @@ -52,6 +52,12 @@ class BattleSystem(BaseModule): self.send_message(channel, f"{nickname}: You're already in battle! Use !attack or !flee.") return + # Check if already in gym battle + gym_battle = await self.database.get_active_gym_battle(player["id"]) + if gym_battle: + self.send_message(channel, f"{nickname}: You're already in a gym battle! Finish your gym battle first.") + return + # Get player's active pet pets = await self.database.get_player_pets(player["id"], active_only=True) if not pets: diff --git a/modules/exploration.py b/modules/exploration.py index e4693f4..c221cf8 100644 --- a/modules/exploration.py +++ b/modules/exploration.py @@ -7,7 +7,7 @@ class Exploration(BaseModule): """Handles exploration, travel, location, weather, and wild commands""" def get_commands(self): - return ["explore", "travel", "location", "where", "weather", "wild", "catch", "capture"] + return ["explore", "travel", "location", "where", "weather", "wild", "catch", "capture", "flee"] async def handle_command(self, channel, nickname, command, args): if command == "explore": @@ -22,6 +22,8 @@ class Exploration(BaseModule): await self.cmd_wild(channel, nickname, args) elif command in ["catch", "capture"]: await self.cmd_catch(channel, nickname) + elif command == "flee": + await self.cmd_flee_encounter(channel, nickname) async def cmd_explore(self, channel, nickname): """Explore current location""" @@ -29,6 +31,18 @@ class Exploration(BaseModule): if not player: return + # Check if player has an active encounter that must be resolved first + if player["id"] in self.bot.active_encounters: + current_encounter = self.bot.active_encounters[player["id"]] + self.send_message(channel, f"{nickname}: You already have an active encounter with a wild {current_encounter['species_name']}! You must choose to !battle, !catch, or !flee before exploring again.") + return + + # Check if player is in an active battle + active_battle = await self.game_engine.battle_engine.get_active_battle(player["id"]) + if active_battle: + self.send_message(channel, f"{nickname}: You're currently in battle! Finish your battle before exploring.") + return + encounter = await self.game_engine.explore_location(player["id"]) if encounter["type"] == "error": @@ -51,7 +65,7 @@ class Exploration(BaseModule): self.send_message(channel, f"🐾 {nickname}: A wild Level {pet['level']} {pet['species_name']} ({type_str}) appeared in {encounter['location']}!") - self.send_message(channel, f"Choose your action: !battle to fight it, or !catch to try catching it directly!") + self.send_message(channel, f"Choose your action: !battle to fight it, !catch to try catching it directly, or !flee to escape!") async def cmd_travel(self, channel, nickname, args): """Travel to a different location""" @@ -196,6 +210,13 @@ class Exploration(BaseModule): # Check if player is in an active battle active_battle = await self.game_engine.battle_engine.get_active_battle(player["id"]) + gym_battle = await self.database.get_active_gym_battle(player["id"]) + + if gym_battle: + # Can't catch pets during gym battles + self.send_message(channel, f"{nickname}: You can't catch pets during gym battles! Focus on the challenge!") + return + if active_battle: # Catching during battle wild_pet = active_battle["wild_pet"] @@ -297,4 +318,36 @@ class Exploration(BaseModule): """Display level up information (shared with battle system)""" from .battle_system import BattleSystem battle_system = BattleSystem(self.bot, self.database, self.game_engine) - await battle_system.handle_level_up_display(channel, nickname, exp_result) \ No newline at end of file + await battle_system.handle_level_up_display(channel, nickname, exp_result) + + async def cmd_flee_encounter(self, channel, nickname): + """Flee from an active encounter without battling""" + player = await self.require_player(channel, nickname) + if not player: + return + + # Check if player has an active encounter to flee from + if player["id"] not in self.bot.active_encounters: + self.send_message(channel, f"{nickname}: You don't have an active encounter to flee from!") + return + + # Check if player is in an active battle - can't flee from exploration if in battle + active_battle = await self.game_engine.battle_engine.get_active_battle(player["id"]) + if active_battle: + self.send_message(channel, f"{nickname}: You're in battle! Use the battle system's !flee command to escape combat.") + return + + # Check if player is in a gym battle + gym_battle = await self.database.get_active_gym_battle(player["id"]) + if gym_battle: + self.send_message(channel, f"{nickname}: You're in a gym battle! Use !forfeit to leave the gym challenge.") + return + + # Get encounter details for message + encounter = self.bot.active_encounters[player["id"]] + + # Remove the encounter + del self.bot.active_encounters[player["id"]] + + self.send_message(channel, f"💨 {nickname}: You fled from the wild {encounter['species_name']}! You can now explore again.") + self.send_message(channel, f"💡 Use !explore to search for another encounter!") \ No newline at end of file diff --git a/modules/gym_battles.py b/modules/gym_battles.py index 00fb7aa..0fbd481 100644 --- a/modules/gym_battles.py +++ b/modules/gym_battles.py @@ -66,7 +66,7 @@ class GymBattles(BaseModule): f" Status: {status} | Next difficulty: {difficulty}") self.send_message(channel, - f"💡 Use '!gym challenge \"\"' to battle!") + f"💡 Use '!gym challenge' to battle (gym name optional if only one gym in location)!") async def cmd_gym_list_all(self, channel, nickname): """List all gyms across all locations""" @@ -97,10 +97,6 @@ class GymBattles(BaseModule): async def cmd_gym_challenge(self, channel, nickname, args): """Challenge a gym""" - if not args: - self.send_message(channel, f"{nickname}: Specify a gym to challenge! Example: !gym challenge \"Forest Guardian\"") - return - player = await self.require_player(channel, nickname) if not player: return @@ -111,19 +107,37 @@ class GymBattles(BaseModule): self.send_message(channel, f"{nickname}: You are not in a valid location! Use !travel to go somewhere first.") return - gym_name = " ".join(self.normalize_input(args)).strip('"') + # Get available gyms in current location + available_gyms = await self.database.get_gyms_in_location(location["id"]) + if not available_gyms: + self.send_message(channel, f"{nickname}: No gyms found in {location['name']}! Try traveling to a different location.") + return - # Look for gym in player's current location (case-insensitive) - gym = await self.database.get_gym_by_name_in_location(gym_name, location["id"]) - if not gym: - # List available gyms in current location for helpful error message - available_gyms = await self.database.get_gyms_in_location(location["id"]) - if available_gyms: + gym = None + + if not args: + # No gym name provided - auto-challenge if only one gym, otherwise list options + if len(available_gyms) == 1: + gym = available_gyms[0] + self.send_message(channel, f"🏛️ {nickname}: Challenging the {gym['name']} gym in {location['name']}!") + else: + # Multiple gyms - show list and ask user to specify + gym_list = ", ".join([f'"{g["name"]}"' for g in available_gyms]) + self.send_message(channel, f"{nickname}: Multiple gyms found in {location['name']}! Specify which gym to challenge:") + self.send_message(channel, f"Available gyms: {gym_list}") + self.send_message(channel, f"💡 Use: !gym challenge \"\"") + return + else: + # Gym name provided - find specific gym + gym_name = " ".join(self.normalize_input(args)).strip('"') + + # Look for gym in player's current location (case-insensitive) + gym = await self.database.get_gym_by_name_in_location(gym_name, location["id"]) + if not gym: + # List available gyms in current location for helpful error message gym_list = ", ".join([f'"{g["name"]}"' for g in available_gyms]) self.send_message(channel, f"{nickname}: No gym named '{gym_name}' found in {location['name']}! Available gyms: {gym_list}") - else: - self.send_message(channel, f"{nickname}: No gyms found in {location['name']}! Try traveling to a different location.") - return + return # Check if player has active pets active_pets = await self.database.get_active_pets(player["id"]) @@ -311,7 +325,7 @@ class GymBattles(BaseModule): return # This will show a summary - for detailed view they can use !gym list - self.send_message(channel, f"🏆 {nickname}: Use !gym list to see all gym progress, or check your profile at: http://petz.rdx4.com/player/{nickname}") + self.send_message(channel, f"🏆 {nickname}: Use !gym list to see all gym progress, or check your profile at: http://petz.rdx4.com/player/{nickname}#gym-badges") async def cmd_forfeit(self, channel, nickname): """Forfeit the current gym battle"""