diff --git a/config/items.json b/config/items.json index c17f6dc..0912108 100644 --- a/config/items.json +++ b/config/items.json @@ -188,6 +188,19 @@ "spawn_rate": 0.0075 } ], + "treasure_items": [ + { + "id": 17, + "name": "Coin Pouch", + "description": "A small leather pouch containing loose coins", + "rarity": "rare", + "category": "treasure", + "effect": "money", + "effect_value": "1-3", + "locations": ["all"], + "spawn_rate": 0.008 + } + ], "rarity_info": { "common": { "color": "white", diff --git a/modules/inventory.py b/modules/inventory.py index 9994ad6..227e882 100644 --- a/modules/inventory.py +++ b/modules/inventory.py @@ -99,6 +99,22 @@ class Inventory(BaseModule): self.send_message(channel, f"🍀 {nickname}: Used {item['name']}! Rare pet encounter rate increased by {effect_value}% for 1 hour!") + elif effect == "money": + # Handle money items (like Coin Pouch) + import random + if "-" in str(effect_value): + # Parse range like "1-3" + min_coins, max_coins = map(int, str(effect_value).split("-")) + coins_gained = random.randint(min_coins, max_coins) + else: + coins_gained = int(effect_value) + + # Add money to player + await self.database.add_money(player["id"], coins_gained) + + self.send_message(channel, + f"💰 {nickname}: Used {item['name']}! Found {coins_gained} coins inside!") + elif effect == "none": self.send_message(channel, f"📦 {nickname}: Used {item['name']}! This item has no immediate effect but may be useful later.") diff --git a/modules/pet_management.py b/modules/pet_management.py index 911bef5..b75ef2c 100644 --- a/modules/pet_management.py +++ b/modules/pet_management.py @@ -22,51 +22,13 @@ class PetManagement(BaseModule): await self.cmd_nickname(channel, nickname, args) async def cmd_team(self, channel, nickname): - """Show active pets (channel display)""" + """Redirect player to their team builder page""" 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 and team position - 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" - - # Show team position - position = pet.get("team_order", "?") - team_info.append(f"[{position}]⭐{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)) - self.send_message(channel, f"🌐 View detailed pet collection at: http://petz.rdx4.com/player/{nickname}#pets") + # Redirect to web interface for team management + self.send_message(channel, f"⚔️ {nickname}: Manage your team at: http://petz.rdx4.com/teambuilder/{nickname}") async def cmd_pets(self, channel, nickname): """Show link to pet collection web page""" diff --git a/webserver.py b/webserver.py index 5b28697..04b9343 100644 --- a/webserver.py +++ b/webserver.py @@ -1402,12 +1402,405 @@ class PetBotRequestHandler(BaseHTTPRequestHandler): self.wfile.write(html_content.encode()) def serve_leaderboard(self): - """Serve the leaderboard page - redirect to players for now""" - # For now, leaderboard is the same as players page since they're ranked - # In the future, this could have different categories - self.send_response(302) # Temporary redirect - self.send_header('Location', '/players') - self.end_headers() + """Serve the enhanced leaderboard page with multiple categories""" + import asyncio + + # Check rate limit first + allowed, rate_limit_message = self.check_rate_limit() + if not allowed: + self.send_rate_limit_error(rate_limit_message) + return + + # Get database instance + database = self.server.database if hasattr(self.server, 'database') else None + + if not database: + self.serve_error_page("Leaderboard", "Database not available") + return + + try: + # Run async database operations in event loop + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # Get all leaderboard data + leaderboard_data = loop.run_until_complete(self.get_leaderboard_data(database)) + + # Generate HTML content + content = self.generate_leaderboard_content(leaderboard_data) + + html_content = self.get_page_template("Leaderboard - PetBot", content, "leaderboard") + + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + self.wfile.write(html_content.encode()) + + except Exception as e: + print(f"Error generating leaderboard: {e}") + self.serve_error_page("Leaderboard", f"Error loading leaderboard data: {str(e)}") + finally: + loop.close() + + async def get_leaderboard_data(self, database): + """Get all leaderboard data for different categories""" + leaderboard_data = {} + + # 1. Top Players by Level + leaderboard_data['levels'] = await self.get_level_leaderboard(database) + + # 2. Top Players by Experience + leaderboard_data['experience'] = await self.get_experience_leaderboard(database) + + # 3. Richest Players + leaderboard_data['money'] = await self.get_money_leaderboard(database) + + # 4. Most Pets Collected + leaderboard_data['pet_count'] = await self.get_pet_count_leaderboard(database) + + # 5. Most Achievements + leaderboard_data['achievements'] = await self.get_achievement_leaderboard(database) + + # 6. Gym Champions (most gym badges) + leaderboard_data['gym_badges'] = await self.get_gym_badge_leaderboard(database) + + # 7. Highest Level Pet + leaderboard_data['highest_pet'] = await self.get_highest_pet_leaderboard(database) + + # 8. Most Rare Pets + leaderboard_data['rare_pets'] = await self.get_rare_pet_leaderboard(database) + + return leaderboard_data + + async def get_level_leaderboard(self, database): + """Get top players by level""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + cursor = await db.execute(""" + SELECT nickname, level, experience + FROM players + ORDER BY level DESC, experience DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "level": p[1], "experience": p[2]} for p in players] + + async def get_experience_leaderboard(self, database): + """Get top players by total experience""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + cursor = await db.execute(""" + SELECT nickname, level, experience + FROM players + ORDER BY experience DESC, level DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "level": p[1], "experience": p[2]} for p in players] + + async def get_money_leaderboard(self, database): + """Get richest players""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + cursor = await db.execute(""" + SELECT nickname, money, level + FROM players + ORDER BY money DESC, level DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "money": p[1], "level": p[2]} for p in players] + + async def get_pet_count_leaderboard(self, database): + """Get players with most pets""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + cursor = await db.execute(""" + SELECT p.nickname, COUNT(pets.id) as pet_count, p.level + FROM players p + LEFT JOIN pets ON p.id = pets.player_id + GROUP BY p.id, p.nickname, p.level + ORDER BY pet_count DESC, p.level DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "pet_count": p[1], "level": p[2]} for p in players] + + async def get_achievement_leaderboard(self, database): + """Get players with most achievements""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + cursor = await db.execute(""" + SELECT p.nickname, COUNT(pa.achievement_id) as achievement_count, p.level + FROM players p + LEFT JOIN player_achievements pa ON p.id = pa.player_id + GROUP BY p.id, p.nickname, p.level + ORDER BY achievement_count DESC, p.level DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "achievement_count": p[1], "level": p[2]} for p in players] + + async def get_gym_badge_leaderboard(self, database): + """Get players with most gym victories (substitute for badges)""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + # Check if player_gym_battles table exists and has data + cursor = await db.execute(""" + SELECT p.nickname, + COALESCE(COUNT(DISTINCT CASE WHEN pgb.victories > 0 THEN pgb.gym_id END), 0) as gym_victories, + p.level + FROM players p + LEFT JOIN player_gym_battles pgb ON p.id = pgb.player_id + GROUP BY p.id, p.nickname, p.level + ORDER BY gym_victories DESC, p.level DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "badge_count": p[1], "level": p[2]} for p in players] + + async def get_highest_pet_leaderboard(self, database): + """Get players with highest level pets""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + cursor = await db.execute(""" + SELECT p.nickname, MAX(pets.level) as highest_pet_level, + ps.name as pet_species, p.level as player_level + FROM players p + JOIN pets ON p.id = pets.player_id + JOIN pet_species ps ON pets.species_id = ps.id + GROUP BY p.id, p.nickname, p.level + ORDER BY highest_pet_level DESC, p.level DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "highest_pet_level": p[1], "pet_species": p[2], "player_level": p[3]} for p in players] + + async def get_rare_pet_leaderboard(self, database): + """Get players with most rare pets (epic/legendary)""" + import aiosqlite + async with aiosqlite.connect(database.db_path) as db: + cursor = await db.execute(""" + SELECT p.nickname, COUNT(pets.id) as rare_pet_count, p.level + FROM players p + JOIN pets ON p.id = pets.player_id + JOIN pet_species ps ON pets.species_id = ps.id + WHERE ps.rarity >= 4 + GROUP BY p.id, p.nickname, p.level + ORDER BY rare_pet_count DESC, p.level DESC + LIMIT 10 + """) + players = await cursor.fetchall() + + return [{"nickname": p[0], "rare_pet_count": p[1], "level": p[2]} for p in players] + + def generate_leaderboard_content(self, leaderboard_data): + """Generate HTML content for the enhanced leaderboard""" + content = """ +
Compete with trainers across all categories!
+| {header} | ' + content += '
|---|
| {cell} | ' + content += '
!use {item['name']}