Petbot/modules/gym_battles.py
megaproxy 8e9ff2960f Implement case-insensitive command processing across all bot modules
Added normalize_input() function to BaseModule for consistent lowercase conversion of user input. Updated all command modules to use normalization for commands, arguments, pet names, location names, gym names, and item names. Players can now use any capitalization for commands and arguments.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-14 21:57:51 +01:00

338 lines
No EOL
15 KiB
Python

#!/usr/bin/env python3
"""Gym battle module for PetBot"""
from .base_module import BaseModule
class GymBattles(BaseModule):
"""Handles gym challenges, battles, and badge tracking"""
def get_commands(self):
return ["gym", "forfeit"]
async def handle_command(self, channel, nickname, command, args):
if command == "gym":
if not args:
await self.cmd_gym_list(channel, nickname)
elif self.normalize_input(args[0]) == "list":
await self.cmd_gym_list_all(channel, nickname)
elif self.normalize_input(args[0]) == "challenge":
await self.cmd_gym_challenge(channel, nickname, args[1:])
elif self.normalize_input(args[0]) == "info":
await self.cmd_gym_info(channel, nickname, args[1:])
elif self.normalize_input(args[0]) == "status":
await self.cmd_gym_status(channel, nickname)
else:
await self.cmd_gym_list(channel, nickname)
elif command == "forfeit":
await self.cmd_forfeit(channel, nickname)
async def cmd_gym_list(self, channel, nickname):
"""List gyms in current location"""
player = await self.require_player(channel, nickname)
if not player:
return
# Get player's current location
location = await self.database.get_player_location(player["id"])
if not location:
self.send_message(channel, f"{nickname}: You are not in a valid location!")
return
# Get gyms in current location
gyms = await self.database.get_gyms_in_location(location["id"], player["id"])
if not gyms:
self.send_message(channel, f"🏛️ {nickname}: No gyms found in {location['name']}.")
return
self.send_message(channel, f"🏛️ Gyms in {location['name']}:")
for gym in gyms:
victories = gym.get("victories", 0)
if victories == 0:
status = "Not challenged"
difficulty = "Beginner"
else:
status = f"{victories} victories"
difficulty = f"Level {victories + 1}"
badge_icon = gym["badge_icon"]
leader = gym["leader_name"]
theme = gym["theme"]
self.send_message(channel,
f" {badge_icon} {gym['name']} - Leader: {leader} ({theme})")
self.send_message(channel,
f" Status: {status} | Next difficulty: {difficulty}")
self.send_message(channel,
f"💡 Use '!gym challenge \"<gym name>\"' to battle!")
async def cmd_gym_list_all(self, channel, nickname):
"""List all gyms across all locations"""
player = await self.require_player(channel, nickname)
if not player:
return
# Get all locations and their gyms
all_locations = await self.database.get_all_locations()
self.send_message(channel, f"🗺️ {nickname}: All Gym Locations:")
total_badges = 0
for location in all_locations:
gyms = await self.database.get_gyms_in_location(location["id"], player["id"])
if gyms:
for gym in gyms:
victories = gym.get("victories", 0)
status_icon = "" if victories > 0 else ""
if victories > 0:
total_badges += 1
self.send_message(channel,
f" {status_icon} {location['name']}: {gym['name']} ({gym['theme']}) - {victories} victories")
self.send_message(channel, f"🏆 Total badges earned: {total_badges}")
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
# Get player's current location first
location = await self.database.get_player_location(player["id"])
if not location:
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('"')
# 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_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
# Check if player has active pets
active_pets = await self.database.get_active_pets(player["id"])
if not active_pets:
self.send_message(channel, f"{nickname}: You need at least one active pet to challenge a gym! Use !activate <pet> first.")
return
# Get player's gym progress
progress = await self.database.get_player_gym_progress(player["id"], gym["id"])
difficulty_level = (progress["victories"] if progress else 0) + 1
difficulty_multiplier = 1.0 + (difficulty_level - 1) * 0.2 # 20% increase per victory
# Start gym battle
await self.start_gym_battle(channel, nickname, player, gym, difficulty_level, difficulty_multiplier)
async def start_gym_battle(self, channel, nickname, player, gym, difficulty_level, difficulty_multiplier):
"""Start a gym battle"""
# Check if player is already in any battle
regular_battle = await self.game_engine.battle_engine.get_active_battle(player["id"])
gym_battle = await self.database.get_active_gym_battle(player["id"])
if regular_battle or gym_battle:
self.send_message(channel, f"{nickname}: You're already in a battle! Finish your current battle first.")
return
# Get gym team with scaling
gym_team = await self.database.get_gym_team(gym["id"], difficulty_multiplier)
if not gym_team:
self.send_message(channel, f"{nickname}: {gym['name']} gym has no team configured!")
return
# Display battle start
badge_icon = gym["badge_icon"]
leader = gym["leader_name"]
difficulty_name = f"Level {difficulty_level}" if difficulty_level > 1 else "Beginner"
self.send_message(channel,
f"🏛️ {nickname} challenges the {gym['name']} gym!")
self.send_message(channel,
f"{badge_icon} Leader {leader}: \"Welcome to my {gym['theme']}-type gym! Let's see what you've got!\"")
self.send_message(channel,
f"⚔️ Difficulty: {difficulty_name} ({len(gym_team)} pets)")
# Start gym battle state
battle_id = await self.database.start_gym_battle(player["id"], gym["id"], difficulty_level, gym_team)
# Start battle with first gym pet
first_gym_pet = gym_team[0]
active_pets = await self.database.get_active_pets(player["id"])
player_pet = active_pets[0] # Use first active pet
# Create gym pet in wild pet format for battle engine
gym_pet_data = {
"species_name": first_gym_pet["species_name"],
"level": first_gym_pet["level"],
"type1": first_gym_pet["type1"],
"type2": first_gym_pet["type2"],
"stats": {
"hp": first_gym_pet["hp"],
"attack": first_gym_pet["attack"],
"defense": first_gym_pet["defense"],
"speed": first_gym_pet["speed"]
}
}
# Start the battle using existing battle engine
battle = await self.game_engine.battle_engine.start_battle(player["id"], player_pet, gym_pet_data)
# Display first battle start
self.send_message(channel,
f"🥊 {leader} sends out {first_gym_pet['species_name']} (Lv.{first_gym_pet['level']})!")
self.send_message(channel,
f"⚔️ Your {player_pet['species_name']} (Lv.{player_pet['level']}, {battle['player_hp']}/{player_pet['max_hp']} HP) vs {first_gym_pet['species_name']} (Lv.{first_gym_pet['level']}, {battle['wild_hp']}/{first_gym_pet['hp']} HP)")
# Show available moves
from .battle_system import BattleSystem
battle_system = BattleSystem(self.bot, self.database, self.game_engine)
moves_colored = " | ".join([
f"{battle_system.get_move_color(move['type'])}{move['name']}\x0F"
for move in battle["available_moves"]
])
self.send_message(channel, f"🎯 Moves: {moves_colored} | Use !attack <move> or !use <item>")
async def handle_gym_victory(self, channel, nickname, player, gym, difficulty_level):
"""Handle gym battle victory"""
# Record victory in database
result = await self.database.record_gym_victory(player["id"], gym["id"])
badge_icon = gym["badge_icon"]
leader = gym["leader_name"]
self.send_message(channel, f"🎉 {nickname} defeats the {gym['name']} gym!")
if result["is_first_victory"]:
# First time victory - award badge
self.send_message(channel,
f"{badge_icon} Leader {leader}: \"Impressive! You've earned the {gym['badge_name']}!\"")
self.send_message(channel,
f"🏆 {nickname} earned the {gym['badge_name']} {badge_icon}!")
# Award bonus rewards for first victory
money_reward = 500 + (difficulty_level * 100)
exp_reward = 200 + (difficulty_level * 50)
else:
# Repeat victory
self.send_message(channel,
f"{badge_icon} Leader {leader}: \"Well fought! Your skills keep improving!\"")
money_reward = 200 + (difficulty_level * 50)
exp_reward = 100 + (difficulty_level * 25)
# Award rewards (we'll implement this when we have currency/exp systems)
victories = result["victories"]
next_difficulty = result["next_difficulty"]
self.send_message(channel,
f"💰 Rewards: ${money_reward} | 🌟 {exp_reward} EXP")
self.send_message(channel,
f"📊 {gym['name']} record: {victories} victories | Next challenge: Level {next_difficulty}")
async def handle_gym_defeat(self, channel, nickname, gym, difficulty_level):
"""Handle gym battle defeat"""
badge_icon = gym["badge_icon"]
leader = gym["leader_name"]
self.send_message(channel, f"💥 {nickname} was defeated by the {gym['name']} gym!")
self.send_message(channel,
f"{badge_icon} Leader {leader}: \"Good battle! Train more and come back stronger!\"")
self.send_message(channel,
f"💡 Tip: Level up your pets, get better items, or try a different strategy!")
async def cmd_gym_info(self, channel, nickname, args):
"""Get detailed information about a gym"""
if not args:
self.send_message(channel, f"{nickname}: Specify a gym name! Example: !gym info \"Forest Guardian\"")
return
player = await self.require_player(channel, nickname)
if not player:
return
gym_name = " ".join(self.normalize_input(args)).strip('"')
# First try to find gym in player's current location
location = await self.database.get_player_location(player["id"])
gym = None
if location:
gym = await self.database.get_gym_by_name_in_location(gym_name, location["id"])
# If not found in current location, search globally
if not gym:
gym = await self.database.get_gym_by_name(gym_name)
if not gym:
self.send_message(channel, f"{nickname}: Gym '{gym_name}' not found!")
return
# Get gym team info
gym_team = await self.database.get_gym_team(gym["id"])
badge_icon = gym["badge_icon"]
self.send_message(channel, f"🏛️ {gym['name']} Gym Information:")
self.send_message(channel, f"📍 Location: {gym['location_name']}")
self.send_message(channel, f"👤 Leader: {gym['leader_name']}")
self.send_message(channel, f"🎯 Theme: {gym['theme']}-type")
self.send_message(channel, f"📝 {gym['description']}")
self.send_message(channel, f"🏆 Badge: {gym['badge_name']} {badge_icon}")
if gym_team:
self.send_message(channel, f"⚔️ Team ({len(gym_team)} pets):")
for pet in gym_team:
type_str = pet["type1"]
if pet["type2"]:
type_str += f"/{pet['type2']}"
self.send_message(channel,
f" • Level {pet['level']} {pet['species_name']} ({type_str})")
async def cmd_gym_status(self, channel, nickname):
"""Show player's overall gym progress"""
player = await self.require_player(channel, nickname)
if not player:
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}")
async def cmd_forfeit(self, channel, nickname):
"""Forfeit the current gym battle"""
player = await self.require_player(channel, nickname)
if not player:
return
# Check if player is in a gym battle
gym_battle = await self.database.get_active_gym_battle(player["id"])
if not gym_battle:
self.send_message(channel, f"{nickname}: You're not currently in a gym battle!")
return
# End any active regular battle first
await self.game_engine.battle_engine.end_battle(player["id"], "forfeit")
# End gym battle
result = await self.database.end_gym_battle(player["id"], victory=False)
self.send_message(channel, f"🏳️ {nickname} forfeited the gym battle!")
self.send_message(channel,
f"{gym_battle['badge_icon']} {gym_battle['leader_name']}: \"Sometimes retreat is the wisest strategy. Come back when you're ready!\"")
self.send_message(channel,
f"💡 {nickname}: Train your pets and try again. You can challenge the gym anytime!")