123 lines
4.2 KiB
JavaScript
123 lines
4.2 KiB
JavaScript
/**
|
|
* 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;
|