Petbot/modules/pet_management.py
megaproxy bd455f1be5 Implement comprehensive experience and leveling system
**NEW FEATURES:**
- Complete EXP system with Pokemon-style stat calculation
- Level-based stat growth and automatic HP restoration on level up
- Experience gain from catches and battle victories
- Visual level up notifications with stat increases

**EXPERIENCE SOURCES:**
- Successful catches: 5 EXP × caught pet level
- Wild battle victories: 10 EXP × defeated pet level
- Gym battle victories: 20 EXP × defeated pet level (2x multiplier)

**LEVELING MECHANICS:**
- Cubic growth formula: level³ × 4 - 12 (smooth progression)
- Level cap at 100
- Stats recalculated on level up using Pokemon formulas
- Full HP restoration on level up
- Real-time stat increase display

**EXPERIENCE DISPLAY:**
- \!team command now shows "EXP: X to next" for active pets
- Level up messages show exact stat gains
- Experience awarded immediately after catch/victory

**STAT CALCULATION:**
- HP: (2 × base + 31) × level / 100 + level + 10
- Other stats: (2 × base + 31) × level / 100 + 5
- Progressive growth ensures meaningful advancement

**BATTLE INTEGRATION:**
- EXP awarded after wild battles, gym individual battles, and catches
- Different multipliers for different victory types
- First active pet receives all experience

This creates proper RPG progression where pets grow stronger through gameplay,
encouraging both exploration (catches) and combat (battles) for advancement.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-14 16:11:20 +01:00

161 lines
No EOL
6.8 KiB
Python

#!/usr/bin/env python3
"""Pet management commands module for PetBot"""
from .base_module import BaseModule
class PetManagement(BaseModule):
"""Handles team, pets, and future pet management commands"""
def get_commands(self):
return ["team", "pets", "activate", "deactivate", "swap"]
async def handle_command(self, channel, nickname, command, args):
if command == "team":
await self.cmd_team(channel, nickname)
elif command == "pets":
await self.cmd_pets(channel, nickname)
elif command == "activate":
await self.cmd_activate(channel, nickname, args)
elif command == "deactivate":
await self.cmd_deactivate(channel, nickname, args)
elif command == "swap":
await self.cmd_swap(channel, nickname, args)
async def cmd_team(self, channel, nickname):
"""Show active pets (channel display)"""
player = await self.require_player(channel, nickname)
if not player:
return
pets = await self.database.get_player_pets(player["id"], active_only=False)
if not pets:
self.send_message(channel, f"{nickname}: You don't have any pets! Use !catch to find some.")
return
# Show active pets first, then others
active_pets = [pet for pet in pets if pet.get("is_active")]
inactive_pets = [pet for pet in pets if not pet.get("is_active")]
team_info = []
# Active pets with star
for pet in active_pets:
name = pet["nickname"] or pet["species_name"]
# Calculate EXP progress
current_exp = pet.get("experience", 0)
next_level_exp = self.database.calculate_exp_for_level(pet["level"] + 1)
current_level_exp = self.database.calculate_exp_for_level(pet["level"])
exp_needed = next_level_exp - current_exp
if pet["level"] >= 100:
exp_display = "MAX"
else:
exp_display = f"{exp_needed} to next"
team_info.append(f"{name} (Lv.{pet['level']}) - {pet['hp']}/{pet['max_hp']} HP | EXP: {exp_display}")
# Inactive pets
for pet in inactive_pets[:5]: # Show max 5 inactive
name = pet["nickname"] or pet["species_name"]
team_info.append(f"{name} (Lv.{pet['level']}) - {pet['hp']}/{pet['max_hp']} HP")
if len(inactive_pets) > 5:
team_info.append(f"... and {len(inactive_pets) - 5} more in storage")
self.send_message(channel, f"🐾 {nickname}'s team: " + " | ".join(team_info))
async def cmd_pets(self, channel, nickname):
"""Show link to pet collection web page"""
player = await self.require_player(channel, nickname)
if not player:
return
# Send URL to player's profile page instead of PM spam
self.send_message(channel, f"{nickname}: View your complete pet collection at: http://petz.rdx4.com/player/{nickname}")
async def cmd_activate(self, channel, nickname, args):
"""Activate a pet for battle (PM only)"""
# Redirect to PM for privacy
if not args:
self.send_pm(nickname, "Usage: !activate <pet_name>")
self.send_message(channel, f"{nickname}: Pet activation instructions sent via PM!")
return
player = await self.require_player(channel, nickname)
if not player:
return
pet_name = " ".join(args)
result = await self.database.activate_pet(player["id"], pet_name)
if result["success"]:
pet = result["pet"]
display_name = pet["nickname"] or pet["species_name"]
self.send_pm(nickname, f"{display_name} is now active for battle!")
self.send_message(channel, f"{nickname}: Pet activated successfully!")
else:
self.send_pm(nickname, f"{result['error']}")
self.send_message(channel, f"{nickname}: Pet activation failed - check PM for details!")
async def cmd_deactivate(self, channel, nickname, args):
"""Deactivate a pet to storage (PM only)"""
# Redirect to PM for privacy
if not args:
self.send_pm(nickname, "Usage: !deactivate <pet_name>")
self.send_message(channel, f"{nickname}: Pet deactivation instructions sent via PM!")
return
player = await self.require_player(channel, nickname)
if not player:
return
pet_name = " ".join(args)
result = await self.database.deactivate_pet(player["id"], pet_name)
if result["success"]:
pet = result["pet"]
display_name = pet["nickname"] or pet["species_name"]
self.send_pm(nickname, f"📦 {display_name} moved to storage!")
self.send_message(channel, f"{nickname}: Pet deactivated successfully!")
else:
self.send_pm(nickname, f"{result['error']}")
self.send_message(channel, f"{nickname}: Pet deactivation failed - check PM for details!")
async def cmd_swap(self, channel, nickname, args):
"""Swap active/storage status of two pets (PM only)"""
# Redirect to PM for privacy
if len(args) < 2:
self.send_pm(nickname, "Usage: !swap <pet1> <pet2>")
self.send_pm(nickname, "Example: !swap Flamey Aqua")
self.send_message(channel, f"{nickname}: Pet swap instructions sent via PM!")
return
player = await self.require_player(channel, nickname)
if not player:
return
# Handle multi-word pet names by splitting on first space vs last space
if len(args) == 2:
pet1_name, pet2_name = args
else:
# For more complex parsing, assume equal split
mid_point = len(args) // 2
pet1_name = " ".join(args[:mid_point])
pet2_name = " ".join(args[mid_point:])
result = await self.database.swap_pets(player["id"], pet1_name, pet2_name)
if result["success"]:
pet1 = result["pet1"]
pet2 = result["pet2"]
pet1_display = pet1["nickname"] or pet1["species_name"]
pet2_display = pet2["nickname"] or pet2["species_name"]
self.send_pm(nickname, f"🔄 Swap complete!")
self.send_pm(nickname, f"{pet1_display}{result['pet1_now']}")
self.send_pm(nickname, f"{pet2_display}{result['pet2_now']}")
self.send_message(channel, f"{nickname}: Pet swap completed!")
else:
self.send_pm(nickname, f"{result['error']}")
self.send_message(channel, f"{nickname}: Pet swap failed - check PM for details!")