🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
300 lines
No EOL
15 KiB
Python
300 lines
No EOL
15 KiB
Python
#!/usr/bin/env python3
|
|
"""Exploration commands module for PetBot"""
|
|
|
|
from .base_module import BaseModule
|
|
|
|
class Exploration(BaseModule):
|
|
"""Handles exploration, travel, location, weather, and wild commands"""
|
|
|
|
def get_commands(self):
|
|
return ["explore", "travel", "location", "where", "weather", "wild", "catch", "capture"]
|
|
|
|
async def handle_command(self, channel, nickname, command, args):
|
|
if command == "explore":
|
|
await self.cmd_explore(channel, nickname)
|
|
elif command == "travel":
|
|
await self.cmd_travel(channel, nickname, args)
|
|
elif command in ["location", "where"]:
|
|
await self.cmd_location(channel, nickname)
|
|
elif command == "weather":
|
|
await self.cmd_weather(channel, nickname)
|
|
elif command == "wild":
|
|
await self.cmd_wild(channel, nickname, args)
|
|
elif command in ["catch", "capture"]:
|
|
await self.cmd_catch(channel, nickname)
|
|
|
|
async def cmd_explore(self, channel, nickname):
|
|
"""Explore current location"""
|
|
player = await self.require_player(channel, nickname)
|
|
if not player:
|
|
return
|
|
|
|
encounter = await 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"] == "item_found":
|
|
self.send_message(channel, f"{nickname}: {encounter['message']}")
|
|
elif encounter["type"] == "encounter":
|
|
# Store the encounter for potential catching
|
|
self.bot.active_encounters[player["id"]] = encounter["pet"]
|
|
|
|
pet = encounter["pet"]
|
|
type_str = pet["type1"]
|
|
if pet["type2"]:
|
|
type_str += f"/{pet['type2']}"
|
|
|
|
# Record the encounter
|
|
await self.database.record_encounter(player["id"], pet["species_name"])
|
|
|
|
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!")
|
|
|
|
async def cmd_travel(self, channel, nickname, args):
|
|
"""Travel to a different location"""
|
|
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 = await self.require_player(channel, nickname)
|
|
if not player:
|
|
return
|
|
|
|
# Handle various input formats and normalize location names
|
|
destination_input = " ".join(args).lower()
|
|
|
|
# Map common variations to exact location names
|
|
location_mappings = {
|
|
"starter town": "Starter Town",
|
|
"whispering woods": "Whispering Woods",
|
|
"electric canyon": "Electric Canyon",
|
|
"crystal caves": "Crystal Caves",
|
|
"frozen tundra": "Frozen Tundra",
|
|
"dragon's peak": "Dragon's Peak",
|
|
"dragons peak": "Dragon's Peak",
|
|
"dragon peak": "Dragon's Peak",
|
|
"dragons-peak": "Dragon's Peak"
|
|
}
|
|
|
|
destination = location_mappings.get(destination_input)
|
|
if not destination:
|
|
# Fall back to title case if no mapping found
|
|
destination = " ".join(args).title()
|
|
|
|
location = await self.database.get_location_by_name(destination)
|
|
|
|
if not location:
|
|
self.send_message(channel, f"{nickname}: '{destination}' is not a valid location!")
|
|
return
|
|
|
|
# CRITICAL FIX: Check and award any outstanding achievements before checking travel requirements
|
|
# This ensures players get credit for achievements they've earned but haven't been awarded yet
|
|
print(f"🔄 Checking all achievements for {nickname} before travel...")
|
|
|
|
# Check ALL possible achievements comprehensively
|
|
all_new_achievements = await self.game_engine.check_all_achievements(player["id"])
|
|
if all_new_achievements:
|
|
for achievement in all_new_achievements:
|
|
self.send_message(channel, f"🏆 {nickname}: Achievement unlocked: {achievement['name']}! {achievement['description']}")
|
|
|
|
# Now check if player can access this location (after awarding achievements)
|
|
missing_requirements = await self.database.get_missing_location_requirements(player["id"], location["id"])
|
|
if missing_requirements:
|
|
# Build specific message about required achievements
|
|
if len(missing_requirements) == 1:
|
|
achievement = missing_requirements[0]
|
|
self.send_message(channel, f"{nickname}: You cannot access {destination} yet! Required achievement: '{achievement['name']}' - {achievement['description']}")
|
|
else:
|
|
achievement_names = [f"'{req['name']}'" for req in missing_requirements]
|
|
self.send_message(channel, f"{nickname}: You cannot access {destination} yet! Required achievements: {', '.join(achievement_names)}. Use !achievements to see progress.")
|
|
return
|
|
|
|
# Clear any active encounters when traveling
|
|
if player["id"] in self.bot.active_encounters:
|
|
del self.bot.active_encounters[player["id"]]
|
|
|
|
await self.database.update_player_location(player["id"], location["id"])
|
|
|
|
# Show weather info
|
|
weather = await 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}")
|
|
|
|
async def cmd_location(self, channel, nickname):
|
|
"""Show current location"""
|
|
player = await self.require_player(channel, nickname)
|
|
if not player:
|
|
return
|
|
|
|
location = await 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.")
|
|
|
|
async def cmd_weather(self, channel, nickname):
|
|
"""Show current weather"""
|
|
player = await self.require_player(channel, nickname)
|
|
if not player:
|
|
return
|
|
|
|
location = await self.database.get_player_location(player["id"])
|
|
if not location:
|
|
self.send_message(channel, f"{nickname}: You seem to be lost!")
|
|
return
|
|
|
|
weather = await 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.")
|
|
|
|
async def cmd_wild(self, channel, nickname, args):
|
|
"""Show wild pets in location (defaults to current)"""
|
|
player = await self.require_player(channel, nickname)
|
|
if not player:
|
|
return
|
|
|
|
if args:
|
|
# Specific location requested
|
|
location_name = " ".join(args).title()
|
|
else:
|
|
# Default to current location
|
|
current_location = await self.database.get_player_location(player["id"])
|
|
if not current_location:
|
|
self.send_message(channel, f"{nickname}: You seem to be lost!")
|
|
return
|
|
location_name = current_location["name"]
|
|
|
|
wild_pets = await 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}'")
|
|
|
|
async def cmd_catch(self, channel, nickname):
|
|
"""Catch a pet during exploration or battle"""
|
|
player = await self.require_player(channel, nickname)
|
|
if not player:
|
|
return
|
|
|
|
# Check if player is in an active battle
|
|
active_battle = await 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 = await self.game_engine.attempt_catch_current_location(player["id"], wild_pet)
|
|
|
|
# Record the successful catch
|
|
await self.database.record_encounter(player["id"], wild_pet["species_name"], was_caught=True)
|
|
|
|
# End the battle
|
|
await_result = await self.game_engine.battle_engine.end_battle(player["id"], "caught")
|
|
|
|
# Check for achievements
|
|
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", "")
|
|
|
|
all_achievements = type_achievements + total_achievements
|
|
if all_achievements:
|
|
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"]]
|
|
|
|
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.bot.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.bot.active_encounters[player["id"]]
|
|
result = await self.game_engine.attempt_catch_current_location(player["id"], target_pet)
|
|
|
|
# Check for achievements after successful catch
|
|
if "Success!" in result:
|
|
# Record the successful catch
|
|
await self.database.record_encounter(player["id"], target_pet["species_name"], was_caught=True)
|
|
|
|
# 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", "")
|
|
|
|
all_achievements = type_achievements + total_achievements
|
|
if all_achievements:
|
|
for achievement in all_achievements:
|
|
self.send_message(channel, f"🏆 {nickname}: Achievement unlocked: {achievement['name']}! {achievement['description']}")
|
|
|
|
# Remove the encounter regardless of success
|
|
del self.bot.active_encounters[player["id"]]
|
|
|
|
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) |