/** * Galaxy Strike Online โ€” Galaxy Events (GDD ยง20.2) * Types: Alien Invasion, Void Surge, Sector Domination, Rogue AI Fleet Boss * Events rotate on a schedule and reward all participating players. */ const EVENT_TYPES = { alien_invasion: { name: 'Alien Invasion', icon: '๐Ÿ‘พ', desc: 'A massive alien fleet has entered the galaxy. Coordinate with other commanders to repel the threat.', durationHours: 24, rewardPool: { credits: 50000, xp: 2000, darkMatter: 50 }, minParticipants: 1, }, void_surge: { name: 'Void Surge', icon: '๐ŸŒ€', desc: 'Dark matter concentrations spike across multiple sectors. Mine bonus Dark Matter while the surge lasts.', durationHours: 12, rewardPool: { credits: 20000, xp: 1000, darkMatter: 200 }, minParticipants: 1, bonuses: { darkMatterMultiplier: 3 }, }, rogue_ai_boss: { name: 'Rogue AI Fleet Boss', icon: '๐Ÿค–', desc: 'The Rogue AI Collective has deployed a capital-class vessel. Destroy it for legendary loot including Dreadnought Blueprints.', durationHours: 48, rewardPool: { credits: 100000, xp: 5000, darkMatter: 100, loot: ['dreadnought_blueprint'] }, minParticipants: 5, }, sector_domination: { name: 'Sector Domination', icon: 'โš”', desc: 'Alliance territory season is active. The alliance controlling the most sectors at the end wins exclusive skins and titles.', durationHours: 720, // 30 days rewardPool: { credits: 200000, exclusiveSkin: true }, minParticipants: 10, }, }; class GalaxyEventSystem { constructor() { this.activeEvent = null; this.participants = new Map(); // userId -> contribution score this.eventHistory = []; this._scheduleNext(); } _scheduleNext() { // Rotate through events every 6 hours in dev; configurable in prod const keys = Object.keys(EVENT_TYPES); const idx = this.eventHistory.length % keys.length; const type = keys[idx]; const def = EVENT_TYPES[type]; const now = Date.now(); this.activeEvent = { id: 'event_' + now, type, ...def, startedAt: now, endsAt: now + def.durationHours * 3600 * 1000, participantCount: 0, status: 'active', }; this.participants.clear(); // Schedule end setTimeout(() => this._endEvent(), def.durationHours * 3600 * 1000); console.log(`[GALAXY EVENT] Started: ${def.name} โ€” ends in ${def.durationHours}h`); } _endEvent() { if (!this.activeEvent) return; this.activeEvent.status = 'ended'; this.eventHistory.push({ ...this.activeEvent, endedAt: Date.now() }); this.activeEvent = null; // Schedule next after 30 min break setTimeout(() => this._scheduleNext(), 30 * 60 * 1000); } participate(userId, contributionDelta = 1) { if (!this.activeEvent || this.activeEvent.status !== 'active') return false; const current = this.participants.get(userId) || 0; this.participants.set(userId, current + contributionDelta); this.activeEvent.participantCount = this.participants.size; return true; } getEventData() { if (!this.activeEvent) return { active: false }; const now = Date.now(); const remaining = Math.max(0, this.activeEvent.endsAt - now); const h = Math.floor(remaining / 3600000); const m = Math.floor((remaining % 3600000) / 60000); return { active: true, event: { ...this.activeEvent, remainingMs: remaining, etaLabel: h > 0 ? `${h}h ${m}m remaining` : `${m}m remaining`, topParticipants: [...this.participants.entries()] .sort((a,b) => b[1]-a[1]).slice(0,10) .map(([uid, score], i) => ({ rank: i+1, userId: uid, score })), } }; } claimEventReward(playerData) { const score = this.participants.get(playerData.userId) || 0; if (score === 0) return { success: false, error: 'You have not participated in this event.' }; const event = this.activeEvent || {}; const pool = event.rewardPool || {}; if (pool.credits) playerData.stats.credits = (playerData.stats.credits||0) + pool.credits; if (pool.xp) playerData.addExperience?.(pool.xp); return { success: true, rewards: pool }; } } module.exports = GalaxyEventSystem;