/** * Galaxy Strike Online - Server Dungeon System * Manages dungeon instances, encounters, and rewards */ class DungeonSystem { constructor() { this.dungeons = new Map(); this.instances = new Map(); // instanceId -> dungeon instance this.playerInstances = new Map(); // userId -> instanceId // Initialize room templates this.roomTypes = { entrance: { name: 'Entrance', enemies: 0, rewards: false }, corridor: { name: 'Corridor', enemies: 1, rewards: false }, chamber: { name: 'Chamber', enemies: 2, rewards: true }, treasure: { name: 'Treasure Room', enemies: 0, rewards: true }, boss: { name: 'Boss Room', enemies: 1, rewards: true, isBoss: true }, exit: { name: 'Exit', enemies: 0, rewards: false } }; // Initialize enemy templates this.enemyTemplates = { // Original enemies training_drone: { name: 'Training Drone', health: 10, attack: 5, defense: 2, experience: 5, credits: 3 }, practice_target: { name: 'Practice Target', health: 5, attack: 0, defense: 0, experience: 2, credits: 1 }, alien_guardian: { name: 'Alien Guardian', health: 50, attack: 8, defense: 5, experience: 25, credits: 15 }, ancient_drone: { name: 'Ancient Drone', health: 30, attack: 12, defense: 2, experience: 20, credits: 10 }, crystal_golem: { name: 'Crystal Golem', health: 80, attack: 6, defense: 10, experience: 35, credits: 25 }, space_pirate: { name: 'Space Pirate', health: 25, attack: 10, defense: 3, experience: 15, credits: 12 }, pirate_captain: { name: 'Pirate Captain', health: 40, attack: 15, defense: 6, experience: 30, credits: 20 }, defense_turret: { name: 'Defense Turret', health: 20, attack: 18, defense: 8, experience: 18, credits: 8 }, security_drone: { name: 'Security Drone', health: 35, attack: 14, defense: 4, experience: 22, credits: 15 }, corrupted_ai: { name: 'Corrupted AI', health: 60, attack: 20, defense: 2, experience: 40, credits: 30 }, virus_program: { name: 'Virus Program', health: 15, attack: 25, defense: 1, experience: 20, credits: 12 }, mining_drone: { name: 'Mining Drone', health: 20, attack: 8, defense: 3, experience: 12, credits: 8 }, rock_creature: { name: 'Rock Creature', health: 45, attack: 6, defense: 12, experience: 25, credits: 15 }, explosive_asteroid: { name: 'Explosive Asteroid', health: 10, attack: 30, defense: 0, experience: 15, credits: 5 }, energy_being: { name: 'Energy Being', health: 55, attack: 22, defense: 3, experience: 45, credits: 35 }, phase_shifter: { name: 'Phase Shifter', health: 30, attack: 28, defense: 1, experience: 35, credits: 25 }, quantum_entity: { name: 'Quantum Entity', health: 70, attack: 35, defense: 5, experience: 60, credits: 50 }, // NEW ENEMIES - Space Theme maintenance_drone: { name: 'Maintenance Drone', health: 28, attack: 11, defense: 4, experience: 18, credits: 12 }, security_android: { name: 'Security Android', health: 42, attack: 16, defense: 7, experience: 28, credits: 20 }, station_ai: { name: 'Station AI', health: 65, attack: 24, defense: 3, experience: 45, credits: 32 }, ice_elemental: { name: 'Ice Elemental', health: 38, attack: 18, defense: 8, experience: 32, credits: 24 }, frost_wyrm: { name: 'Frost Wyrm', health: 72, attack: 26, defense: 6, experience: 52, credits: 38 }, gravity_wraith: { name: 'Gravity Wraith', health: 85, attack: 32, defense: 4, experience: 68, credits: 48 }, void_stalker: { name: 'Void Stalker', health: 78, attack: 38, defense: 5, experience: 72, credits: 52 }, singularity_spawn: { name: 'Singularity Spawn', health: 95, attack: 42, defense: 8, experience: 85, credits: 65 }, plasma_elemental: { name: 'Plasma Elemental', health: 58, attack: 28, defense: 4, experience: 48, credits: 35 }, solar_guardian: { name: 'Solar Guardian', health: 82, attack: 34, defense: 7, experience: 65, credits: 48 }, fusion_core: { name: 'Fusion Core', health: 68, attack: 30, defense: 12, experience: 58, credits: 42 }, scrap_golem: { name: 'Scrap Golem', health: 35, attack: 14, defense: 9, experience: 22, credits: 16 }, hull_breacher: { name: 'Hull Breacher', health: 32, attack: 20, defense: 3, experience: 26, credits: 18 }, salage_drone: { name: 'Salvage Drone', health: 22, attack: 12, defense: 5, experience: 16, credits: 11 }, // NEW ENEMIES - Planet Theme plant_beast: { name: 'Plant Beast', health: 48, attack: 15, defense: 8, experience: 35, credits: 26 }, tribal_warrior: { name: 'Tribal Warrior', health: 38, attack: 18, defense: 6, experience: 28, credits: 20 }, jungle_spirit: { name: 'Jungle Spirit', health: 55, attack: 22, defense: 4, experience: 42, credits: 30 }, sand_worm: { name: 'Sand Worm', health: 75, attack: 28, defense: 9, experience: 58, credits: 42 }, mummy_guardian: { name: 'Mummy Guardian', health: 62, attack: 24, defense: 7, experience: 48, credits: 35 }, heat_elemental: { name: 'Heat Elemental', health: 52, attack: 26, defense: 3, experience: 45, credits: 32 }, lava_elemental: { name: 'Lava Elemental', health: 68, attack: 30, defense: 5, experience: 55, credits: 40 }, fire_demon: { name: 'Fire Demon', health: 72, attack: 32, defense: 6, experience: 62, credits: 45 }, magma_beast: { name: 'Magma Beast', health: 85, attack: 28, defense: 12, experience: 68, credits: 50 }, cryo_mutant: { name: 'Cryo Mutant', health: 45, attack: 20, defense: 7, experience: 38, credits: 28 }, frost_android: { name: 'Frost Android', health: 52, attack: 22, defense: 8, experience: 42, credits: 30 }, ice_wraith: { name: 'Ice Wraith', health: 58, attack: 25, defense: 4, experience: 48, credits: 35 }, swamp_beast: { name: 'Swamp Beast', health: 35, attack: 16, defense: 9, experience: 24, credits: 18 }, toxic_spitter: { name: 'Toxic Spitter', health: 28, attack: 19, defense: 3, experience: 22, credits: 16 }, mud_golem: { name: 'Mud Golem', health: 42, attack: 12, defense: 11, experience: 26, credits: 19 }, // NEW ENEMIES - Technology Theme glitch_wraith: { name: 'Glitch Wraith', health: 48, attack: 26, defense: 2, experience: 45, credits: 32 }, firewall_guardian: { name: 'Firewall Guardian', health: 65, attack: 22, defense: 8, experience: 52, credits: 38 }, data_vampire: { name: 'Data Vampire', health: 38, attack: 30, defense: 3, experience: 35, credits: 26 }, assembly_drone: { name: 'Assembly Drone', health: 32, attack: 15, defense: 6, experience: 24, credits: 17 }, welder_bot: { name: 'Welder Bot', health: 28, attack: 20, defense: 4, experience: 22, credits: 15 }, factory_overseer: { name: 'Factory Overseer', health: 58, attack: 24, defense: 7, experience: 46, credits: 33 }, quantum_phantom: { name: 'Quantum Phantom', health: 78, attack: 35, defense: 4, experience: 68, credits: 50 }, particle_accelerator: { name: 'Particle Accelerator', health: 92, attack: 40, defense: 6, experience: 85, credits: 62 }, reality_bender: { name: 'Reality Bender', health: 88, attack: 45, defense: 3, experience: 92, credits: 68 }, sentinel_program: { name: 'Sentinel Program', health: 42, attack: 21, defense: 8, experience: 32, credits: 24 }, data_corruptor: { name: 'Data Corruptor', health: 35, attack: 25, defense: 3, experience: 28, credits: 20 }, system_guardian: { name: 'System Guardian', health: 55, attack: 23, defense: 9, experience: 42, credits: 30 }, // NEW ENEMIES - Biome/Elemental Theme shard_elemental: { name: 'Shard Elemental', health: 45, attack: 19, defense: 10, experience: 38, credits: 28 }, resonance_beast: { name: 'Resonance Beast', health: 52, attack: 22, defense: 6, experience: 42, credits: 30 }, mutant_horror: { name: 'Mutant Horror', health: 68, attack: 28, defense: 5, experience: 58, credits: 42 }, toxic_slime: { name: 'Toxic Slime', health: 42, attack: 18, defense: 8, experience: 32, credits: 24 }, radiation_beast: { name: 'Radiation Beast', health: 75, attack: 30, defense: 4, experience: 65, credits: 48 }, shadow_demon: { name: 'Shadow Demon', health: 72, attack: 34, defense: 3, experience: 68, credits: 50 }, nightmare_stalker: { name: 'Nightmare Stalker', health: 85, attack: 38, defense: 5, experience: 78, credits: 58 }, void_walker: { name: 'Void Walker', health: 92, attack: 42, defense: 7, experience: 88, credits: 65 }, temporal_paradox: { name: 'Temporal Paradox', health: 78, attack: 40, defense: 4, experience: 75, credits: 55 }, future_soldier: { name: 'Future Soldier', health: 65, attack: 32, defense: 9, experience: 58, credits: 42 }, past_guardian: { name: 'Past Guardian', health: 70, attack: 28, defense: 12, experience: 62, credits: 45 }, // NEW ENEMIES - Military/War Theme enemy_soldier: { name: 'Enemy Soldier', health: 45, attack: 20, defense: 7, experience: 35, credits: 26 }, combat_drone: { name: 'Combat Drone', health: 38, attack: 22, defense: 5, experience: 30, credits: 22 }, field_commander: { name: 'Field Commander', health: 62, attack: 28, defense: 9, experience: 52, credits: 38 }, turret_system: { name: 'Turret System', health: 48, attack: 26, defense: 10, experience: 38, credits: 28 }, combat_android: { name: 'Combat Android', health: 55, attack: 24, defense: 8, experience: 45, credits: 33 }, base_commander: { name: 'Base Commander', health: 72, attack: 30, defense: 11, experience: 62, credits: 45 }, weapon_drone: { name: 'Weapon Drone', health: 42, attack: 28, defense: 4, experience: 38, credits: 28 }, test_subject: { name: 'Test Subject', health: 58, attack: 25, defense: 6, experience: 48, credits: 35 }, chief_scientist: { name: 'Chief Scientist', health: 35, attack: 32, defense: 3, experience: 42, credits: 30 }, // NEW ENEMIES - Special/Unique Theme nightmare_creature: { name: 'Nightmare Creature', health: 62, attack: 28, defense: 5, experience: 55, credits: 40 }, dream_guardian: { name: 'Dream Guardian', health: 68, attack: 30, defense: 8, experience: 58, credits: 42 }, subconscious_demon: { name: 'Subconscious Demon', health: 75, attack: 34, defense: 4, experience: 68, credits: 50 }, memory_fragment: { name: 'Memory Fragment', health: 48, attack: 26, defense: 6, experience: 45, credits: 33 }, forgetfulness_demon: { name: 'Forgetfulness Demon', health: 55, attack: 30, defense: 3, experience: 48, credits: 35 }, nostalgia_spirit: { name: 'Nostalgia Spirit', health: 52, attack: 24, defense: 9, experience: 42, credits: 30 }, rift_demon: { name: 'Rift Demon', health: 88, attack: 44, defense: 5, experience: 92, credits: 68 }, dimensional_hunter: { name: 'Dimensional Hunter', health: 95, attack: 48, defense: 8, experience: 105, credits: 78 }, reality_tear: { name: 'Reality Tear', health: 102, attack: 52, defense: 3, experience: 115, credits: 85 } }; this.initializeDungeons(); } initializeDungeons() { // Tutorial Dungeon this.addDungeon('tutorial', { name: 'Tutorial Dungeon', description: 'Learn the basics of dungeon exploration in this guided tutorial', difficulty: 'tutorial', enemyTypes: ['training_drone', 'practice_target'], rewardMultiplier: 0.5, oneTimeOnly: true, healthType: 'player', // Ground mission energyCost: 0, minLevel: 1, maxLevel: 5, maxPlayers: 4, estimatedTime: 15 }); // Space Theme Dungeons this.addDungeon('alien_ruins', { name: 'Alien Ruins', description: 'Ancient alien structures filled with mysterious technology', difficulty: 'medium', enemyTypes: ['alien_guardian', 'ancient_drone', 'crystal_golem'], rewardMultiplier: 1.2, healthType: 'player', // Ground mission energyCost: 20, minLevel: 5, maxLevel: 15, maxPlayers: 4, estimatedTime: 25 }); this.addDungeon('pirate_lair', { name: 'Pirate Lair', description: 'Dangerous pirate hideouts with valuable loot', difficulty: 'easy', enemyTypes: ['space_pirate', 'pirate_captain', 'defense_turret'], rewardMultiplier: 1.0, healthType: 'ship', // Space mission energyCost: 15, minLevel: 3, maxLevel: 10, maxPlayers: 4, estimatedTime: 20 }); this.addDungeon('corrupted_vault', { name: 'Corrupted AI Vault', description: ' malfunctioning AI facilities with corrupted security', difficulty: 'hard', enemyTypes: ['security_drone', 'corrupted_ai', 'virus_program'], rewardMultiplier: 1.5, healthType: 'ship', // Space mission energyCost: 25, minLevel: 10, maxLevel: 20, maxPlayers: 4, estimatedTime: 35 }); this.addDungeon('asteroid_mine', { name: 'Asteroid Mine', description: 'Abandoned mining facilities in asteroid fields', difficulty: 'easy', enemyTypes: ['mining_drone', 'rock_creature', 'explosive_asteroid'], rewardMultiplier: 0.8, healthType: 'ship', // Space mission energyCost: 10, minLevel: 2, maxLevel: 8, maxPlayers: 4, estimatedTime: 18 }); this.addDungeon('nebula_anomaly', { name: 'Nebula Anomaly', description: 'Strange energy anomalies in deep space', difficulty: 'extreme', enemyTypes: ['energy_being', 'phase_shifter', 'quantum_entity'], rewardMultiplier: 2.0, healthType: 'ship', // Space mission energyCost: 30, minLevel: 15, maxLevel: 25, maxPlayers: 4, estimatedTime: 40 }); this.addDungeon('space_station', { name: 'Abandoned Space Station', description: 'A derelict space station floating in the void', difficulty: 'medium', enemyTypes: ['maintenance_drone', 'security_android', 'station_ai'], rewardMultiplier: 1.3, healthType: 'player', energyCost: 22, minLevel: 6, maxLevel: 16, maxPlayers: 4, estimatedTime: 28 }); this.addDungeon('comet_core', { name: 'Comet Core', description: 'The frozen heart of a passing comet', difficulty: 'hard', enemyTypes: ['ice_elemental', 'frost_wyrm', 'crystal_guardian'], rewardMultiplier: 1.6, healthType: 'ship', energyCost: 28, minLevel: 12, maxLevel: 22, maxPlayers: 4, estimatedTime: 32 }); this.addDungeon('black_hole_perimeter', { name: 'Black Hole Perimeter', description: 'Dangerous space near a black hole event horizon', difficulty: 'extreme', enemyTypes: ['gravity_wraith', 'void_stalker', 'singularity_spawn'], rewardMultiplier: 2.5, healthType: 'ship', energyCost: 35, minLevel: 18, maxLevel: 30, maxPlayers: 4, estimatedTime: 45 }); this.addDungeon('star_forge', { name: 'Star Forge', description: 'Ancient facility that harnesses stellar energy', difficulty: 'hard', enemyTypes: ['plasma_elemental', 'solar_guardian', 'fusion_core'], rewardMultiplier: 1.8, healthType: 'ship', energyCost: 30, minLevel: 14, maxLevel: 24, maxPlayers: 4, estimatedTime: 35 }); this.addDungeon('debris_field', { name: 'Ship Debris Field', description: 'Graveyard of destroyed spacecraft', difficulty: 'easy', enemyTypes: ['scrap_golem', 'hull_breacher', 'salage_drone'], rewardMultiplier: 0.9, healthType: 'ship', energyCost: 12, minLevel: 3, maxLevel: 9, maxPlayers: 4, estimatedTime: 20 }); // Planet Theme Dungeons this.addDungeon('jungle_temple', { name: 'Jungle Temple', description: 'Overgrown temple hidden in dense alien jungle', difficulty: 'medium', enemyTypes: ['plant_beast', 'tribal_warrior', 'jungle_spirit'], rewardMultiplier: 1.4, healthType: 'player', energyCost: 25, minLevel: 7, maxLevel: 17, maxPlayers: 4, estimatedTime: 30 }); this.addDungeon('desert_pyramid', { name: 'Desert Pyramid', description: 'Ancient pyramid buried under endless sand dunes', difficulty: 'hard', enemyTypes: ['sand_worm', 'mummy_guardian', 'heat_elemental'], rewardMultiplier: 1.7, healthType: 'player', energyCost: 28, minLevel: 13, maxLevel: 23, maxPlayers: 4, estimatedTime: 33 }); this.addDungeon('volcanic_caverns', { name: 'Volcanic Caverns', description: 'Molten caverns deep within an active volcano', difficulty: 'hard', enemyTypes: ['lava_elemental', 'fire_demon', 'magma_beast'], rewardMultiplier: 1.6, healthType: 'player', energyCost: 26, minLevel: 12, maxLevel: 22, maxPlayers: 4, estimatedTime: 32 }); this.addDungeon('arctic_research', { name: 'Arctic Research Base', description: 'Frozen research facility with failed experiments', difficulty: 'medium', enemyTypes: ['cryo_mutant', 'frost_android', 'ice_wraith'], rewardMultiplier: 1.5, healthType: 'player', energyCost: 24, minLevel: 8, maxLevel: 18, maxPlayers: 4, estimatedTime: 29 }); this.addDungeon('swamp_lair', { name: 'Swamp Lair', description: 'Murky swamp inhabited by strange creatures', difficulty: 'easy', enemyTypes: ['swamp_beast', 'toxic_spitter', 'mud_golem'], rewardMultiplier: 1.1, healthType: 'player', energyCost: 18, minLevel: 4, maxLevel: 12, maxPlayers: 4, estimatedTime: 22 }); // Technology Theme Dungeons this.addDungeon('cyber_realm', { name: 'Cyber Realm', description: 'Virtual reality space corrupted by malware', difficulty: 'hard', enemyTypes: ['glitch_wraith', 'firewall_guardian', 'data_vampire'], rewardMultiplier: 1.9, healthType: 'player', energyCost: 32, minLevel: 16, maxLevel: 26, maxPlayers: 4, estimatedTime: 38 }); this.addDungeon('robot_factory', { name: 'Robot Factory', description: 'Automated factory producing hostile machines', difficulty: 'medium', enemyTypes: ['assembly_drone', 'welder_bot', 'factory_overseer'], rewardMultiplier: 1.4, healthType: 'player', energyCost: 23, minLevel: 9, maxLevel: 19, maxPlayers: 4, estimatedTime: 27 }); this.addDungeon('quantum_lab', { name: 'Quantum Laboratory', description: 'Research facility experimenting with quantum physics', difficulty: 'extreme', enemyTypes: ['quantum_phantom', 'particle_accelerator', 'reality_bender'], rewardMultiplier: 2.3, healthType: 'player', energyCost: 38, minLevel: 20, maxLevel: 30, maxPlayers: 4, estimatedTime: 42 }); this.addDungeon('server_farm', { name: 'Server Farm', description: 'Massive data center with rogue security programs', difficulty: 'medium', enemyTypes: ['sentinel_program', 'data_corruptor', 'system_guardian'], rewardMultiplier: 1.3, healthType: 'player', energyCost: 21, minLevel: 7, maxLevel: 17, maxPlayers: 4, estimatedTime: 25 }); console.log(`[DUNGEON SYSTEM] Initialized ${this.dungeons.size} dungeons`); } setIO(io) { this.io = io; console.log('[DUNGEON SYSTEM] Socket.IO instance set for quest events'); } generateDungeonEncounters(dungeon) { console.log('[DUNGEON SYSTEM] Generating encounters for dungeon:', dungeon.id); const encounters = []; const encounterCount = Math.max(3, Math.floor(dungeon.estimatedTime / 5)); // 1 encounter per ~5 minutes console.log('[DUNGEON SYSTEM] Encounter count:', encounterCount, 'for difficulty:', dungeon.difficulty); // Always start with entrance encounters.push({ type: 'entrance', name: 'Dungeon Entrance', description: 'The entrance to the dungeon', enemies: [], rewards: false }); // Generate random encounters based on dungeon difficulty for (let i = 1; i < encounterCount - 1; i++) { const encounterTypes = ['corridor', 'chamber']; if (i === Math.floor(encounterCount / 2)) { encounterTypes.push('treasure'); // Mid-dungeon treasure room } const type = encounterTypes[Math.floor(Math.random() * encounterTypes.length)]; const template = this.roomTypes[type]; encounters.push({ type, name: template.name, description: `A ${template.name.toLowerCase()} in the dungeon`, enemies: this.generateEnemies(dungeon.enemyTypes, template.enemies), rewards: template.rewards }); } // Always end with boss room (except tutorial) if (dungeon.difficulty !== 'tutorial') { encounters.push({ type: 'boss', name: 'Boss Chamber', description: 'The final chamber with a powerful enemy', enemies: this.generateEnemies(dungeon.enemyTypes, 1, true), rewards: true, isBoss: true }); } else { // Tutorial ends with simple chamber (only practice targets for auto-completion) encounters.push({ type: 'chamber', name: 'Training Room', description: 'The final training room', enemies: this.generateEnemies(['practice_target'], 1), // Only practice targets rewards: true }); } console.log('[DUNGEON SYSTEM] Generated encounters:', encounters.map((e, i) => ({ index: i, type: e.type, name: e.name, enemyCount: e.enemies?.length || 0 }))); return encounters; } generateEnemies(enemyTypes, count, isBoss = false) { console.log('[DUNGEON SYSTEM] Generating enemies:', { enemyTypes, count, isBoss }); console.log('[DUNGEON SYSTEM] Available enemy templates:', Object.keys(this.enemyTemplates)); const enemies = []; for (let i = 0; i < count; i++) { const enemyTypeId = enemyTypes[Math.floor(Math.random() * enemyTypes.length)]; const template = this.enemyTemplates[enemyTypeId]; console.log(`[DUNGEON SYSTEM] Generating enemy ${i}: type=${enemyTypeId}, template=`, template); if (!template) { console.warn(`[DUNGEON SYSTEM] Enemy template not found for type: ${enemyTypeId}`); continue; } // Scale enemy for boss fights const enemy = { ...template }; if (isBoss) { enemy.health *= 3; enemy.attack *= 2; enemy.defense *= 1.5; enemy.experience *= 5; enemy.credits *= 3; enemy.name = `Elite ${enemy.name}`; } enemies.push(enemy); } console.log('[DUNGEON SYSTEM] Generated enemies:', enemies); return enemies; } addDungeon(id, dungeon) { // Generate encounters for this dungeon const encounters = this.generateDungeonEncounters(dungeon); this.dungeons.set(id, { id, ...dungeon, encounters, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() }); } getDungeon(id) { return this.dungeons.get(id); } getAllDungeons() { return Array.from(this.dungeons.values()); } getDungeonsGroupedByDifficulty() { const dungeonsByDifficulty = { tutorial: [], easy: [], medium: [], hard: [], extreme: [] }; this.dungeons.forEach(dungeon => { if (dungeonsByDifficulty[dungeon.difficulty]) { dungeonsByDifficulty[dungeon.difficulty].push(dungeon); } }); return dungeonsByDifficulty; } getRoomTypes() { return this.roomTypes; } getEnemyTemplates() { return this.enemyTemplates; } getEnemyTemplate(enemyId) { return this.enemyTemplates[enemyId]; } getDungeonsByDifficulty(difficulty) { return Array.from(this.dungeons.values()).filter(dungeon => dungeon.difficulty === difficulty); } createInstance(dungeonId, creatorId, playerIds = []) { console.log('[DUNGEON SYSTEM] Creating instance:', { dungeonId, creatorId, playerIds }); const dungeon = this.getDungeon(dungeonId); if (!dungeon) { throw new Error('Dungeon not found'); } console.log('[DUNGEON SYSTEM] Dungeon found:', { id: dungeon.id, encountersCount: dungeon.encounters?.length || 0, encounters: dungeon.encounters?.map((e, i) => ({ index: i, type: e.type, name: e.name })) }); // Validate requirements if (playerIds.length > dungeon.maxPlayers) { throw new Error('Too many players for this dungeon'); } const instanceId = `instance_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; const instance = { id: instanceId, dungeonId, creatorId, players: new Set([creatorId, ...playerIds]), currentEncounter: 0, status: 'active', startedAt: new Date().toISOString(), completedAt: null, progress: {}, rewards: null, deaths: new Map() // userId -> death count }; // Initialize progress tracking dungeon.encounters.forEach((encounter, index) => { instance.progress[index] = { completed: false, attempts: 0, deaths: 0 }; }); this.instances.set(instanceId, instance); // Track player instances for (const playerId of instance.players) { this.playerInstances.set(playerId, instanceId); } return instance; } getInstance(instanceId) { return this.instances.get(instanceId); } getPlayerInstance(userId) { const instanceId = this.playerInstances.get(userId); return instanceId ? this.getInstance(instanceId) : null; } joinInstance(instanceId, userId) { const instance = this.getInstance(instanceId); if (!instance) { throw new Error('Instance not found'); } if (instance.status !== 'active') { throw new Error('Instance is not active'); } const dungeon = this.getDungeon(instance.dungeonId); if (instance.players.size >= dungeon.maxPlayers) { throw new Error('Instance is full'); } // Remove from existing instance if any const existingInstanceId = this.playerInstances.get(userId); if (existingInstanceId) { this.leaveInstance(userId); } // Add to new instance instance.players.add(userId); this.playerInstances.set(userId, instanceId); return instance; } leaveInstance(userId) { const instanceId = this.playerInstances.get(userId); if (!instanceId) { return null; } const instance = this.getInstance(instanceId); if (instance) { instance.players.delete(userId); // If instance is empty, clean it up if (instance.players.size === 0) { this.instances.delete(instanceId); } } this.playerInstances.delete(userId); return instance; } startEncounter(instanceId, userId) { const instance = this.getInstance(instanceId); if (!instance) { throw new Error('Instance not found'); } if (!instance.players.has(userId)) { throw new Error('Player not in instance'); } const dungeon = this.getDungeon(instance.dungeonId); const encounter = dungeon.encounters[instance.currentEncounter]; if (!encounter) { throw new Error('No more encounters'); } return { encounter, encounterIndex: instance.currentEncounter, instance }; } completeEncounter(instanceId, userId, result) { const instance = this.getInstance(instanceId); if (!instance) { throw new Error('Instance not found'); } const dungeon = this.getDungeon(instance.dungeonId); const encounter = dungeon.encounters[instance.currentEncounter]; if (!encounter) { throw new Error('No encounter to complete'); } const progress = instance.progress[instance.currentEncounter]; progress.completed = true; progress.attempts++; if (result.deaths) { progress.deaths += result.deaths; instance.deaths.set(userId, (instance.deaths.get(userId) || 0) + result.deaths); } // Move to next encounter instance.currentEncounter++; // Check if dungeon is complete if (instance.currentEncounter >= dungeon.encounters.length) { return this.completeDungeon(instanceId); } return { success: true, nextEncounter: instance.currentEncounter < dungeon.encounters.length ? dungeon.encounters[instance.currentEncounter] : null, encounterIndex: instance.currentEncounter, instance }; } completeDungeon(instanceId) { const instance = this.getInstance(instanceId); if (!instance) { throw new Error('Instance not found'); } const dungeon = this.getDungeon(instance.dungeonId); // Calculate rewards const rewards = this.calculateRewards(dungeon, instance); // Mark as completed instance.status = 'completed'; instance.completedAt = new Date().toISOString(); instance.rewards = rewards; // Remove players from instance tracking for (const playerId of instance.players) { this.playerInstances.delete(playerId); // Check for quest completion this.checkQuestCompletion(playerId, instance.dungeonId); } return { success: true, dungeon, rewards, instance, encounterIndex: -1, // Dungeon complete isComplete: true }; } // Check if dungeon completion should trigger quests checkQuestCompletion(userId, dungeonId) { // Tutorial dungeon completion should trigger main story progression if (dungeonId === 'tutorial') { console.log('[DUNGEON SYSTEM] Tutorial dungeon completed, triggering quest progression'); // Set player stat for tutorial completion if (this.io) { this.io.emit('player_stat_update', { userId: userId, stat: 'tutorialDungeonCompleted', value: true }); // Emit quest completion event for quest system this.io.emit('quest_completed', { userId: userId, questId: 'main_story_first_dungeon', questType: 'dungeon', dungeonId: dungeonId, rewards: { experience: 50, credits: 25 } }); } } } getPlayerCompletedDungeons(userId) { const instances = Array.from(this.instances.values()).filter(instance => instance.players.has(userId) && instance.status === 'completed' ); return instances.map(instance => instance.dungeonId); } calculateRewards(dungeon, instance) { const rewards = { experience: 0, credits: 0, items: [] }; // Check if dungeon has rewards defined if (!dungeon.rewards) { console.warn('[DUNGEON SYSTEM] No rewards defined for dungeon:', dungeon.id); return rewards; } // Calculate base rewards const expRange = dungeon.rewards.experience; const creditRange = dungeon.rewards.credits; if (!expRange || !creditRange) { console.warn('[DUNGEON SYSTEM] Incomplete rewards defined for dungeon:', dungeon.id); return rewards; } rewards.experience = Math.floor(Math.random() * (expRange.max - expRange.min + 1)) + expRange.min; rewards.credits = Math.floor(Math.random() * (creditRange.max - creditRange.min + 1)) + creditRange.min; // Add death penalty const totalDeaths = Array.from(instance.deaths.values()).reduce((sum, deaths) => sum + deaths, 0); const deathPenalty = Math.floor(totalDeaths * 0.1); // 10% penalty per death rewards.experience = Math.max(0, rewards.experience - deathPenalty); rewards.credits = Math.max(0, rewards.credits - (deathPenalty * 2)); // Add items (chance based on performance) const itemChance = Math.max(0.1, 0.5 - (totalDeaths * 0.1)); // Lower chance with more deaths if (Math.random() < itemChance) { const itemPool = dungeon.rewards.items; if (itemPool && itemPool.length > 0) { rewards.items.push(itemPool[Math.floor(Math.random() * itemPool.length)]); } } return rewards; } getAvailableDungeons(playerLevel, playerCount = 1) { return Array.from(this.dungeons.values()).filter(dungeon => dungeon.minLevel <= playerLevel && dungeon.maxLevel >= playerLevel && dungeon.maxPlayers >= playerCount ); } moveToNextRoom(instanceId, userId) { console.log('[DUNGEON SYSTEM] moveToNextRoom called:', { instanceId, userId }); const instance = this.getInstance(instanceId); if (!instance) { throw new Error('Instance not found'); } if (!instance.players.has(userId)) { throw new Error('Player not in instance'); } const dungeon = this.getDungeon(instance.dungeonId); console.log('[DUNGEON SYSTEM] Current state:', { currentEncounter: instance.currentEncounter, totalEncounters: dungeon.encounters.length, dungeonId: dungeon.id }); // Move to next encounter instance.currentEncounter++; console.log('[DUNGEON SYSTEM] After increment:', { newEncounter: instance.currentEncounter, isComplete: instance.currentEncounter >= dungeon.encounters.length }); // Check if dungeon is complete if (instance.currentEncounter >= dungeon.encounters.length) { return this.completeDungeon(instanceId); } // Get next encounter const encounter = dungeon.encounters[instance.currentEncounter]; console.log('[DUNGEON SYSTEM] Next encounter:', { encounterIndex: instance.currentEncounter, encounterType: encounter.type, encounterName: encounter.name, enemies: encounter.enemies }); return { success: true, encounter, encounterIndex: instance.currentEncounter, instance, isComplete: false }; } getDungeonStatistics(userId) { const instances = Array.from(this.instances.values()).filter(instance => instance.players.has(userId) ); const completed = instances.filter(instance => instance.status === 'completed'); const active = instances.filter(instance => instance.status === 'active'); return { totalDungeons: instances.length, completedDungeons: completed.length, activeDungeons: active.length, totalDeaths: Array.from(this.playerInstances.values()).length, recentCompletions: completed.slice(-5).map(instance => ({ dungeonId: instance.dungeonId, completedAt: instance.completedAt, rewards: instance.rewards })) }; } cleanupExpiredInstances() { const now = Date.now(); const expiredInstances = []; for (const [instanceId, instance] of this.instances) { const age = now - new Date(instance.startedAt).getTime(); const maxAge = 2 * 60 * 60 * 1000; // 2 hours if (age > maxAge && instance.status === 'active') { expiredInstances.push(instanceId); } } // Clean up expired instances for (const instanceId of expiredInstances) { const instance = this.instances.get(instanceId); if (instance) { for (const playerId of instance.players) { this.playerInstances.delete(playerId); } this.instances.delete(instanceId); } } return expiredInstances.length; } } module.exports = DungeonSystem;