Game-Server/GameServer/systems/IdleSystem.js
2026-01-30 10:58:30 -04:00

324 lines
12 KiB
JavaScript

/**
* Galaxy Strike Online - Server Idle System
* Manages offline progression and idle mechanics
*/
class IdleSystem {
constructor() {
// Idle settings
this.maxOfflineTime = 7 * 24 * 60 * 60 * 1000; // 7 days in milliseconds
this.playerLastActive = new Map(); // userId -> last active timestamp
this.playerProductionRates = new Map(); // userId -> production rates
this.playerAchievements = new Map(); // userId -> achievements
// Default production rates
this.defaultProductionRates = {
credits: 0.1, // 1 credit every 10 seconds (0.1 per second)
experience: 0, // no auto experience - only from dungeons
energy: 1/300 // 1 energy every 5 minutes (1/300 per second)
};
// Offline rates (different from online rates)
this.offlineProductionRates = {
credits: 1/60, // 1 credit every 1 minute (1/60 per second)
experience: 0, // no experience offline - only from dungeons
energy: 1/300 // 1 energy every 5 minutes (same as online)
};
// Idle rewards
this.offlineRewards = {
credits: 0,
experience: 0,
energy: 0,
items: []
};
// Idle bonuses
this.bonuses = {
premium: 1.0,
guild: 1.0,
research: 1.0
};
// Idle achievements
this.achievements = {
totalOfflineTime: 0,
maxOfflineSession: 0,
totalIdleCredits: 0,
totalIdleExperience: 0
};
}
initializePlayerData(userId) {
console.log('[IDLE SYSTEM] initializePlayerData called for userId:', userId);
console.log('[IDLE SYSTEM] Current playerProductionRates size:', this.playerProductionRates.size);
if (!this.playerLastActive.has(userId)) {
console.log('[IDLE SYSTEM] Initializing new player data for:', userId);
this.playerLastActive.set(userId, Date.now());
this.playerProductionRates.set(userId, { ...this.defaultProductionRates });
console.log('[IDLE SYSTEM] Set production rates for', userId, ':', this.defaultProductionRates);
this.playerAchievements.set(userId, {
totalOfflineTime: 0,
maxOfflineSession: 0,
totalIdleCredits: 0,
totalIdleExperience: 0
});
console.log('[IDLE SYSTEM] Player data initialized successfully for:', userId);
} else {
console.log('[IDLE SYSTEM] Player data already exists for:', userId);
const existingRates = this.playerProductionRates.get(userId);
console.log('[IDLE SYSTEM] Existing production rates:', existingRates);
}
}
updateLastActive(userId) {
this.playerLastActive.set(userId, Date.now());
}
updateProductionRates(userId, rates) {
this.playerProductionRates.set(userId, {
...this.defaultProductionRates,
...rates
});
}
calculateOfflineProgress(userId) {
this.initializePlayerData(userId);
const lastActive = this.playerLastActive.get(userId);
const currentTime = Date.now();
const offlineTime = currentTime - lastActive;
// Cap offline time to maximum
const cappedOfflineTime = Math.min(offlineTime, this.maxOfflineTime);
if (cappedOfflineTime <= 0) {
return {
offlineTime: 0,
rewards: {
credits: 0,
experience: 0,
energy: 0,
items: []
}
};
}
const productionRates = this.playerProductionRates.get(userId);
const achievements = this.playerAchievements.get(userId);
// Calculate offline rewards using offline rates
const offlineSeconds = Math.floor(cappedOfflineTime / 1000);
const offlineRates = this.offlineProductionRates;
const rewards = {
credits: Math.floor(offlineRates.credits * offlineSeconds),
experience: Math.floor(offlineRates.experience * offlineSeconds),
energy: Math.floor(offlineRates.energy * offlineSeconds),
items: []
};
// Update achievements
achievements.totalOfflineTime += cappedOfflineTime;
achievements.maxOfflineSession = Math.max(achievements.maxOfflineSession, cappedOfflineTime);
achievements.totalIdleCredits += rewards.credits;
achievements.totalIdleExperience += rewards.experience;
// Update last active time
this.playerLastActive.set(userId, currentTime);
return {
offlineTime: cappedOfflineTime,
offlineSeconds,
rewards,
productionRates
};
}
applyIdleRewards(userId, playerData) {
const progress = this.calculateOfflineProgress(userId);
if (progress.offlineTime > 0) {
// Apply rewards to player data
if (playerData.stats) {
playerData.stats.credits = (playerData.stats.credits || 0) + progress.rewards.credits;
playerData.stats.experience = (playerData.stats.experience || 0) + progress.rewards.experience;
}
if (playerData.resources) {
playerData.resources.energy = (playerData.resources.energy || 0) + progress.rewards.energy;
}
// Add items if any
if (progress.rewards.items && progress.rewards.items.length > 0) {
if (!playerData.inventory) {
playerData.inventory = [];
}
playerData.inventory.push(...progress.rewards.items);
}
}
return progress;
}
getProductionRates(userId) {
this.initializePlayerData(userId);
return this.playerProductionRates.get(userId);
}
updateBonuses(userId, bonuses) {
this.initializePlayerData(userId);
const currentRates = this.playerProductionRates.get(userId);
// Apply bonuses to production rates
const bonusMultiplier =
(bonuses.premium || this.defaultBonuses.premium) *
(bonuses.guild || this.defaultBonuses.guild) *
(bonuses.research || this.defaultBonuses.research);
const updatedRates = {};
for (const [resource, baseRate] of Object.entries(this.defaultProductionRates)) {
updatedRates[resource] = Math.floor(baseRate * bonusMultiplier);
}
this.playerProductionRates.set(userId, updatedRates);
return updatedRates;
}
getAchievements(userId) {
this.initializePlayerData(userId);
return this.playerAchievements.get(userId);
}
getIdleStats(userId) {
this.initializePlayerData(userId);
const achievements = this.playerAchievements.get(userId);
const productionRates = this.playerProductionRates.get(userId);
return {
lastActive: this.playerLastActive.get(userId),
productionRates,
achievements,
maxOfflineTime: this.maxOfflineTime,
currentOfflineTime: Date.now() - this.playerLastActive.get(userId)
};
}
// Calculate idle income for a specific time period
calculateIncomeForTime(userId, timeInSeconds) {
this.initializePlayerData(userId);
const productionRates = this.playerProductionRates.get(userId);
return {
credits: Math.floor(productionRates.credits * timeInSeconds),
experience: Math.floor(productionRates.experience * timeInSeconds),
energy: Math.floor(productionRates.energy * timeInSeconds)
};
}
// Get time until next milestone
getTimeUntilMilestone(userId, targetCredits = 10000) {
this.initializePlayerData(userId);
const productionRates = this.playerProductionRates.get(userId);
const currentOfflineTime = Date.now() - this.playerLastActive.get(userId);
if (productionRates.credits <= 0) {
return { seconds: Infinity, hours: Infinity };
}
const creditsPerSecond = productionRates.credits;
const secondsNeeded = Math.ceil(targetCredits / creditsPerSecond);
return {
seconds: secondsNeeded,
minutes: Math.ceil(secondsNeeded / 60),
hours: Math.ceil(secondsNeeded / 3600)
};
}
// Process idle rewards for multiple users (batch processing)
processBatchIdleRewards(userIds) {
const results = new Map();
for (const userId of userIds) {
try {
const progress = this.calculateOfflineProgress(userId);
results.set(userId, {
success: true,
progress
});
} catch (error) {
results.set(userId, {
success: false,
error: error.message
});
}
}
return results;
}
// Generate online idle rewards (called periodically while player is online)
generateOnlineIdleRewards(userId, deltaTimeMs) {
console.log('[IDLE SYSTEM] generateOnlineIdleRewards called for userId:', userId, 'deltaTimeMs:', deltaTimeMs);
const productionRates = this.playerProductionRates.get(userId);
console.log('[IDLE SYSTEM] Production rates for', userId, ':', productionRates);
if (!productionRates) {
console.log('[IDLE SYSTEM] No production rates found for', userId, '- returning 0 rewards');
return {
credits: 0,
experience: 0,
energy: 0
};
}
const deltaTimeSeconds = deltaTimeMs / 1000;
console.log('[IDLE SYSTEM] Delta time seconds:', deltaTimeSeconds);
console.log('[IDLE SYSTEM] Raw calculation:', productionRates.credits, '*', deltaTimeSeconds, '=', productionRates.credits * deltaTimeSeconds);
const creditsBeforeFloor = productionRates.credits * deltaTimeSeconds;
const rewards = {
credits: Math.floor(creditsBeforeFloor),
experience: Math.floor(productionRates.experience * deltaTimeSeconds),
energy: Math.floor(productionRates.energy * deltaTimeSeconds)
};
console.log('[IDLE SYSTEM] Credits before floor:', creditsBeforeFloor, 'after floor:', rewards.credits);
console.log('[IDLE SYSTEM] Calculated rewards for', userId, ':', rewards);
return rewards;
}
// Reset idle data for a user
resetPlayerData(userId) {
this.playerLastActive.delete(userId);
this.playerProductionRates.delete(userId);
this.playerAchievements.delete(userId);
}
// Get global idle statistics
getGlobalStats() {
const totalPlayers = this.playerLastActive.size;
let totalOfflineTime = 0;
let totalIdleCredits = 0;
let totalIdleExperience = 0;
for (const achievements of this.playerAchievements.values()) {
totalOfflineTime += achievements.totalOfflineTime;
totalIdleCredits += achievements.totalIdleCredits;
totalIdleExperience += achievements.totalIdleExperience;
}
return {
totalPlayers,
totalOfflineTime,
totalIdleCredits,
totalIdleExperience,
averageOfflineTime: totalPlayers > 0 ? totalOfflineTime / totalPlayers : 0
};
}
}
module.exports = IdleSystem;