Major Features Added: - Complete petdex page showing all available pets with stats, types, evolution info - Encounter tracking system recording pet discoveries and catch statistics - Gym badges display on player profiles with victory counts and dates - Enhanced player profiles with discovery progress and completion percentages Technical Implementation: - New /petdex route with rarity-organized pet encyclopedia - Database encounter tracking with automatic integration into exploration/catch - Updated webserver.py with encounter data fetching and display - Fixed battle_system.py syntax error in gym battle completion logic - Organized project by moving unused bot files to backup_bots/ folder Database Changes: - Added player_encounters table for tracking discoveries - Added methods: record_encounter, get_player_encounters, get_encounter_stats - Enhanced player profile queries to include gym badges and encounters Web Interface Updates: - Petdex page with search stats, rarity grouping, and spawn location info - Player profiles now show species seen, completion %, gym badges earned - Encounter section displaying discovered pets with catch statistics - Updated navigation to include petdex link on main game hub 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
633 lines
No EOL
29 KiB
Python
633 lines
No EOL
29 KiB
Python
#!/usr/bin/env python3
|
|
import socket
|
|
import threading
|
|
import time
|
|
import json
|
|
import sys
|
|
import os
|
|
import asyncio
|
|
|
|
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from src.database import Database
|
|
from src.game_engine import GameEngine
|
|
|
|
class PetBot:
|
|
def __init__(self):
|
|
self.database = Database()
|
|
self.game_engine = GameEngine(self.database)
|
|
self.config = {
|
|
"server": "irc.libera.chat",
|
|
"port": 6667,
|
|
"nickname": "PetBot",
|
|
"channel": "#petz",
|
|
"command_prefix": "!"
|
|
}
|
|
self.socket = None
|
|
self.connected = False
|
|
self.running = True
|
|
self.active_encounters = {} # player_id -> encounter_data
|
|
|
|
def initialize_async_components(self):
|
|
"""Initialize async components"""
|
|
loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(loop)
|
|
|
|
print("Initializing database...")
|
|
loop.run_until_complete(self.database.init_database())
|
|
print("Loading game data...")
|
|
loop.run_until_complete(self.game_engine.load_game_data())
|
|
print("✓ Bot initialized successfully!")
|
|
|
|
self.loop = loop
|
|
|
|
def test_connection(self):
|
|
"""Test if we can connect to IRC"""
|
|
print("Testing IRC connection...")
|
|
try:
|
|
test_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
test_sock.settimeout(5)
|
|
test_sock.connect((self.config["server"], self.config["port"]))
|
|
test_sock.close()
|
|
print("✓ IRC connection test successful")
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ IRC connection failed: {e}")
|
|
return False
|
|
|
|
def connect(self):
|
|
self.initialize_async_components()
|
|
|
|
if not self.test_connection():
|
|
print("\n=== IRC Connection Failed ===")
|
|
print("The bot's core functionality is working perfectly!")
|
|
print("Run these commands to test locally:")
|
|
print(" python3 test_commands.py - Test all functionality")
|
|
print(" python3 test_db.py - Test database operations")
|
|
print("\nTo connect to IRC:")
|
|
print("1. Ensure network access to irc.libera.chat:6667")
|
|
print("2. Or modify config in run_bot.py to use a different server")
|
|
return
|
|
|
|
print(f"Connecting to {self.config['server']}:{self.config['port']}")
|
|
|
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.socket.settimeout(10)
|
|
|
|
try:
|
|
self.socket.connect((self.config["server"], self.config["port"]))
|
|
print("✓ Connected to IRC server!")
|
|
except Exception as e:
|
|
print(f"✗ Failed to connect: {e}")
|
|
return
|
|
|
|
# Send IRC handshake
|
|
self.send(f"NICK {self.config['nickname']}")
|
|
self.send(f"USER {self.config['nickname']} 0 * :{self.config['nickname']}")
|
|
print("✓ Sent IRC handshake")
|
|
|
|
# Start message processing
|
|
self.socket.settimeout(1)
|
|
self.main_loop()
|
|
|
|
def main_loop(self):
|
|
print("✓ Bot is now running! Use Ctrl+C to stop.")
|
|
print(f"✓ Bot will join {self.config['channel']} when connected")
|
|
print("✓ Available commands: !help !start !catch !team !wild !stats")
|
|
|
|
while self.running:
|
|
try:
|
|
data = self.socket.recv(4096).decode('utf-8', errors='ignore')
|
|
if not data:
|
|
print("Connection closed by server")
|
|
break
|
|
|
|
lines = data.strip().split('\n')
|
|
for line in lines:
|
|
if line.strip():
|
|
self.handle_line(line.strip())
|
|
|
|
except socket.timeout:
|
|
continue
|
|
except KeyboardInterrupt:
|
|
print("\n✓ Shutting down bot...")
|
|
self.running = False
|
|
break
|
|
except Exception as e:
|
|
print(f"Error in main loop: {e}")
|
|
time.sleep(1)
|
|
|
|
if self.socket:
|
|
self.socket.close()
|
|
self.loop.close()
|
|
|
|
def send(self, message):
|
|
if self.socket:
|
|
full_message = f"{message}\r\n"
|
|
print(f">> {message}")
|
|
self.socket.send(full_message.encode('utf-8'))
|
|
|
|
def handle_line(self, line):
|
|
print(f"<< {line}")
|
|
|
|
if line.startswith("PING"):
|
|
pong_response = line.replace("PING", "PONG")
|
|
self.send(pong_response)
|
|
return
|
|
|
|
# Handle connection messages
|
|
if "376" in line or "422" in line: # End of MOTD
|
|
if not self.connected:
|
|
self.send(f"JOIN {self.config['channel']}")
|
|
self.connected = True
|
|
print(f"✓ Joined {self.config['channel']} - Bot is ready!")
|
|
return
|
|
|
|
parts = line.split()
|
|
if len(parts) < 4:
|
|
return
|
|
|
|
if parts[1] == "PRIVMSG":
|
|
channel = parts[2]
|
|
message = " ".join(parts[3:])[1:]
|
|
|
|
hostmask = parts[0][1:]
|
|
nickname = hostmask.split('!')[0]
|
|
|
|
if message.startswith(self.config["command_prefix"]):
|
|
print(f"Processing command from {nickname}: {message}")
|
|
self.handle_command(channel, nickname, message)
|
|
|
|
def handle_command(self, channel, nickname, message):
|
|
command_parts = message[1:].split()
|
|
if not command_parts:
|
|
return
|
|
|
|
command = command_parts[0].lower()
|
|
args = command_parts[1:]
|
|
|
|
try:
|
|
if command == "help":
|
|
self.cmd_help(channel, nickname)
|
|
elif command == "start":
|
|
self.cmd_start(channel, nickname)
|
|
elif command == "explore":
|
|
self.cmd_explore(channel, nickname)
|
|
elif command == "catch" or command == "capture":
|
|
self.cmd_catch(channel, nickname)
|
|
elif command == "battle":
|
|
self.cmd_battle(channel, nickname)
|
|
elif command == "attack":
|
|
self.cmd_attack(channel, nickname, args)
|
|
elif command == "flee":
|
|
self.cmd_flee(channel, nickname)
|
|
elif command == "moves":
|
|
self.cmd_moves(channel, nickname)
|
|
elif command == "travel":
|
|
self.cmd_travel(channel, nickname, args)
|
|
elif command == "location" or command == "where":
|
|
self.cmd_location(channel, nickname)
|
|
elif command == "achievements":
|
|
self.cmd_achievements(channel, nickname)
|
|
elif command == "weather":
|
|
self.cmd_weather(channel, nickname)
|
|
elif command == "team":
|
|
self.cmd_team(channel, nickname)
|
|
elif command == "wild":
|
|
self.cmd_wild(channel, nickname, args)
|
|
elif command == "stats":
|
|
self.cmd_stats(channel, nickname, args)
|
|
else:
|
|
self.send_message(channel, f"{nickname}: Unknown command. Use !help for available commands.")
|
|
except Exception as e:
|
|
self.send_message(channel, f"{nickname}: Error processing command: {str(e)}")
|
|
print(f"Command error: {e}")
|
|
|
|
def send_message(self, target, message):
|
|
self.send(f"PRIVMSG {target} :{message}")
|
|
time.sleep(0.5)
|
|
|
|
def run_async_command(self, coro):
|
|
return self.loop.run_until_complete(coro)
|
|
|
|
def cmd_help(self, channel, nickname):
|
|
help_text = [
|
|
"🐾 Pet Bot Commands:",
|
|
"!start - Begin your pet journey in Starter Town",
|
|
"!explore - Explore your current location for wild pets",
|
|
"!catch (or !capture) - Try to catch a pet during exploration or battle",
|
|
"!battle - Start a battle with an encountered wild pet",
|
|
"!attack <move> - Use a move in battle",
|
|
"!flee - Try to escape from battle",
|
|
"!moves - View your active pet's available moves",
|
|
"!travel <location> - Travel to a different location",
|
|
"!location - See where you currently are",
|
|
"!achievements - View your achievements and progress",
|
|
"!weather - Check current weather effects",
|
|
"!team - View your active pets",
|
|
"!wild <location> - See what pets spawn in a location",
|
|
"!stats - View your player stats",
|
|
"Locations: Starter Town, Whispering Woods, Electric Canyon, Crystal Caves, Frozen Tundra, Dragon's Peak"
|
|
]
|
|
for line in help_text:
|
|
self.send_message(channel, line)
|
|
|
|
def cmd_start(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if player:
|
|
self.send_message(channel, f"{nickname}: You already have an account! Use !team to see your pets.")
|
|
return
|
|
|
|
player_id = self.run_async_command(self.database.create_player(nickname))
|
|
starter_pet = self.run_async_command(self.game_engine.give_starter_pet(player_id))
|
|
|
|
self.send_message(channel,
|
|
f"🎉 {nickname}: Welcome to the world of pets! You received a Level {starter_pet['level']} {starter_pet['species_name']}! You are now in Starter Town.")
|
|
|
|
def cmd_explore(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
encounter = self.run_async_command(self.game_engine.explore_location(player["id"]))
|
|
|
|
if encounter["type"] == "error":
|
|
self.send_message(channel, f"{nickname}: {encounter['message']}")
|
|
elif encounter["type"] == "empty":
|
|
self.send_message(channel, f"🔍 {nickname}: {encounter['message']}")
|
|
elif encounter["type"] == "encounter":
|
|
# Store the encounter for potential catching
|
|
self.active_encounters[player["id"]] = encounter["pet"]
|
|
|
|
pet = encounter["pet"]
|
|
type_str = pet["type1"]
|
|
if pet["type2"]:
|
|
type_str += f"/{pet['type2']}"
|
|
|
|
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!")
|
|
|
|
def cmd_battle(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
# Check if player has an active encounter
|
|
if player["id"] not in self.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 = self.run_async_command(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 <move> or !flee.")
|
|
return
|
|
|
|
# Get player's active pet
|
|
pets = self.run_async_command(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.active_encounters[player["id"]]
|
|
|
|
# Start battle
|
|
battle = self.run_async_command(self.game_engine.battle_engine.start_battle(player["id"], player_pet, wild_pet))
|
|
|
|
self.send_message(channel, f"⚔️ {nickname}: Battle started!")
|
|
self.send_message(channel, f"Your {player_pet['species_name']} (Lv.{player_pet['level']}, {battle['player_hp']}/{player_pet['max_hp']} HP) vs Wild {wild_pet['species_name']} (Lv.{wild_pet['level']}, {battle['wild_hp']}/{wild_pet['max_hp']} HP)")
|
|
|
|
move_list = [move["name"] for move in battle["available_moves"]]
|
|
self.send_message(channel, f"Available moves: {', '.join(move_list)}")
|
|
self.send_message(channel, f"Use !attack <move_name> to attack!")
|
|
|
|
def cmd_attack(self, channel, nickname, args):
|
|
if not args:
|
|
self.send_message(channel, f"{nickname}: Specify a move to use! Example: !attack Tackle")
|
|
return
|
|
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
move_name = " ".join(args).title() # Normalize to Title Case
|
|
result = self.run_async_command(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
|
|
for action in result["results"]:
|
|
damage_msg = f"{action['attacker']} used {action['move']}!"
|
|
self.send_message(channel, damage_msg)
|
|
|
|
if action["damage"] > 0:
|
|
effectiveness_msgs = {
|
|
"super_effective": "It's super effective!",
|
|
"not_very_effective": "It's not very effective...",
|
|
"no_effect": "It had no effect!",
|
|
"super_effective_critical": "Critical hit! It's super effective!",
|
|
"normal_critical": "Critical hit!",
|
|
"not_very_effective_critical": "Critical hit! It's not very effective..."
|
|
}
|
|
|
|
damage_text = f"Dealt {action['damage']} damage!"
|
|
if action["effectiveness"] in effectiveness_msgs:
|
|
damage_text += f" {effectiveness_msgs[action['effectiveness']]}"
|
|
|
|
self.send_message(channel, damage_text)
|
|
self.send_message(channel, f"Target HP: {action['target_hp']}")
|
|
|
|
if result["battle_over"]:
|
|
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.active_encounters:
|
|
del self.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.active_encounters:
|
|
del self.active_encounters[player["id"]]
|
|
else:
|
|
# Battle continues
|
|
move_list = [move["name"] for move in result["available_moves"]]
|
|
self.send_message(channel, f"Your turn! Available moves: {', '.join(move_list)}")
|
|
|
|
def cmd_flee(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
success = self.run_async_command(self.game_engine.battle_engine.flee_battle(player["id"]))
|
|
|
|
if success:
|
|
self.send_message(channel, f"💨 {nickname}: You successfully escaped from battle!")
|
|
# Remove encounter
|
|
if player["id"] in self.active_encounters:
|
|
del self.active_encounters[player["id"]]
|
|
else:
|
|
self.send_message(channel, f"❌ {nickname}: Couldn't escape! The battle continues!")
|
|
|
|
def cmd_moves(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
# Get player's active pets
|
|
pets = self.run_async_command(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
|
|
moves_to_show = available_moves[:4]
|
|
|
|
move_info = []
|
|
for move in moves_to_show:
|
|
power_info = f"Power: {move.get('power', 'N/A')}" if move.get('power') else "Status move"
|
|
move_info.append(f"{move['name']} ({move['type']}) - {power_info}")
|
|
|
|
pet_name = active_pet["nickname"] or active_pet["species_name"]
|
|
self.send_message(channel, f"🎯 {pet_name}'s moves:")
|
|
for move_line in move_info:
|
|
self.send_message(channel, f" • {move_line}")
|
|
|
|
def cmd_catch(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
# Check if player is in an active battle
|
|
active_battle = self.run_async_command(self.game_engine.battle_engine.get_active_battle(player["id"]))
|
|
if active_battle:
|
|
# Catching during battle
|
|
wild_pet = active_battle["wild_pet"]
|
|
current_hp = active_battle["wild_hp"]
|
|
max_hp = wild_pet["max_hp"]
|
|
|
|
# Calculate catch rate based on remaining HP (lower HP = higher catch rate)
|
|
hp_percentage = current_hp / max_hp
|
|
base_catch_rate = 0.3 # Lower base rate than direct catch
|
|
hp_bonus = (1.0 - hp_percentage) * 0.5 # Up to 50% bonus for low HP
|
|
final_catch_rate = min(0.9, base_catch_rate + hp_bonus) # Cap at 90%
|
|
|
|
import random
|
|
if random.random() < final_catch_rate:
|
|
# Successful catch during battle
|
|
result = self.run_async_command(self.game_engine.attempt_catch_current_location(player["id"], wild_pet))
|
|
|
|
# End the battle
|
|
await_result = self.run_async_command(self.game_engine.battle_engine.end_battle(player["id"], "caught"))
|
|
|
|
# Check for achievements
|
|
achievements = self.run_async_command(self.game_engine.check_and_award_achievements(player["id"], "catch_type", ""))
|
|
if achievements:
|
|
for achievement in achievements:
|
|
self.send_message(channel, f"🏆 {nickname}: Achievement unlocked: {achievement['name']}! {achievement['description']}")
|
|
|
|
# Remove encounter
|
|
if player["id"] in self.active_encounters:
|
|
del self.active_encounters[player["id"]]
|
|
|
|
self.send_message(channel, f"🎯 {nickname}: Caught the {wild_pet['species_name']} during battle! (HP: {current_hp}/{max_hp})")
|
|
else:
|
|
# Failed catch - battle continues
|
|
catch_percentage = int(final_catch_rate * 100)
|
|
self.send_message(channel, f"🎯 {nickname}: The {wild_pet['species_name']} broke free! ({catch_percentage}% catch rate) Battle continues!")
|
|
return
|
|
|
|
# Regular exploration catch (not in battle)
|
|
if player["id"] not in self.active_encounters:
|
|
self.send_message(channel, f"{nickname}: You need to !explore first to find a pet, or be in battle to catch!")
|
|
return
|
|
|
|
target_pet = self.active_encounters[player["id"]]
|
|
result = self.run_async_command(self.game_engine.attempt_catch_current_location(player["id"], target_pet))
|
|
|
|
# Check for achievements after successful catch
|
|
if "Success!" in result:
|
|
achievements = self.run_async_command(self.game_engine.check_and_award_achievements(player["id"], "catch_type", ""))
|
|
if achievements:
|
|
for achievement in achievements:
|
|
self.send_message(channel, f"🏆 {nickname}: Achievement unlocked: {achievement['name']}! {achievement['description']}")
|
|
|
|
# Remove the encounter regardless of success
|
|
del self.active_encounters[player["id"]]
|
|
|
|
self.send_message(channel, f"🎯 {nickname}: {result}")
|
|
|
|
def cmd_travel(self, channel, nickname, args):
|
|
if not args:
|
|
self.send_message(channel, f"{nickname}: Specify where to travel! Available: Starter Town, Whispering Woods, Electric Canyon, Crystal Caves, Frozen Tundra, Dragon's Peak")
|
|
return
|
|
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
destination = " ".join(args).title() # Normalize to Title Case
|
|
location = self.run_async_command(self.database.get_location_by_name(destination))
|
|
|
|
if not location:
|
|
self.send_message(channel, f"{nickname}: '{destination}' is not a valid location!")
|
|
return
|
|
|
|
# Check if player can access this location
|
|
can_access = self.run_async_command(self.database.can_access_location(player["id"], location["id"]))
|
|
if not can_access:
|
|
self.send_message(channel, f"{nickname}: You cannot access {destination} yet! Complete achievements to unlock new areas. Use !achievements to see progress.")
|
|
return
|
|
|
|
# Clear any active encounters when traveling
|
|
if player["id"] in self.active_encounters:
|
|
del self.active_encounters[player["id"]]
|
|
|
|
self.run_async_command(self.database.update_player_location(player["id"], location["id"]))
|
|
|
|
# Show weather info
|
|
weather = self.run_async_command(self.database.get_location_weather(location["id"]))
|
|
weather_msg = ""
|
|
if weather:
|
|
weather_patterns = getattr(self.game_engine, 'weather_patterns', {})
|
|
weather_info = weather_patterns.get("weather_types", {}).get(weather["weather_type"], {})
|
|
weather_desc = weather_info.get("description", f"{weather['weather_type']} weather")
|
|
weather_msg = f" Weather: {weather['weather_type']} - {weather_desc}"
|
|
|
|
self.send_message(channel, f"🗺️ {nickname}: You traveled to {destination}. {location['description']}{weather_msg}")
|
|
|
|
def cmd_location(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
location = self.run_async_command(self.database.get_player_location(player["id"]))
|
|
if location:
|
|
self.send_message(channel, f"📍 {nickname}: You are currently in {location['name']}. {location['description']}")
|
|
else:
|
|
self.send_message(channel, f"{nickname}: You seem to be lost! Contact an admin.")
|
|
|
|
def cmd_achievements(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
achievements = self.run_async_command(self.database.get_player_achievements(player["id"]))
|
|
|
|
if achievements:
|
|
self.send_message(channel, f"🏆 {nickname}'s Achievements:")
|
|
for achievement in achievements[:5]: # Show last 5 achievements
|
|
self.send_message(channel, f"• {achievement['name']}: {achievement['description']}")
|
|
|
|
if len(achievements) > 5:
|
|
self.send_message(channel, f"... and {len(achievements) - 5} more!")
|
|
else:
|
|
self.send_message(channel, f"{nickname}: No achievements yet! Keep exploring and catching pets to unlock new areas!")
|
|
|
|
def cmd_weather(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
location = self.run_async_command(self.database.get_player_location(player["id"]))
|
|
if not location:
|
|
self.send_message(channel, f"{nickname}: You seem to be lost!")
|
|
return
|
|
|
|
weather = self.run_async_command(self.database.get_location_weather(location["id"]))
|
|
if weather:
|
|
weather_patterns = getattr(self.game_engine, 'weather_patterns', {})
|
|
weather_info = weather_patterns.get("weather_types", {}).get(weather["weather_type"], {})
|
|
weather_desc = weather_info.get("description", f"{weather['weather_type']} weather")
|
|
|
|
self.send_message(channel, f"🌤️ {nickname}: Current weather in {location['name']}: {weather['weather_type']}")
|
|
self.send_message(channel, f"Effect: {weather_desc}")
|
|
else:
|
|
self.send_message(channel, f"🌤️ {nickname}: The weather in {location['name']} is calm with no special effects.")
|
|
|
|
def cmd_team(self, channel, nickname):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
pets = self.run_async_command(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"]
|
|
team_info.append(f"⭐{name} (Lv.{pet['level']}) - {pet['hp']}/{pet['max_hp']} HP")
|
|
|
|
# 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))
|
|
|
|
def cmd_wild(self, channel, nickname, args):
|
|
if not args:
|
|
self.send_message(channel, f"{nickname}: Specify a location! Available: Starter Town, Whispering Woods, Electric Canyon, Crystal Caves, Frozen Tundra, Dragon's Peak")
|
|
return
|
|
|
|
location_name = " ".join(args).title() # Normalize to Title Case
|
|
wild_pets = self.run_async_command(self.game_engine.get_location_spawns(location_name))
|
|
|
|
if wild_pets:
|
|
pet_list = ", ".join([pet["name"] for pet in wild_pets])
|
|
self.send_message(channel, f"🌿 Wild pets in {location_name}: {pet_list}")
|
|
else:
|
|
self.send_message(channel, f"{nickname}: No location found called '{location_name}'")
|
|
|
|
def cmd_stats(self, channel, nickname, args):
|
|
player = self.run_async_command(self.database.get_player(nickname))
|
|
if not player:
|
|
self.send_message(channel, f"{nickname}: Use !start to begin your journey first!")
|
|
return
|
|
|
|
self.send_message(channel,
|
|
f"📊 {nickname}: Level {player['level']} | {player['experience']} XP | ${player['money']}")
|
|
|
|
if __name__ == "__main__":
|
|
print("🐾 Starting Pet Bot for IRC...")
|
|
bot = PetBot()
|
|
try:
|
|
bot.connect()
|
|
except KeyboardInterrupt:
|
|
print("\n✓ Bot stopped by user")
|
|
except Exception as e:
|
|
print(f"✗ Bot crashed: {e}")
|
|
import traceback
|
|
traceback.print_exc() |