/** * QuestSystemExpanded.js * ======================= * KRVAVA Ε½ETEV - Enhanced Quest System for Main Campaign * * Extends the original QuestSystem to support: * - Act-based quest structure * - Location-based objectives * - Dialogue integration * - Event triggers * - Quest chains * - Bond strength rewards * * @author NovaFarma Team * @date 2025-12-23 */ class QuestSystemExpanded { constructor(scene) { this.scene = scene; // Load Act 1 Quest Data this.questDB = typeof Act1QuestData !== 'undefined' ? Act1QuestData : {}; // Quest state this.activeQuests = new Map(); // questId -> quest instance this.completedQuests = new Set(); this.questHistory = []; // Current main quest this.mainQuest = null; // UI reference this.questUI = null; console.log('πŸ“œ QuestSystemExpanded initialized'); console.log(`πŸ“š Loaded ${Object.keys(this.questDB).length} quests`); } /** * Start a quest */ startQuest(questId) { if (this.completedQuests.has(questId)) { console.log(`⚠️ Quest already completed: ${questId}`); return false; } if (this.activeQuests.has(questId)) { console.log(`⚠️ Quest already active: ${questId}`); return false; } const questTemplate = this.questDB[questId]; if (!questTemplate) { console.error(`❌ Quest not found: ${questId}`); return false; } // Create quest instance (deep copy) const quest = JSON.parse(JSON.stringify(questTemplate)); quest.startTime = Date.now(); quest.status = 'active'; // Add to active quests this.activeQuests.set(questId, quest); // If main quest, set as current if (quest.isMainQuest) { this.mainQuest = quest; } // Show start dialogue if (quest.startDialogue) { this.showQuestDialogue(quest.startDialogue); } // Trigger any start events if (quest.triggerEvent) { this.triggerQuestEvent(quest.triggerEvent); } // Notification this.showQuestNotification({ title: 'New Quest!', questTitle: quest.title, description: quest.description, icon: quest.isMainQuest ? 'πŸ“–' : 'πŸ“' }); // Update UI this.updateUI(); console.log(`πŸ“œ Quest Started: ${quest.title} (${questId})`); // Emit event this.scene.events.emit('questStarted', { quest }); return true; } /** * Complete a quest objective */ completeObjective(questId, objectiveId) { const quest = this.activeQuests.get(questId); if (!quest) { console.warn(`Quest not active: ${questId}`); return false; } const objective = quest.objectives.find(obj => obj.id === objectiveId); if (!objective) { console.error(`Objective not found: ${objectiveId}`); return false; } if (objective.completed) { console.log(`Objective already complete: ${objectiveId}`); return false; } objective.completed = true; objective.completedTime = Date.now(); console.log(`βœ… Objective Complete: ${objective.description}`); // Show notification this.showObjectiveNotification(objective.description); // Check if all objectives complete const allComplete = quest.objectives.every(obj => obj.completed); if (allComplete) { this.completeQuest(questId); } else { // Update UI this.updateUI(); } return true; } /** * Update objective progress */ updateObjectiveProgress(questId, objectiveId, amount) { const quest = this.activeQuests.get(questId); if (!quest) return false; const objective = quest.objectives.find(obj => obj.id === objectiveId); if (!objective) return false; objective.current = Math.min(objective.required, objective.current + amount); // Check if objective complete if (objective.current >= objective.required && !objective.completed) { this.completeObjective(questId, objectiveId); } else { this.updateUI(); } return true; } /** * Complete a quest */ completeQuest(questId) { const quest = this.activeQuests.get(questId); if (!quest) { console.error(`Quest not active: ${questId}`); return false; } // Mark as completed quest.status = 'completed'; quest.completeTime = Date.now(); // Show completion dialogue if (quest.completeDialogue) { this.showQuestDialogue(quest.completeDialogue); } // Grant rewards if (quest.rewards) { this.grantRewards(quest.rewards); } // Move to completed this.activeQuests.delete(questId); this.completedQuests.add(questId); this.questHistory.push(quest); // Notification this.showQuestNotification({ title: 'Quest Complete!', questTitle: quest.title, description: 'Rewards granted', icon: 'πŸ†', color: '#FFD700' }); // Start next quest if exists if (quest.nextQuest) { setTimeout(() => { this.startQuest(quest.nextQuest); }, 2000); // 2 second delay } // If main quest, clear it if (quest.isMainQuest) { this.mainQuest = null; } console.log(`πŸ† Quest Complete: ${quest.title}`); // Emit event this.scene.events.emit('questCompleted', { quest }); // Update UI this.updateUI(); return true; } /** * Grant quest rewards */ grantRewards(rewards) { // XP if (rewards.xp && this.scene.player) { this.scene.player.addXP?.(rewards.xp); console.log(`πŸ’« Gained ${rewards.xp} XP`); } // Items if (rewards.items && this.scene.inventorySystem) { rewards.items.forEach(item => { this.scene.inventorySystem.addItem(item.id, item.amount); console.log(`πŸ“¦ Received ${item.amount}x ${item.id}`); }); } // Bond Strength if (rewards.bondStrength && this.scene.twinBondSystem) { this.scene.twinBondSystem.changeBondStrength(rewards.bondStrength); console.log(`πŸ’ž Bond Strength ${rewards.bondStrength > 0 ? '+' : ''}${rewards.bondStrength}`); } // Unlocks if (rewards.unlocks) { rewards.unlocks.forEach(unlock => { this.unlockFeature(unlock); }); } } /** * Unlock feature/system */ unlockFeature(featureId) { console.log(`πŸ”“ Unlocked: ${featureId}`); switch (featureId) { case 'twin_bond_ui': if (this.scene.twinBondSystem) { this.scene.twinBondSystem.createBondUI(); } break; case 'zombie_commands': // Already available through ZombieSystem break; case 'grave_crafting': if (this.scene.recipeSystem) { this.scene.recipeSystem.unlockRecipe('grave'); } break; case 'act_2': console.log('🎬 ACT 2 UNLOCKED!'); // TODO: Transition to Act 2 break; } this.scene.events.emit('featureUnlocked', { featureId }); } /** * Trigger quest event */ triggerQuestEvent(event) { if (event.type === 'twin_bond_message' && this.scene.twinBondSystem) { setTimeout(() => { this.scene.twinBondSystem.showTelepathicMessage( event.message, event.emotion ); }, event.delay || 0); } } /** * Show quest dialogue */ showQuestDialogue(dialogue) { // Simple text display for now // TODO: Integrate with DialogueSystem console.log(`πŸ’¬ ${dialogue.speaker}: ${dialogue.text}`); } /** * Show quest notification */ showQuestNotification(notification) { const ui = this.scene.scene.get('UIScene'); if (ui && ui.showNotification) { ui.showNotification(notification); } else { // Fallback console.log(`πŸ“’ ${notification.icon} ${notification.title}: ${notification.questTitle}`); } } /** * Show objective notification */ showObjectiveNotification(description) { const ui = this.scene.scene.get('UIScene'); if (ui && ui.showFloatingText) { ui.showFloatingText({ x: this.scene.player?.x || 400, y: this.scene.player?.y - 50 || 300, text: `βœ“ ${description}`, color: '#00FF00' }); } } /** * Check location objective */ checkLocationObjective(questId, objectiveId, playerX, playerY) { const quest = this.activeQuests.get(questId); if (!quest) return false; const objective = quest.objectives.find(obj => obj.id === objectiveId); if (!objective || objective.type !== 'location') return false; const distance = Phaser.Math.Distance.Between( playerX, playerY, objective.target.x, objective.target.y ); if (distance <= objective.target.radius) { this.completeObjective(questId, objectiveId); return true; } return false; } /** * Update (check objectives) */ update(delta) { // Check location objectives for active quests if (this.scene.player) { const playerX = this.scene.player.x; const playerY = this.scene.player.y; this.activeQuests.forEach((quest, questId) => { quest.objectives.forEach(objective => { if (objective.completed) return; if (objective.type === 'location') { this.checkLocationObjective(questId, objective.id, playerX, playerY); } }); }); } } /** * Update UI */ updateUI() { const ui = this.scene.scene.get('UIScene'); if (ui && ui.updateQuestTracker) { ui.updateQuestTracker(this.mainQuest); } } /** * Getters */ isQuestActive(questId) { return this.activeQuests.has(questId); } isQuestComplete(questId) { return this.completedQuests.has(questId); } getActiveQuests() { return Array.from(this.activeQuests.values()); } getMainQuest() { return this.mainQuest; } getQuestProgress(questId) { const quest = this.activeQuests.get(questId); if (!quest) return null; const total = quest.objectives.length; const completed = quest.objectives.filter(obj => obj.completed).length; return { total, completed, percentage: Math.round((completed / total) * 100) }; } }