635 lines
23 KiB
JavaScript
635 lines
23 KiB
JavaScript
/**
|
||
* QUEST SYSTEM v2.0 - Mrtva Dolina
|
||
* Complete quest management with ADHD-friendly dialogue and VFX integration
|
||
*
|
||
* Features:
|
||
* - Main story arc (Zamegljeni Spomini, Anina Sled)
|
||
* - Town economy quests (NPCs)
|
||
* - Defense quests (Wall building, Museum)
|
||
* - Side quests & companion recruitment
|
||
* - Progress tracking & rewards
|
||
* - Dialogue system integration
|
||
* - VFX triggers
|
||
*
|
||
* v2.1 UPDATE: Now loads quests from QuestDataLoader (Quest Manifest v2.0 compatible)
|
||
*/
|
||
|
||
// ❌ DISABLED: ES6 import not supported in browser without module bundler
|
||
// import QuestDataLoader from '../data/QuestDataLoader.js';
|
||
|
||
class QuestSystem {
|
||
constructor(scene) {
|
||
this.scene = scene;
|
||
this.quests = new Map();
|
||
this.activeQuests = [];
|
||
this.completedQuests = [];
|
||
this.questLog = [];
|
||
|
||
this.initializeQuests();
|
||
}
|
||
|
||
initializeQuests() {
|
||
console.log('📜 Loading Quest Manifest v2.0...');
|
||
|
||
// ❌ DISABLED: QuestDataLoader not available without ES6 modules
|
||
// Load all quests from QuestDataLoader
|
||
// const manifestQuests = QuestDataLoader.loadAllQuests();
|
||
// manifestQuests.forEach(quest => {
|
||
// this.registerQuest(quest);
|
||
// });
|
||
// console.log(`✅ Loaded ${manifestQuests.length} quests from manifest!`);
|
||
|
||
// Keep legacy quests for backwards compatibility
|
||
this.registerLegacyQuests();
|
||
}
|
||
|
||
/**
|
||
* Legacy quests for backwards compatibility
|
||
*/
|
||
registerLegacyQuests() {
|
||
// MAIN STORY QUEST 1: Zamegljeni Spomini
|
||
this.registerQuest({
|
||
id: 'zamegljeni_spomini',
|
||
title: 'Zamegljeni Spomini',
|
||
type: 'main_story',
|
||
priority: 5,
|
||
description: 'Kai se zbudi brez spominov. Najdi fotografijo.',
|
||
objectives: [
|
||
{ id: 'find_photo', text: 'Najdi družinsko fotografijo', type: 'item', item: 'family_photo', required: 1, current: 0 },
|
||
{ id: 'unlock_capital', text: 'Odkleni Capital City', type: 'flag', flag: 'capital_unlocked', complete: false }
|
||
],
|
||
rewards: { xp: 500, items: ['family_photo'], unlocks: ['capital_city'] },
|
||
vfx: { start: 'amnesia_blur', complete: 'flashback' },
|
||
dialogue: {
|
||
start: ["Kje sem? Kaj se mi dogaja?", "Ta fotka... Kdo je ta punca?"],
|
||
complete: ["Ana! Pomnim se! To je moja sestra!"]
|
||
},
|
||
npc: null,
|
||
location: 'base_farm'
|
||
});
|
||
|
||
// MAIN STORY QUEST 2: Anina Sled
|
||
this.registerQuest({
|
||
id: 'anина_sled',
|
||
title: 'Anina Sled',
|
||
type: 'main_story',
|
||
priority: 5,
|
||
description: 'Zberi 50 namigov o tem, kje je Ana.',
|
||
objectives: [
|
||
{ id: 'collect_clues', text: 'Zberi Aniне namige (0/50)', type: 'collection', item: 'ana_clue', required: 50, current: 0 }
|
||
],
|
||
rewards: { xp: 5000, items: ['final_location'], unlocks: ['ana_encounter'] },
|
||
vfx: { onClue: 'amnesia_blur', complete: 'revelation' },
|
||
dialogue: {
|
||
onClue: ["Kai, veš da te imam rada?", "Ne skrbi zame..."],
|
||
complete: ["NAŠEL SEM JO! Ana, prihajam!"]
|
||
},
|
||
prerequisites: ['zamegljeni_spomini'],
|
||
npc: null
|
||
});
|
||
|
||
// TOWN QUEST: Šivilja
|
||
this.registerQuest({
|
||
id: 'siviljina_prosnja',
|
||
title: 'Šiviljina Prošnja',
|
||
type: 'town_economy',
|
||
priority: 4,
|
||
description: 'Šivilja rabi 20x Platno.',
|
||
objectives: [
|
||
{ id: 'cloth', text: 'Zberi platno (0/20)', type: 'item', item: 'cloth', required: 20, current: 0 }
|
||
],
|
||
rewards: { xp: 200, items: ['enchanted_jacket'], gold: 500 },
|
||
dialogue: {
|
||
start: ["Hej! Rabim 20 platna. Lahko mi pomagaš?"],
|
||
progress: ["Še {remaining}!"],
|
||
complete: ["SUPER! Na, vzemi ta jopič!"]
|
||
},
|
||
npc: 'sivilja'
|
||
});
|
||
|
||
// TOWN QUEST: Pek
|
||
this.registerQuest({
|
||
id: 'pekov_recept',
|
||
title: 'Pekov Recept',
|
||
type: 'town_economy',
|
||
priority: 4,
|
||
description: 'Pek rabi 10x Pšenico za kruh.',
|
||
objectives: [
|
||
{ id: 'wheat', text: 'Zberi pšenico (0/10)', type: 'item', item: 'wheat', required: 10, current: 0 },
|
||
{ id: 'restore', text: 'Obnovi pekarno', type: 'building', building: 'bakery', complete: false }
|
||
],
|
||
rewards: { xp: 300, items: ['daily_bread'], unlocks: ['bakery_shop'] },
|
||
dialogue: {
|
||
start: ["Živjo! Rabim 10 pšenice, pa še pekarna je zrušena!"],
|
||
complete: ["ZAKON! Zdej lahko spet pečem! Kruh na dan ZASTONJ!"]
|
||
},
|
||
npc: 'pek'
|
||
});
|
||
|
||
// TOWN QUEST: Tehnik
|
||
this.registerQuest({
|
||
id: 'tehnik_generator',
|
||
title: 'Tehnikova Naprava',
|
||
type: 'town_economy',
|
||
priority: 4,
|
||
description: 'Tehnik rabi 5x Električne Komponente.',
|
||
objectives: [
|
||
{ id: 'components', text: 'Zberi komponente (0/5)', type: 'item', item: 'electric_component', required: 5, current: 0 }
|
||
],
|
||
rewards: { xp: 250, items: ['auto_tiller'], unlocks: ['tech_services'] },
|
||
dialogue: {
|
||
start: ["Yo! Rabim 5 električnih komponent za generator."],
|
||
complete: ["NICE! Generator dela! Auto-Tiller upgrade za te!"]
|
||
},
|
||
npc: 'tehnik'
|
||
});
|
||
|
||
// DEFENSE QUEST: Walls
|
||
this.registerQuest({
|
||
id: 'obzidje',
|
||
title: 'Obzidje Mrtve Doline',
|
||
type: 'defense',
|
||
priority: 5,
|
||
description: 'Zgradi 20 segmentov zidov.',
|
||
objectives: [
|
||
{ id: 'walls', text: 'Zgradi zunanje zidove (0/20)', type: 'building', building: 'wall', required: 20, current: 0 }
|
||
],
|
||
rewards: { xp: 400, items: ['wall_blueprint_2'], unlocks: ['raid_event'] },
|
||
vfx: { complete: 'raid_warning' },
|
||
dialogue: {
|
||
start: ["Zombiji prihajajo! Hitr zgradi zidove!"],
|
||
complete: ["DONE! Zidovi so zgoraj! Priprav se na raiderje!"]
|
||
},
|
||
npc: 'mayor',
|
||
triggers: ['day_7']
|
||
});
|
||
|
||
// COLLECTION QUEST: Museum
|
||
this.registerQuest({
|
||
id: 'muzej_hrosci',
|
||
title: 'Muzejski Mejnik',
|
||
type: 'collection',
|
||
priority: 3,
|
||
description: 'Kustos rabi 24 različnih hroščev.',
|
||
objectives: [
|
||
{ id: 'bugs', text: 'Doniraj hrošče (0/24)', type: 'collection', collection: 'museum_bugs', required: 24, current: 0 }
|
||
],
|
||
rewards: { xp: 600, items: ['museum_key'], unlocks: ['museum_stage2'] },
|
||
dialogue: {
|
||
start: ["Potrebujem 24 različnih hroščev za muzejsko zbirko."],
|
||
progress: ["Odlično! Še {remaining}!"],
|
||
complete: ["FANTASTIČNO! Razširim muzej!"]
|
||
},
|
||
npc: 'kustos'
|
||
});
|
||
|
||
// SIDE QUEST: Arborist
|
||
this.registerQuest({
|
||
id: 'arborist_sadike',
|
||
title: 'Arboristova Pomoč',
|
||
type: 'side',
|
||
priority: 2,
|
||
description: 'Arborist rabi 50x sadike dreves.',
|
||
objectives: [
|
||
{ id: 'saplings', text: 'Zberi sadike (0/50)', type: 'item', item: 'tree_sapling', required: 50, current: 0 }
|
||
],
|
||
rewards: { xp: 350, items: ['tree_planter'], unlocks: ['forest_restore'] },
|
||
dialogue: {
|
||
start: ["Rad bi obnovil gozd. Mi lahko pomagaš?"],
|
||
complete: ["SUPER! Drevo-sadersko orodje je zate!"]
|
||
},
|
||
npc: 'arborist'
|
||
});
|
||
|
||
// COMPANION QUEST: Zombie Scout
|
||
this.registerQuest({
|
||
id: 'zombie_scout_join',
|
||
title: 'Zombi Skavt - Rekrutacija',
|
||
type: 'companion',
|
||
priority: 5,
|
||
description: 'Pridobi zaupanje inteligentnega zombija.',
|
||
objectives: [
|
||
{ id: 'feed', text: 'Nahrani zombija (0/3 mesa)', type: 'item_give', item: 'meat', required: 3, current: 0 },
|
||
{ id: 'trust', text: 'Pridobi zaupanje', type: 'interaction', complete: false }
|
||
],
|
||
rewards: { xp: 1000, unlocks: ['zombie_scout_companion'], companions: ['zombie_scout'] },
|
||
vfx: { complete: 'companion_join' },
|
||
dialogue: {
|
||
start: ["*zombie zvoki* ...Meso?"],
|
||
progress: ["*munch* ...Ti dobr človek."],
|
||
complete: ["*zavija* Jaz s teb! Jaz pomagat! [JOINED]"]
|
||
},
|
||
npc: 'zombie_scout',
|
||
location: 'dead_forest'
|
||
});
|
||
|
||
// Legacy basic quest chain for backwards compatibility
|
||
this.registerQuest({
|
||
id: 'q1_start',
|
||
title: 'Survival Basics',
|
||
type: 'tutorial',
|
||
priority: 5,
|
||
description: 'Collect Wood and Stone.',
|
||
objectives: [
|
||
{ id: 'wood', text: 'Collect Wood (0/5)', type: 'item', item: 'wood', required: 5, current: 0 },
|
||
{ id: 'stone', text: 'Collect Stone (0/3)', type: 'item', item: 'stone', required: 3, current: 0 }
|
||
],
|
||
rewards: { gold: 10, xp: 50 },
|
||
nextQuest: 'q2_farm',
|
||
npc: 'villager'
|
||
});
|
||
|
||
this.registerQuest({
|
||
id: 'q2_farm',
|
||
title: 'The Farmer',
|
||
type: 'tutorial',
|
||
priority: 5,
|
||
description: 'Plant some seeds.',
|
||
objectives: [
|
||
{ id: 'plant', text: 'Plant seeds (0/3)', type: 'action', action: 'plant', required: 3, current: 0 }
|
||
],
|
||
rewards: { gold: 20, items: ['wood:10'] },
|
||
prerequisites: ['q1_start'],
|
||
nextQuest: 'q3_defense',
|
||
npc: 'villager'
|
||
});
|
||
|
||
this.registerQuest({
|
||
id: 'q3_defense',
|
||
title: 'Fortification',
|
||
type: 'tutorial',
|
||
priority: 4,
|
||
description: 'Build a Fence.',
|
||
objectives: [
|
||
{ id: 'fence', text: 'Build Fence (0/2)', type: 'action', action: 'build_fence', required: 2, current: 0 }
|
||
],
|
||
rewards: { gold: 50, items: ['sword:1'] },
|
||
prerequisites: ['q2_farm'],
|
||
nextQuest: 'q4_slayer',
|
||
npc: 'merchant'
|
||
});
|
||
|
||
this.registerQuest({
|
||
id: 'q4_slayer',
|
||
title: 'Zombie Slayer',
|
||
type: 'tutorial',
|
||
priority: 4,
|
||
description: 'Kill 3 Zombies.',
|
||
objectives: [
|
||
{ id: 'kill', text: 'Kill Zombies (0/3)', type: 'kill', target: 'zombie', required: 3, current: 0 }
|
||
],
|
||
rewards: { gold: 100, items: ['gold:50'] },
|
||
prerequisites: ['q3_defense'],
|
||
npc: 'villager'
|
||
});
|
||
}
|
||
|
||
registerQuest(questData) {
|
||
// Set defaults
|
||
questData.isActive = false;
|
||
questData.isComplete = false;
|
||
questData.startTime = null;
|
||
|
||
this.quests.set(questData.id, questData);
|
||
}
|
||
|
||
startQuest(questId) {
|
||
const quest = this.quests.get(questId);
|
||
if (!quest) {
|
||
console.error(`Quest ${questId} not found!`);
|
||
return false;
|
||
}
|
||
|
||
// Check prerequisites
|
||
if (quest.prerequisites) {
|
||
for (const prereq of quest.prerequisites) {
|
||
if (!this.isQuestComplete(prereq)) {
|
||
console.log(`Cannot start ${questId}: Missing ${prereq}`);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Prevent duplicate active
|
||
if (this.activeQuests.includes(questId)) {
|
||
return false;
|
||
}
|
||
|
||
quest.isActive = true;
|
||
quest.startTime = Date.now();
|
||
this.activeQuests.push(questId);
|
||
|
||
this.questLog.push({
|
||
questId,
|
||
startTime: quest.startTime,
|
||
status: 'active'
|
||
});
|
||
|
||
// Show start dialogue
|
||
if (quest.dialogue && quest.dialogue.start) {
|
||
this.showDialogue(quest.dialogue.start, quest.npc);
|
||
|
||
// 🔊 VOICEOVER: Play quest start dialogue
|
||
if (this.scene.voiceoverSystem) {
|
||
const voiceKey = `quest_${questId}_start`;
|
||
this.scene.voiceoverSystem.playVoiceover(voiceKey, quest.dialogue.start[0]);
|
||
}
|
||
}
|
||
|
||
// Trigger VFX
|
||
if (quest.vfx && quest.vfx.start) {
|
||
this.scene.events.emit('vfx:trigger', quest.vfx.start);
|
||
}
|
||
|
||
// Show notification
|
||
this.scene.events.emit('show-floating-text', {
|
||
x: this.scene.player.x,
|
||
y: this.scene.player.y - 50,
|
||
text: 'Quest Accepted!',
|
||
color: '#FFFF00'
|
||
});
|
||
|
||
this.scene.events.emit('quest:started', questId);
|
||
this.updateUI();
|
||
return true;
|
||
}
|
||
|
||
updateObjective(questId, objectiveId, progress) {
|
||
const quest = this.quests.get(questId);
|
||
if (!quest || !quest.isActive) return;
|
||
|
||
const objective = quest.objectives.find(obj => obj.id === objectiveId);
|
||
if (!objective) return;
|
||
|
||
// Update progress
|
||
if (objective.type === 'item' || objective.type === 'collection') {
|
||
objective.current = Math.min(progress, objective.required);
|
||
objective.complete = objective.current >= objective.required;
|
||
} else if (objective.type === 'action' || objective.type === 'kill') {
|
||
objective.current = Math.min(progress, objective.required);
|
||
objective.complete = objective.current >= objective.required;
|
||
} else if (objective.type === 'flag' || objective.type === 'interaction' || objective.type === 'building') {
|
||
objective.complete = progress === true;
|
||
}
|
||
|
||
// Check completion
|
||
const allComplete = quest.objectives.every(obj => obj.complete);
|
||
if (allComplete) {
|
||
this.completeQuest(questId);
|
||
} else {
|
||
// Progress dialogue
|
||
if (quest.dialogue && quest.dialogue.progress) {
|
||
const remaining = objective.required - objective.current;
|
||
const msg = quest.dialogue.progress[0].replace('{remaining}', remaining);
|
||
this.showDialogue([msg], quest.npc);
|
||
|
||
// 🔊 VOICEOVER: Play progress dialogue
|
||
if (this.scene.voiceoverSystem) {
|
||
const voiceKey = `quest_${questId}_progress`;
|
||
this.scene.voiceoverSystem.playVoiceover(voiceKey, msg);
|
||
}
|
||
}
|
||
}
|
||
|
||
this.scene.events.emit('quest:updated', questId, objectiveId, progress);
|
||
this.updateUI();
|
||
}
|
||
|
||
trackAction(actionType, amount = 1) {
|
||
// Track actions for active quests' objectives
|
||
for (const questId of this.activeQuests) {
|
||
const quest = this.quests.get(questId);
|
||
if (!quest) continue;
|
||
|
||
for (const obj of quest.objectives) {
|
||
if (obj.complete) continue;
|
||
|
||
if (obj.type === 'action' && obj.action === actionType) {
|
||
obj.current = Math.min((obj.current || 0) + amount, obj.required);
|
||
if (obj.current >= obj.required) {
|
||
obj.complete = true;
|
||
this.scene.events.emit('show-floating-text', {
|
||
x: this.scene.player.x,
|
||
y: this.scene.player.y,
|
||
text: 'Objective Complete!',
|
||
color: '#00FF00'
|
||
});
|
||
}
|
||
} else if (obj.type === 'kill' && obj.target === actionType) {
|
||
obj.current = Math.min((obj.current || 0) + amount, obj.required);
|
||
if (obj.current >= obj.required) {
|
||
obj.complete = true;
|
||
this.scene.events.emit('show-floating-text', {
|
||
x: this.scene.player.x,
|
||
y: this.scene.player.y,
|
||
text: 'Objective Complete!',
|
||
color: '#00FF00'
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
// Auto-complete check
|
||
const allDone = quest.objectives.every(o => o.complete);
|
||
if (allDone) {
|
||
this.completeQuest(questId);
|
||
}
|
||
}
|
||
|
||
this.updateUI();
|
||
}
|
||
|
||
completeQuest(questId) {
|
||
const quest = this.quests.get(questId);
|
||
if (!quest) return;
|
||
|
||
quest.isActive = false;
|
||
quest.isComplete = true;
|
||
this.activeQuests = this.activeQuests.filter(id => id !== questId);
|
||
this.completedQuests.push(questId);
|
||
|
||
// Grant rewards
|
||
if (quest.rewards) {
|
||
if (quest.rewards.xp && this.scene.player && this.scene.player.gainExperience) {
|
||
this.scene.player.gainExperience(quest.rewards.xp);
|
||
}
|
||
if (quest.rewards.gold && this.scene.inventorySystem) {
|
||
this.scene.inventorySystem.addGold(quest.rewards.gold);
|
||
}
|
||
if (quest.rewards.items) {
|
||
quest.rewards.items.forEach(item => {
|
||
const [itemName, amt] = item.includes(':') ? item.split(':') : [item, 1];
|
||
if (this.scene.inventorySystem) {
|
||
this.scene.inventorySystem.addItem(itemName, parseInt(amt));
|
||
} else if (this.scene.player && this.scene.player.inventory) {
|
||
this.scene.player.inventory.addItem(itemName, parseInt(amt));
|
||
}
|
||
});
|
||
}
|
||
if (quest.rewards.unlocks) {
|
||
quest.rewards.unlocks.forEach(unlock => {
|
||
if (this.scene.gameState && this.scene.gameState.unlock) {
|
||
this.scene.gameState.unlock(unlock);
|
||
}
|
||
});
|
||
}
|
||
if (quest.rewards.companions) {
|
||
quest.rewards.companions.forEach(companion => {
|
||
if (this.scene.player && this.scene.player.addCompanion) {
|
||
this.scene.player.addCompanion(companion);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// Completion dialogue
|
||
if (quest.dialogue && quest.dialogue.complete) {
|
||
this.showDialogue(quest.dialogue.complete, quest.npc);
|
||
|
||
// 🔊 VOICEOVER: Play completion dialogue
|
||
if (this.scene.voiceoverSystem) {
|
||
const voiceKey = `quest_${questId}_complete`;
|
||
this.scene.voiceoverSystem.playVoiceover(voiceKey, quest.dialogue.complete[0]);
|
||
}
|
||
}
|
||
|
||
// Completion VFX
|
||
if (quest.vfx && (quest.vfx.complete || quest.vfx.onComplete)) {
|
||
this.scene.events.emit('vfx:trigger', quest.vfx.complete || quest.vfx.onComplete);
|
||
}
|
||
|
||
// Notification
|
||
this.scene.events.emit('show-floating-text', {
|
||
x: this.scene.player.x,
|
||
y: this.scene.player.y - 50,
|
||
text: 'Quest Complete!',
|
||
color: '#00FF00'
|
||
});
|
||
|
||
console.log(`🏆 Quest Complete: ${quest.title}`);
|
||
this.scene.events.emit('quest:completed', questId);
|
||
|
||
// Auto-start next quest if defined
|
||
if (quest.nextQuest) {
|
||
setTimeout(() => {
|
||
console.log(`Next quest available: ${quest.nextQuest}`);
|
||
}, 1000);
|
||
}
|
||
|
||
this.updateUI();
|
||
}
|
||
|
||
showDialogue(lines, npcId) {
|
||
// Emit dialogue event
|
||
this.scene.events.emit('dialogue:show', {
|
||
npc: npcId,
|
||
lines: lines
|
||
});
|
||
}
|
||
|
||
getActiveQuests() {
|
||
return this.activeQuests.map(id => this.quests.get(id));
|
||
}
|
||
|
||
getAvailableQuest(npcType) {
|
||
// Find first uncompleted, non-active quest for this NPC
|
||
for (const [id, quest] of this.quests) {
|
||
if (quest.isComplete || quest.isActive) continue;
|
||
if (quest.npc !== npcType) continue;
|
||
|
||
// Check prerequisites
|
||
if (quest.prerequisites) {
|
||
const prereqsMet = quest.prerequisites.every(p => this.isQuestComplete(p));
|
||
if (!prereqsMet) continue;
|
||
}
|
||
|
||
return quest;
|
||
}
|
||
return null;
|
||
}
|
||
|
||
getQuestProgress(questId) {
|
||
const quest = this.quests.get(questId);
|
||
if (!quest) return null;
|
||
|
||
return {
|
||
id: questId,
|
||
title: quest.title,
|
||
objectives: quest.objectives.map(obj => ({
|
||
text: obj.text,
|
||
current: obj.current || 0,
|
||
required: obj.required || 1,
|
||
complete: obj.complete || false
|
||
})),
|
||
isActive: quest.isActive,
|
||
isComplete: quest.isComplete
|
||
};
|
||
}
|
||
|
||
isQuestActive(questId) {
|
||
const quest = this.quests.get(questId);
|
||
return quest && quest.isActive;
|
||
}
|
||
|
||
isQuestComplete(questId) {
|
||
const quest = this.quests.get(questId);
|
||
return quest && quest.isComplete;
|
||
}
|
||
|
||
update(delta) {
|
||
// Auto-update objectives based on inventory
|
||
if (!this.scene.inventorySystem) return;
|
||
|
||
for (const questId of this.activeQuests) {
|
||
const quest = this.quests.get(questId);
|
||
if (!quest) continue;
|
||
|
||
let changed = false;
|
||
for (const obj of quest.objectives) {
|
||
if (obj.complete) continue;
|
||
|
||
if (obj.type === 'item') {
|
||
const count = this.scene.inventorySystem.getItemCount(obj.item);
|
||
if (count !== obj.current) {
|
||
obj.current = count;
|
||
changed = true;
|
||
if (obj.current >= obj.required) {
|
||
obj.complete = true;
|
||
this.scene.events.emit('show-floating-text', {
|
||
x: this.scene.player.x,
|
||
y: this.scene.player.y,
|
||
text: 'Objective Complete!',
|
||
color: '#00FF00'
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (changed) {
|
||
const allDone = quest.objectives.every(o => o.complete);
|
||
if (allDone) {
|
||
this.completeQuest(questId);
|
||
} else {
|
||
this.updateUI();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
updateUI() {
|
||
const ui = this.scene.scene.get('UIScene');
|
||
if (ui && ui.updateQuestTracker) {
|
||
const active = this.activeQuests.length > 0 ? this.quests.get(this.activeQuests[0]) : null;
|
||
ui.updateQuestTracker(active);
|
||
}
|
||
}
|
||
|
||
destroy() {
|
||
this.quests.clear();
|
||
this.activeQuests = [];
|
||
this.completedQuests = [];
|
||
this.questLog = [];
|
||
}
|
||
}
|