/** * Galaxy Strike Online - Server Quest System * Manages quests, progress tracking, and rewards */ class QuestSystem { constructor() { this.quests = new Map(); this.playerQuests = new Map(); // userId -> quest data this.initializeQuests(); } initializeQuests() { // Tutorial quests this.addQuest('tutorial_first_battle', { name: 'First Battle', description: 'Complete your first battle to learn the basics', type: 'tutorial', difficulty: 'easy', requirements: { battlesWon: 1 }, rewards: { experience: 100, credits: 500, items: ['health_kit', 'energy_pack'] }, prerequisites: [], repeatable: false, timeLimit: null }); // Combat quests this.addQuest('warrior_path', { name: 'Warrior Path', description: 'Win 10 battles to prove your combat skills', type: 'combat', difficulty: 'medium', requirements: { battlesWon: 10, enemiesDefeated: 50 }, rewards: { experience: 500, credits: 2000, items: ['basic_armor'] }, prerequisites: ['tutorial_first_battle'], repeatable: false, timeLimit: null }); // Crafting quests this.addQuest('novice_crafter', { name: 'Novice Crafter', description: 'Craft 5 items to learn the basics of crafting', type: 'crafting', difficulty: 'easy', requirements: { itemsCrafted: 5 }, rewards: { experience: 200, credits: 1000, craftingExperience: 100 }, prerequisites: [], repeatable: false, timeLimit: null }); // Exploration quests this.addQuest('explorer_spirit', { name: 'Explorer Spirit', description: 'Discover 5 different locations in the galaxy', type: 'exploration', difficulty: 'medium', requirements: { locationsDiscovered: 5 }, rewards: { experience: 300, credits: 1500, items: ['navigation_device'] }, prerequisites: ['tutorial_first_battle'], repeatable: false, timeLimit: null }); // Daily quests this.addQuest('daily_bounty', { name: 'Daily Bounty', description: 'Complete 3 battles today', type: 'daily', difficulty: 'easy', requirements: { battlesWon: 3 }, rewards: { experience: 150, credits: 750 }, prerequisites: [], repeatable: true, timeLimit: 86400000 // 24 hours }); // Weekly quests this.addQuest('weekly_champion', { name: 'Weekly Champion', description: 'Win 20 battles this week', type: 'weekly', difficulty: 'hard', requirements: { battlesWon: 20 }, rewards: { experience: 1000, credits: 5000, items: ['rare_weapon_part'] }, prerequisites: [], repeatable: true, timeLimit: 604800000 // 7 days }); } addQuest(id, quest) { this.quests.set(id, { id, ...quest, createdAt: new Date().toISOString() }); } getQuest(id) { return this.quests.get(id); } getAllQuests() { return Array.from(this.quests.values()); } getQuestsByType(type) { return Array.from(this.quests.values()).filter(quest => quest.type === type); } getQuestsByDifficulty(difficulty) { return Array.from(this.quests.values()).filter(quest => quest.difficulty === difficulty); } initializePlayerData(userId) { if (!this.playerQuests.has(userId)) { this.playerQuests.set(userId, { activeQuests: new Map(), completedQuests: new Map(), questHistory: [], totalQuestsCompleted: 0, dailyQuestsCompleted: 0, weeklyQuestsCompleted: 0 }); } return this.playerQuests.get(userId); } getPlayerData(userId) { return this.playerQuests.get(userId) || this.initializePlayerData(userId); } getAvailableQuests(userId) { const playerData = this.getPlayerData(userId); const availableQuests = []; for (const [questId, quest] of this.quests) { // Skip if already active or completed (for non-repeatable quests) if (playerData.activeQuests.has(questId)) continue; if (playerData.completedQuests.has(questId) && !quest.repeatable) continue; // Check prerequisites const prerequisitesMet = quest.prerequisites.every(prereqId => playerData.completedQuests.has(prereqId) ); if (prerequisitesMet) { availableQuests.push({ ...quest, status: playerData.activeQuests.has(questId) ? 'active' : 'available' }); } } return availableQuests; } startQuest(userId, questId) { const quest = this.getQuest(questId); if (!quest) { throw new Error('Quest not found'); } const playerData = this.getPlayerData(userId); // Check if quest is already active if (playerData.activeQuests.has(questId)) { throw new Error('Quest already active'); } // Check if quest is completed and not repeatable if (playerData.completedQuests.has(questId) && !quest.repeatable) { throw new Error('Quest already completed'); } // Check prerequisites const prerequisitesMet = quest.prerequisites.every(prereqId => playerData.completedQuests.has(prereqId) ); if (!prerequisitesMet) { throw new Error('Prerequisites not met'); } // Start the quest const questProgress = { questId, startedAt: new Date().toISOString(), progress: {}, completed: false, expiredAt: quest.timeLimit ? new Date(Date.now() + quest.timeLimit).toISOString() : null }; // Initialize progress tracking for (const requirement of Object.keys(quest.requirements)) { questProgress.progress[requirement] = 0; } playerData.activeQuests.set(questId, questProgress); return { success: true, quest, progress: questProgress }; } updateQuestProgress(userId, questId, progressUpdates) { const playerData = this.getPlayerData(userId); const activeQuest = playerData.activeQuests.get(questId); if (!activeQuest) { throw new Error('Quest not active'); } // Check if quest is expired if (activeQuest.expiredAt && new Date() > new Date(activeQuest.expiredAt)) { playerData.activeQuests.delete(questId); throw new Error('Quest expired'); } const quest = this.getQuest(questId); let updated = false; // Update progress for (const [requirement, amount] of Object.entries(progressUpdates)) { if (quest.requirements[requirement] !== undefined) { activeQuest.progress[requirement] = Math.min( activeQuest.progress[requirement] + amount, quest.requirements[requirement] ); updated = true; } } // Check if quest is completed const isCompleted = Object.entries(quest.requirements).every( ([requirement, requiredAmount]) => activeQuest.progress[requirement] >= requiredAmount ); if (isCompleted && !activeQuest.completed) { activeQuest.completed = true; activeQuest.completedAt = new Date().toISOString(); return { success: true, questCompleted: true, quest, progress: activeQuest.progress }; } return { success: true, questCompleted: false, quest, progress: activeQuest.progress, updated }; } completeQuest(userId, questId) { const playerData = this.getPlayerData(userId); const activeQuest = playerData.activeQuests.get(questId); if (!activeQuest) { throw new Error('Quest not active'); } if (!activeQuest.completed) { throw new Error('Quest requirements not met'); } const quest = this.getQuest(questId); // Move to completed quests playerData.completedQuests.set(questId, { ...activeQuest, quest, completedAt: new Date().toISOString() }); // Remove from active quests playerData.activeQuests.delete(questId); // Update statistics playerData.totalQuestsCompleted += 1; if (quest.type === 'daily') { playerData.dailyQuestsCompleted += 1; } else if (quest.type === 'weekly') { playerData.weeklyQuestsCompleted += 1; } // Add to history playerData.questHistory.push({ questId, questName: quest.name, completedAt: new Date().toISOString(), rewards: quest.rewards }); // Keep only last 100 quest records if (playerData.questHistory.length > 100) { playerData.questHistory = playerData.questHistory.slice(-100); } return { success: true, quest, rewards: quest.rewards, statistics: { totalCompleted: playerData.totalQuestsCompleted, dailyCompleted: playerData.dailyQuestsCompleted, weeklyCompleted: playerData.weeklyQuestsCompleted } }; } getPlayerActiveQuests(userId) { const playerData = this.getPlayerData(userId); const activeQuests = []; for (const [questId, progress] of playerData.activeQuests) { const quest = this.getQuest(questId); activeQuests.push({ ...quest, progress: progress.progress, startedAt: progress.startedAt, expiredAt: progress.expiredAt }); } return activeQuests; } getPlayerCompletedQuests(userId) { const playerData = this.getPlayerData(userId); const completedQuests = []; for (const [questId, completion] of playerData.completedQuests) { completedQuests.push({ ...completion.quest, completedAt: completion.completedAt, progress: completion.progress }); } return completedQuests; } getQuestStatistics(userId) { const playerData = this.getPlayerData(userId); const availableQuests = this.getAvailableQuests(userId); return { activeQuests: playerData.activeQuests.size, completedQuests: playerData.completedQuests.size, availableQuests: availableQuests.length, totalCompleted: playerData.totalQuestsCompleted, dailyCompleted: playerData.dailyQuestsCompleted, weeklyCompleted: playerData.weeklyQuestsCompleted, recentHistory: playerData.questHistory.slice(-10) }; } resetDailyQuests(userId) { const playerData = this.getPlayerData(userId); // Remove completed daily quests for (const [questId, quest] of this.quests) { if (quest.type === 'daily') { playerData.completedQuests.delete(questId); playerData.activeQuests.delete(questId); } } playerData.dailyQuestsCompleted = 0; return { success: true, message: 'Daily quests reset' }; } resetWeeklyQuests(userId) { const playerData = this.getPlayerData(userId); // Remove completed weekly quests for (const [questId, quest] of this.quests) { if (quest.type === 'weekly') { playerData.completedQuests.delete(questId); playerData.activeQuests.delete(questId); } } playerData.weeklyQuestsCompleted = 0; return { success: true, message: 'Weekly quests reset' }; } } module.exports = QuestSystem;