Petbot/modules/npc_events.py
megaproxy cd2ad10aec Add comprehensive NPC events system with community collaboration
- Implement NPC events module with full IRC command support:
  - \!events: View all active community events
  - \!event <id>: Get detailed event information and leaderboard
  - \!contribute <id>: Participate in community events
  - \!eventhelp: Comprehensive event system documentation
- Add NPC events backend system with automatic spawning:
  - Configurable event types (resource gathering, pet rescue, exploration)
  - Difficulty levels (easy, medium, hard) with scaled rewards
  - Community collaboration mechanics with shared progress
  - Automatic event spawning and expiration management
- Database integration for event tracking and player contributions
- Expandable system supporting future event types and mechanics
- Admin \!startevent command for manual event creation
- Comprehensive error handling and user feedback

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

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

236 lines
No EOL
10 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
NPC Events Module
Handles player commands for NPC events system
"""
from modules.base_module import BaseModule
from src.npc_events import NPCEventsManager
from datetime import datetime
class NPCEventsModule(BaseModule):
"""Module for NPC events system commands"""
def __init__(self, bot, database, game_engine):
super().__init__(bot, database, game_engine)
self.events_manager = NPCEventsManager(database)
def get_commands(self):
return ['events', 'event', 'help', 'contribute', 'eventhelp']
async def handle_command(self, command, channel, nickname, args):
"""Handle NPC events commands"""
# Normalize command
command = self.normalize_input(command)
if command == 'events':
await self.cmd_events(channel, nickname)
elif command == 'event':
await self.cmd_event(channel, nickname, args)
elif command == 'help' and len(args) > 0 and args[0].lower() == 'events':
await self.cmd_event_help(channel, nickname)
elif command == 'contribute':
await self.cmd_contribute(channel, nickname, args)
elif command == 'eventhelp':
await self.cmd_event_help(channel, nickname)
async def cmd_events(self, channel, nickname):
"""Show all active NPC events"""
try:
active_events = await self.events_manager.get_active_events()
if not active_events:
self.send_message(channel, "📅 No active community events at the moment. Check back later!")
return
message = "🎯 **Active Community Events:**\n"
for event in active_events:
progress = self.events_manager.get_progress_bar(
event['current_contributions'],
event['target_contributions']
)
# Calculate time remaining
expires_at = datetime.fromisoformat(event['expires_at'])
time_left = expires_at - datetime.now()
if time_left.total_seconds() > 0:
hours_left = int(time_left.total_seconds() / 3600)
minutes_left = int((time_left.total_seconds() % 3600) / 60)
time_str = f"{hours_left}h {minutes_left}m"
else:
time_str = "Expired"
difficulty_stars = "" * event['difficulty']
message += f"\n**#{event['id']} - {event['title']}** {difficulty_stars}\n"
message += f"📝 {event['description']}\n"
message += f"📊 Progress: {progress}\n"
message += f"⏰ Time left: {time_str}\n"
message += f"💰 Reward: {event['reward_experience']} XP, ${event['reward_money']}\n"
message += f"🤝 Use `!contribute {event['id']}` to help!\n"
self.send_message(channel, message)
except Exception as e:
print(f"Error in cmd_events: {e}")
self.send_message(channel, f"❌ Error fetching events: {str(e)}")
async def cmd_event(self, channel, nickname, args):
"""Show details for a specific event"""
if not args:
self.send_message(channel, "❌ Usage: !event <event_id>")
return
try:
event_id = int(args[0])
event = await self.events_manager.get_event_details(event_id)
if not event:
self.send_message(channel, f"❌ Event #{event_id} not found or expired.")
return
# Calculate time remaining
expires_at = datetime.fromisoformat(event['expires_at'])
time_left = expires_at - datetime.now()
if time_left.total_seconds() > 0:
hours_left = int(time_left.total_seconds() / 3600)
minutes_left = int((time_left.total_seconds() % 3600) / 60)
time_str = f"{hours_left}h {minutes_left}m"
else:
time_str = "Expired"
progress = self.events_manager.get_progress_bar(
event['current_contributions'],
event['target_contributions']
)
difficulty_stars = "" * event['difficulty']
status_emoji = "🔄" if event['status'] == 'active' else "" if event['status'] == 'completed' else ""
message = f"{status_emoji} **Event #{event['id']}: {event['title']}** {difficulty_stars}\n"
message += f"📝 {event['description']}\n"
message += f"📊 Progress: {progress}\n"
message += f"⏰ Time left: {time_str}\n"
message += f"💰 Reward: {event['reward_experience']} XP, ${event['reward_money']}\n"
message += f"🏆 Status: {event['status'].title()}\n"
# Show leaderboard if there are contributors
if event['leaderboard']:
message += "\n🏅 **Top Contributors:**\n"
for i, contributor in enumerate(event['leaderboard'][:5]): # Top 5
rank_emoji = ["🥇", "🥈", "🥉", "4", "5"][i]
message += f"{rank_emoji} {contributor['nickname']}: {contributor['contributions']} contributions\n"
if event['status'] == 'active':
message += f"\n🤝 Use `!contribute {event['id']}` to help!"
self.send_message(channel, message)
except ValueError:
self.send_message(channel, "❌ Invalid event ID. Please use a number.")
except Exception as e:
print(f"Error in cmd_event: {e}")
self.send_message(channel, f"❌ Error fetching event details: {str(e)}")
async def cmd_contribute(self, channel, nickname, args):
"""Allow player to contribute to an event"""
if not args:
self.send_message(channel, "❌ Usage: !contribute <event_id>")
return
player = await self.require_player(channel, nickname)
if not player:
return
try:
event_id = int(args[0])
# Check if event exists and is active
event = await self.events_manager.get_event_details(event_id)
if not event:
self.send_message(channel, f"❌ Event #{event_id} not found or expired.")
return
if event['status'] != 'active':
self.send_message(channel, f"❌ Event #{event_id} is not active.")
return
# Check if event has expired
expires_at = datetime.fromisoformat(event['expires_at'])
if datetime.now() >= expires_at:
self.send_message(channel, f"❌ Event #{event_id} has expired.")
return
# Add contribution
result = await self.events_manager.contribute_to_event(event_id, player['id'])
if not result['success']:
self.send_message(channel, f"❌ Failed to contribute: {result.get('error', 'Unknown error')}")
return
# Get updated event details
updated_event = await self.events_manager.get_event_details(event_id)
player_contributions = await self.events_manager.get_player_contributions(player['id'], event_id)
progress = self.events_manager.get_progress_bar(
updated_event['current_contributions'],
updated_event['target_contributions']
)
if result['event_completed']:
self.send_message(channel, f"🎉 **EVENT COMPLETED!** {updated_event['completion_message']}")
self.send_message(channel, f"🏆 Thanks to everyone who participated! Rewards will be distributed shortly.")
else:
self.send_message(channel, f"{nickname} contributed to '{updated_event['title']}'!")
self.send_message(channel, f"📊 Progress: {progress}")
self.send_message(channel, f"🤝 Your total contributions: {player_contributions}")
# Show encouragement based on progress
progress_percent = (updated_event['current_contributions'] / updated_event['target_contributions']) * 100
if progress_percent >= 75:
self.send_message(channel, "🔥 Almost there! Keep it up!")
elif progress_percent >= 50:
self.send_message(channel, "💪 Great progress! We're halfway there!")
elif progress_percent >= 25:
self.send_message(channel, "🌟 Good start! Keep contributing!")
except ValueError:
self.send_message(channel, "❌ Invalid event ID. Please use a number.")
except Exception as e:
print(f"Error in cmd_contribute: {e}")
self.send_message(channel, f"❌ Error contributing to event: {str(e)}")
async def cmd_event_help(self, channel, nickname):
"""Show help for NPC events system"""
message = """🎯 **Community Events System Help**
**Available Commands:**
• `!events` - Show all active community events
• `!event <id>` - Show details for a specific event
• `!contribute <id>` - Contribute to an event
• `!eventhelp` - Show this help message
**How Events Work:**
🌟 Random community events spawn regularly
🤝 All players can contribute to the same events
📊 Events have progress bars and time limits
🏆 Everyone who contributes gets rewards when completed
⭐ Events have different difficulty levels (1-3 stars)
**Event Types:**
• 🏪 Resource Gathering - Help collect supplies
• 🐾 Pet Rescue - Search for missing pets
• 🎪 Community Projects - Work together on town projects
• 🚨 Emergency Response - Help during crises
• 🔬 Research - Assist with scientific discoveries
**Tips:**
• Check `!events` regularly for new opportunities
• Higher difficulty events give better rewards
• Contributing more increases your reward multiplier
• Events expire after their time limit"""
self.send_message(channel, message)