From bd455f1be564a4f96a0fae0475cb86c01bacd5ec Mon Sep 17 00:00:00 2001 From: megaproxy Date: Mon, 14 Jul 2025 16:11:20 +0100 Subject: [PATCH] Implement comprehensive experience and leveling system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **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 --- modules/battle_system.py | 77 +++++++++++++++++++++++++- modules/exploration.py | 36 ++++++++++++- modules/pet_management.py | 14 ++++- src/database.py | 110 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 233 insertions(+), 4 deletions(-) diff --git a/modules/battle_system.py b/modules/battle_system.py index 54c26b6..4a7a0a8 100644 --- a/modules/battle_system.py +++ b/modules/battle_system.py @@ -134,8 +134,11 @@ class BattleSystem(BaseModule): # Regular wild battle if result["winner"] == "player": self.send_message(channel, f"🎉 {nickname}: You won the battle!") - # Remove encounter since battle is over + + # Award experience for victory if player["id"] in self.bot.active_encounters: + wild_pet = self.bot.active_encounters[player["id"]] + await self.award_battle_experience(channel, nickname, player, wild_pet, "wild") del self.bot.active_encounters[player["id"]] else: self.send_message(channel, f"💀 {nickname}: Your pet fainted! You lost the battle...") @@ -224,6 +227,9 @@ class BattleSystem(BaseModule): self.send_message(channel, f"🎉 {nickname}: You defeated {defeated_pet['species_name']}!") + # Award experience for defeating gym pet + await self.award_battle_experience(channel, nickname, player, defeated_pet, "gym") + # Check if there are more pets if await self.database.advance_gym_battle(player["id"]): # More pets to fight @@ -291,4 +297,71 @@ class BattleSystem(BaseModule): self.send_message(channel, f"❌ {nickname}: Gym battle error occurred - please !forfeit and try again") print(f"Gym battle completion error: {e}") import traceback - traceback.print_exc() \ No newline at end of file + traceback.print_exc() + + async def award_battle_experience(self, channel, nickname, player, defeated_pet, battle_type="wild"): + """Award experience to active pets for battle victory""" + active_pets = await self.database.get_active_pets(player["id"]) + if not active_pets: + return + + # Calculate experience based on defeated pet and battle type + base_exp = self.calculate_battle_exp(defeated_pet, battle_type) + + # Award to first active pet (the one that was battling) + main_pet = active_pets[0] + exp_result = await self.database.award_experience(main_pet["id"], base_exp) + + if exp_result["success"]: + # Display experience gain + self.send_message(channel, + f"⭐ {exp_result['pet_name']} gained {exp_result['exp_gained']} EXP!") + + # Handle level up + if exp_result["leveled_up"]: + await self.handle_level_up_display(channel, nickname, exp_result) + + def calculate_battle_exp(self, defeated_pet, battle_type="wild"): + """Calculate experience gain for defeating a pet""" + base_exp = defeated_pet["level"] * 10 # Base: 10 EXP per level + + # Battle type multipliers + multipliers = { + "wild": 1.0, + "gym": 2.0, # Double EXP for gym battles + "trainer": 1.5 # Future: trainer battles + } + + multiplier = multipliers.get(battle_type, 1.0) + return int(base_exp * multiplier) + + async def handle_level_up_display(self, channel, nickname, exp_result): + """Display level up information""" + levels_gained = exp_result["levels_gained"] + pet_name = exp_result["pet_name"] + + if levels_gained == 1: + self.send_message(channel, + f"🎉 {pet_name} leveled up! Now level {exp_result['new_level']}!") + else: + self.send_message(channel, + f"🎉 {pet_name} gained {levels_gained} levels! Now level {exp_result['new_level']}!") + + # Show stat increases + if "stat_increases" in exp_result: + stats = exp_result["stat_increases"] + stat_msg = f"📈 Stats increased: " + stat_parts = [] + + if stats["hp"] > 0: + stat_parts.append(f"HP +{stats['hp']}") + if stats["attack"] > 0: + stat_parts.append(f"ATK +{stats['attack']}") + if stats["defense"] > 0: + stat_parts.append(f"DEF +{stats['defense']}") + if stats["speed"] > 0: + stat_parts.append(f"SPD +{stats['speed']}") + + if stat_parts: + stat_msg += " | ".join(stat_parts) + self.send_message(channel, stat_msg) \ No newline at end of file diff --git a/modules/exploration.py b/modules/exploration.py index 368bc3b..b59a83a 100644 --- a/modules/exploration.py +++ b/modules/exploration.py @@ -212,6 +212,9 @@ class Exploration(BaseModule): for achievement in all_achievements: self.send_message(channel, f"🏆 {nickname}: Achievement unlocked: {achievement['name']}! {achievement['description']}") + # Award experience for successful catch + await self.award_catch_experience(channel, nickname, player, wild_pet) + # Remove encounter if player["id"] in self.bot.active_encounters: del self.bot.active_encounters[player["id"]] @@ -233,6 +236,9 @@ class Exploration(BaseModule): # Check for achievements after successful catch if "Success!" in result: + # Award experience for successful catch + await self.award_catch_experience(channel, nickname, player, target_pet) + type_achievements = await self.game_engine.check_and_award_achievements(player["id"], "catch_type", "") total_achievements = await self.game_engine.check_and_award_achievements(player["id"], "catch_total", "") @@ -244,4 +250,32 @@ class Exploration(BaseModule): # Remove the encounter regardless of success del self.bot.active_encounters[player["id"]] - self.send_message(channel, f"🎯 {nickname}: {result}") \ No newline at end of file + self.send_message(channel, f"🎯 {nickname}: {result}") + + async def award_catch_experience(self, channel, nickname, player, caught_pet): + """Award experience to active pets for successful catch""" + active_pets = await self.database.get_active_pets(player["id"]) + if not active_pets: + return + + # Calculate experience for catch (less than battle victory) + base_exp = caught_pet["level"] * 5 # 5 EXP per level for catches + + # Award to first active pet + main_pet = active_pets[0] + exp_result = await self.database.award_experience(main_pet["id"], base_exp) + + if exp_result["success"]: + # Display experience gain + self.send_message(channel, + f"⭐ {exp_result['pet_name']} gained {exp_result['exp_gained']} EXP for the catch!") + + # Handle level up + if exp_result["leveled_up"]: + await self.handle_level_up_display(channel, nickname, exp_result) + + async def handle_level_up_display(self, channel, nickname, exp_result): + """Display level up information (shared with battle system)""" + from .battle_system import BattleSystem + battle_system = BattleSystem(self.bot, self.database, self.game_engine) + await battle_system.handle_level_up_display(channel, nickname, exp_result) \ No newline at end of file diff --git a/modules/pet_management.py b/modules/pet_management.py index 2e3a11b..cd6d040 100644 --- a/modules/pet_management.py +++ b/modules/pet_management.py @@ -41,7 +41,19 @@ class PetManagement(BaseModule): # 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") + + # 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 diff --git a/src/database.py b/src/database.py index 2d94f4c..4a9ddee 100644 --- a/src/database.py +++ b/src/database.py @@ -769,6 +769,116 @@ class Database: rows = await cursor.fetchall() return [dict(row) for row in rows] + def calculate_exp_for_level(self, level: int) -> int: + """Calculate total experience needed to reach a level""" + # Using a cubic growth formula: level^3 * 4 - 12 + return max(0, (level ** 3) * 4 - 12) + + def calculate_level_from_exp(self, exp: int) -> int: + """Calculate what level a pet should be based on experience""" + level = 1 + while self.calculate_exp_for_level(level + 1) <= exp: + level += 1 + return min(level, 100) # Cap at level 100 + + async def award_experience(self, pet_id: int, exp_amount: int) -> Dict: + """Award experience to a pet and handle leveling up""" + async with aiosqlite.connect(self.db_path) as db: + db.row_factory = aiosqlite.Row + + # Get current pet data + cursor = await db.execute(""" + SELECT p.*, ps.name as species_name, ps.base_hp, ps.base_attack, + ps.base_defense, ps.base_speed + FROM pets p + JOIN pet_species ps ON p.species_id = ps.id + WHERE p.id = ? + """, (pet_id,)) + + pet = await cursor.fetchone() + if not pet: + return {"success": False, "error": "Pet not found"} + + pet_dict = dict(pet) + old_level = pet_dict["level"] + old_exp = pet_dict["experience"] + new_exp = old_exp + exp_amount + new_level = self.calculate_level_from_exp(new_exp) + + result = { + "success": True, + "pet_id": pet_id, + "pet_name": pet_dict["nickname"] or pet_dict["species_name"], + "species_name": pet_dict["species_name"], + "old_level": old_level, + "new_level": new_level, + "old_exp": old_exp, + "new_exp": new_exp, + "exp_gained": exp_amount, + "leveled_up": new_level > old_level, + "levels_gained": new_level - old_level + } + + # Update experience + await db.execute(""" + UPDATE pets SET experience = ? WHERE id = ? + """, (new_exp, pet_id)) + + # Handle level up if it occurred + if new_level > old_level: + await self._handle_level_up(db, pet_dict, new_level) + result["stat_increases"] = await self._calculate_stat_increases(pet_dict, old_level, new_level) + + await db.commit() + return result + + async def _handle_level_up(self, db, pet_dict: Dict, new_level: int): + """Handle pet leveling up - recalculate stats and HP""" + # Calculate new stats based on level + new_stats = self._calculate_pet_stats(pet_dict, new_level) + + # Update pet stats and level + await db.execute(""" + UPDATE pets + SET level = ?, max_hp = ?, attack = ?, defense = ?, speed = ?, hp = ? + WHERE id = ? + """, ( + new_level, + new_stats["hp"], + new_stats["attack"], + new_stats["defense"], + new_stats["speed"], + new_stats["hp"], # Restore full HP on level up + pet_dict["id"] + )) + + def _calculate_pet_stats(self, pet_dict: Dict, level: int) -> Dict: + """Calculate pet stats for a given level""" + # Pokémon-style stat calculation + hp = int((2 * pet_dict["base_hp"] + 31) * level / 100) + level + 10 + attack = int((2 * pet_dict["base_attack"] + 31) * level / 100) + 5 + defense = int((2 * pet_dict["base_defense"] + 31) * level / 100) + 5 + speed = int((2 * pet_dict["base_speed"] + 31) * level / 100) + 5 + + return { + "hp": hp, + "attack": attack, + "defense": defense, + "speed": speed + } + + async def _calculate_stat_increases(self, pet_dict: Dict, old_level: int, new_level: int) -> Dict: + """Calculate stat increases from leveling up""" + old_stats = self._calculate_pet_stats(pet_dict, old_level) + new_stats = self._calculate_pet_stats(pet_dict, new_level) + + return { + "hp": new_stats["hp"] - old_stats["hp"], + "attack": new_stats["attack"] - old_stats["attack"], + "defense": new_stats["defense"] - old_stats["defense"], + "speed": new_stats["speed"] - old_stats["speed"] + } + # Gym Battle System Methods async def get_gyms_in_location(self, location_id: int, player_id: int = None) -> List[Dict]: """Get all gyms in a specific location with optional player progress"""