#!/usr/bin/env python3 """Battle system commands module for PetBot""" from .base_module import BaseModule class BattleSystem(BaseModule): """Handles battle, attack, flee, and moves commands""" # IRC color codes for different move types MOVE_TYPE_COLORS = { 'Fire': '\x0304', # Red 'Water': '\x0312', # Light Blue 'Grass': '\x0303', # Green 'Electric': '\x0308', # Yellow 'Rock': '\x0307', # Orange/Brown 'Normal': '\x0314', # Gray 'Physical': '\x0313', # Pink/Magenta (fallback for category) 'Special': '\x0310' # Cyan (fallback for category) } def get_commands(self): return ["battle", "attack", "flee", "moves"] def get_move_color(self, move_type): """Get IRC color code for a move type""" return self.MOVE_TYPE_COLORS.get(move_type, '\x0312') # Default to light blue async def handle_command(self, channel, nickname, command, args): if command == "battle": await self.cmd_battle(channel, nickname) elif command == "attack": await self.cmd_attack(channel, nickname, args) elif command == "flee": await self.cmd_flee(channel, nickname) elif command == "moves": await self.cmd_moves(channel, nickname) async def cmd_battle(self, channel, nickname): """Start a battle with encountered wild pet""" player = await self.require_player(channel, nickname) if not player: return # Check if player has an active encounter if player["id"] not in self.bot.active_encounters: self.send_message(channel, f"{nickname}: You need to !explore first to find a pet to battle!") return # Check if already 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 already in battle! Use !attack or !flee.") return # Get player's active pet pets = await self.database.get_player_pets(player["id"], active_only=True) if not pets: self.send_message(channel, f"{nickname}: You need an active pet to battle! Use !team to check your pets.") return player_pet = pets[0] # Use first active pet wild_pet = self.bot.active_encounters[player["id"]] # Start battle battle = await self.game_engine.battle_engine.start_battle(player["id"], player_pet, wild_pet) # Condensed battle start message with all info moves_colored = " | ".join([ f"{self.get_move_color(move['type'])}{move['name']}\x0F" for move in battle["available_moves"] ]) battle_start_msg = (f"⚔️ {nickname}: Battle! Your {player_pet['species_name']} (Lv.{player_pet['level']}, " f"{battle['player_hp']}/{player_pet['max_hp']} HP) vs Wild {wild_pet['species_name']} " f"(Lv.{wild_pet['level']}, {battle['wild_hp']}/{wild_pet['max_hp']} HP)") self.send_message(channel, battle_start_msg) self.send_message(channel, f"🎯 Moves: {moves_colored} | Use !attack ") async def cmd_attack(self, channel, nickname, args): """Use a move in battle""" if not args: self.send_message(channel, f"{nickname}: Specify a move to use! Example: !attack Tackle") return player = await self.require_player(channel, nickname) if not player: return move_name = " ".join(args).title() # Normalize to Title Case result = await self.game_engine.battle_engine.execute_battle_turn(player["id"], move_name) if "error" in result: self.send_message(channel, f"{nickname}: {result['error']}") return # Display battle results - condensed with player names for clarity for action in result["results"]: # Determine attacker and target with player context if "Wild" in action['attacker']: attacker = f"Wild {action['attacker']}" target_context = f"{nickname}" else: attacker = f"{nickname}'s {action['attacker']}" target_context = "wild pet" # Build condensed message with all info on one line if action["damage"] > 0: effectiveness_msgs = { "super_effective": "⚡ Super effective!", "not_very_effective": "💫 Not very effective...", "no_effect": "❌ No effect!", "super_effective_critical": "💥 CRIT! Super effective!", "normal_critical": "💥 Critical hit!", "not_very_effective_critical": "💥 Crit, not very effective..." } effectiveness = effectiveness_msgs.get(action["effectiveness"], "") battle_msg = f"⚔️ {attacker} used {action['move']} → {action['damage']} damage to {target_context}! {effectiveness} (HP: {action['target_hp']})" else: # Status move or no damage battle_msg = f"⚔️ {attacker} used {action['move']} on {target_context}! (HP: {action['target_hp']})" self.send_message(channel, battle_msg) if result["battle_over"]: # Check if this is a gym battle gym_battle = await self.database.get_active_gym_battle(player["id"]) if gym_battle: print(f"DEBUG: Gym battle completion - player: {player['id']}, result: {result.get('winner')}") await self.handle_gym_battle_completion(channel, nickname, player, result, gym_battle) else: # Regular wild battle if result["winner"] == "player": self.send_message(channel, f"🎉 {nickname}: You won the battle!") # Remove encounter since battle is over if player["id"] in self.bot.active_encounters: del self.bot.active_encounters[player["id"]] else: self.send_message(channel, f"💀 {nickname}: Your pet fainted! You lost the battle...") # Remove encounter if player["id"] in self.bot.active_encounters: del self.bot.active_encounters[player["id"]] else: # Battle continues - show available moves with type-based colors moves_colored = " | ".join([ f"{self.get_move_color(move['type'])}{move['name']}\x0F" for move in result["available_moves"] ]) self.send_message(channel, f"🎯 {nickname}'s turn! Moves: {moves_colored}") async def cmd_flee(self, channel, nickname): """Attempt to flee from battle""" player = await self.require_player(channel, nickname) if not player: return # Check if this is a gym battle gym_battle = await self.database.get_active_gym_battle(player["id"]) if gym_battle: # Can't flee from gym battles self.send_message(channel, f"❌ {nickname}: You can't flee from a gym battle! Fight or forfeit with your honor intact!") return success = await self.game_engine.battle_engine.flee_battle(player["id"]) if success: self.send_message(channel, f"💨 {nickname} successfully fled from battle!") # Remove encounter if player["id"] in self.bot.active_encounters: del self.bot.active_encounters[player["id"]] else: self.send_message(channel, f"❌ {nickname} couldn't escape! Battle continues!") async def cmd_moves(self, channel, nickname): """Show active pet's available moves""" player = await self.require_player(channel, nickname) if not player: return # Get player's active pets pets = await self.database.get_player_pets(player["id"], active_only=True) if not pets: self.send_message(channel, f"{nickname}: You don't have any active pets! Use !team to check your pets.") return active_pet = pets[0] # Use first active pet available_moves = self.game_engine.battle_engine.get_available_moves(active_pet) if not available_moves: self.send_message(channel, f"{nickname}: Your {active_pet['species_name']} has no available moves!") return # Limit to 4 moves max and format compactly moves_to_show = available_moves[:4] move_info = [] for move in moves_to_show: power = move.get('power', 'Status') power_str = str(power) if power != 'Status' else 'Stat' move_colored = f"{self.get_move_color(move['type'])}{move['name']}\x0F" move_info.append(f"{move_colored} ({move['type']}, {power_str})") pet_name = active_pet["nickname"] or active_pet["species_name"] moves_line = " | ".join(move_info) self.send_message(channel, f"🎯 {nickname}'s {pet_name}: {moves_line}") async def handle_gym_battle_completion(self, channel, nickname, player, battle_result, gym_battle): """Handle completion of a gym battle turn""" try: if battle_result["winner"] == "player": # Player won this individual battle current_pet_index = gym_battle["current_pet_index"] gym_team = gym_battle["gym_team"] # Safety check for index bounds if current_pet_index >= len(gym_team): self.send_message(channel, f"❌ {nickname}: Gym battle state error - please !forfeit and try again") return defeated_pet = gym_team[current_pet_index] self.send_message(channel, f"🎉 {nickname}: You defeated {defeated_pet['species_name']}!") # Check if there are more pets if await self.database.advance_gym_battle(player["id"]): # More pets to fight next_index = current_pet_index + 1 next_pet = gym_team[next_index] self.send_message(channel, f"🥊 {gym_battle['leader_name']} sends out {next_pet['species_name']} (Lv.{next_pet['level']})!") # Start battle with next gym pet active_pets = await self.database.get_active_pets(player["id"]) player_pet = active_pets[0] # Use first active pet # Create gym pet data for battle engine next_gym_pet_data = { "species_name": next_pet["species_name"], "level": next_pet["level"], "type1": next_pet["type1"], "type2": next_pet["type2"], "stats": { "hp": next_pet["hp"], "attack": next_pet["attack"], "defense": next_pet["defense"], "speed": next_pet["speed"] } } # Start next battle battle = await self.game_engine.battle_engine.start_battle(player["id"], player_pet, next_gym_pet_data) self.send_message(channel, f"⚔️ Your {player_pet['species_name']} (HP: {battle['player_hp']}/{player_pet['max_hp']}) vs {next_pet['species_name']} (HP: {battle['wild_hp']}/{next_pet['hp']})") # Show available moves moves_colored = " | ".join([ f"{self.get_move_color(move['type'])}{move['name']}\x0F" for move in battle["available_moves"] ]) self.send_message(channel, f"🎯 Moves: {moves_colored} | Use !attack or !use ") else: # All gym pets defeated - gym victory! result = await self.database.end_gym_battle(player["id"], victory=True) self.send_message(channel, f"🏆 {nickname}: You defeated all of {gym_battle['leader_name']}'s pets!") self.send_message(channel, f"{gym_battle['badge_icon']} {gym_battle['leader_name']}: \"Impressive! You've earned the {gym_battle['gym_name']} badge!\"") self.send_message(channel, f"🎉 {nickname} earned the {gym_battle['gym_name']} badge {gym_battle['badge_icon']}!") # Award rewards based on difficulty money_reward = 500 + (result["difficulty_level"] * 100) self.send_message(channel, f"💰 Rewards: ${money_reward} | 🌟 Gym mastery increased!") else: # Player lost gym battle result = await self.database.end_gym_battle(player["id"], victory=False) self.send_message(channel, f"💀 {nickname}: Your pet fainted!") self.send_message(channel, f"{gym_battle['badge_icon']} {gym_battle['leader_name']}: \"Good battle! Train more and come back stronger!\"") self.send_message(channel, f"💡 {nickname}: Try leveling up your pets or bringing items to heal during battle!") except Exception as e: self.send_message(channel, f"❌ {nickname}: Gym battle error occurred - please !forfeit and try again") print(f"Gym battle completion error: {e}") import traceback traceback.print_exc()