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>
338 lines
No EOL
15 KiB
Python
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!") |