- 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>
236 lines
No EOL
10 KiB
Python
236 lines
No EOL
10 KiB
Python
"""
|
||
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) |