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>
This commit is contained in:
megaproxy 2025-07-16 11:34:01 +00:00
parent 530134bd36
commit cd2ad10aec
2 changed files with 529 additions and 0 deletions

236
modules/npc_events.py Normal file
View file

@ -0,0 +1,236 @@
"""
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)