User prompt
her biir zindan arasında en az 300 birim boşluk olsun
User prompt
her bir zindan birbirinden en az 300 birim uzak olsun
User prompt
zindanlar random dağılırken ana karakterin ilk durduğu yerden 300 er birim uzakta olsun en yakın zindan ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
dungeonlar haritaya dağılırken birbirilerine uzak olsunlar
User prompt
dungeonların arasındaki mesafeler x ve y kordinatları bakımından en az 200 birim uzakta olacak şekilde random dağılsınlar
User prompt
haritalardaki 10 dungeonun kordinatlarını haritaya random dağıt
Code edit (1 edits merged)
Please save this source code
User prompt
oklar tıkladıkça atsın ve her zaman gelen en yakın düşmanı hedef olarak bulsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ana karakterimiz yaratıklara ok atsın mağaraya girdikten sonra oyun yan ekran formatında olacak yani side-screen mario oyunu gibi ana karakter sabit duracak yandan gelen düşmanlara ok atacak soldan sağa hafif yukarı eğimli oklar ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
mağara içlerinde oyun side-scrolling formatında olsun ana karakterimiz sabit yaratıklara doğru eğimli giden oklar atsın düzenli bir hızda ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
karakteri hangi mağaraya girmek istiyorsak sürükleyip üstüne bırakalım
User prompt
mağaraların tipi aynı olsun zorluk dereceleri ütünde yazın ve oyun alanına gelişi güzel dağıtılsın
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'x')' in or related to this line: 'var localPos = backButton.toLocal(obj.position);' Line Number: 387
Code edit (1 edits merged)
Please save this source code
User prompt
Dungeon Conquest: Epic Loot Adventure
Initial prompt
🎯 Prompt: I want to create a 2D isometric dungeon adventure game consisting of 10 maps, and each map will have 10 dungeons. Each dungeon has a difficulty level: 4 Easy dungeons 3 Normal dungeons 2 Hard dungeons 1 Very Hard dungeon 🧩 Game Flow: On the main map screen, the player sees a character in the center. The player can drag the character with the mouse and drop it onto a dungeon to enter it. Once a dungeon is entered, the screen switches to a horizontal side-view gameplay area, similar to a Mario-style stage: The player stands on the left, fixed in place. Enemies approach from the right side of the screen. The player automatically shoots arrows at the enemies. 👾 Enemies: Each dungeon's difficulty determines enemy health points (HP). Harder dungeons = stronger enemies. Enemies spawn one after another. 📈 Leveling System (EP / Experience): Every defeated enemy grants the player EP (experience points). Player levels up after clearing dungeons depending on difficulty: Easy dungeon = +1 Level Normal dungeon = +2 Levels Hard dungeon = +3 Levels Very Hard dungeon = +4 Levels With every level up, the player's damage and health increase slightly. 🛡️ Loot and Gear: Defeated enemies may drop equipment, such as: Helmet, boots, armor, bow, necklace, ring, gloves. Items have rarity tiers: Common (gray), Rare (blue), Epic (purple), Legendary (gold). Each rarity tier grants better stats like: Bonus damage Extra HP More defense Drop rates vary by dungeon difficulty: Easy: Low drop chance, mostly common items Very Hard: Higher drop chance, chance for rare and legendary items Legendary (gold) items are very rare even in hardest dungeons 🎒 Inventory System: There should be a character inventory system where: Players can see their equipped items Stats change based on current gear Equipment visuals or icons may be shown 📌 Summary of Key Gameplay Requirements: Isometric main map with 10 maps × 10 dungeons Character is dragged onto dungeons to enter them Inside dungeon: side-scroll gameplay like Mario Character auto-fires arrows; enemies come from right EP and leveling based on dungeon difficulty Enemies drop gear of different rarity tiers Stronger gear = better stats Inventory system to manage gear
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Arrow = Container.expand(function () { var self = Container.call(this); var arrowGraphics = self.attachAsset('arrow', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.velocityX = 1; // Default rightward direction self.velocityY = 0; self.target = null; // For homing behavior self.isHoming = false; // Flag to enable homing self.update = function () { if (self.isHoming && self.target && self.target.parent) { // Homing behavior for overworld combat var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.velocityX = dx / distance * self.speed; self.velocityY = dy / distance * self.speed; } } // Move arrow self.x += self.velocityX; self.y += self.velocityY; // Rotate arrow to face direction of movement arrowGraphics.rotation = Math.atan2(self.velocityY, self.velocityX); }; return self; }); var Character = Container.expand(function () { var self = Container.call(this); var characterGraphics = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }); // Character stats self.level = 1; self.experience = 0; self.health = 100; self.maxHealth = 100; self.damage = 12; self.defense = 0; // Equipment slots self.equipment = { helmet: null, boots: null, armor: null, bow: null, necklace: null, ring: null, gloves: null }; self.calculateStats = function () { self.maxHealth = 100 + self.level * 10; self.damage = 12 + self.level * 2; self.defense = 0 + self.level * 1; // Add equipment bonuses for (var slot in self.equipment) { if (self.equipment[slot]) { var item = self.equipment[slot]; self.maxHealth += item.healthBonus || 0; self.damage += item.damageBonus || 0; self.defense += item.defenseBonus || 0; } } if (self.health > self.maxHealth) { self.health = self.maxHealth; } // Update health bar if it exists if (typeof updateHealthBar === 'function') { updateHealthBar(); } }; self.gainExperience = function (amount) { self.experience += amount; var levelUpThreshold = self.level * 100; if (self.experience >= levelUpThreshold) { self.level++; self.experience -= levelUpThreshold; self.calculateStats(); self.health = self.maxHealth; LK.getSound('levelUp').play(); } // Update level bar if in combat if (currentState === 'combat') { updateLevelBar(); } }; return self; }); var Dungeon = Container.expand(function (difficulty, index) { var self = Container.call(this); // Select dungeon asset based on current map var dungeonAsset = 'dungeon'; // default for map 1 if (currentMap === 2) { dungeonAsset = 'dungeon2'; } else if (currentMap === 3) { dungeonAsset = 'dungeon3'; } else if (currentMap === 4) { dungeonAsset = 'dungeon4'; } var dungeonGraphics = self.attachAsset(dungeonAsset, { anchorX: 0.5, anchorY: 0.5 }); self.difficulty = difficulty; self.index = index; self.isHovered = false; // Difficulty colors var colors = { easy: 0x90EE90, normal: 0xFFD700, hard: 0xFF8C00, veryHard: 0xFF4500 }; dungeonGraphics.tint = colors[difficulty]; // Add difficulty text var difficultyNames = { easy: 'EASY', normal: 'NORMAL', hard: 'HARD', veryHard: 'BOSS' }; var difficultyText = new Text2(difficultyNames[difficulty], { size: 50, fill: 0xFFFFFF }); difficultyText.anchor.set(0.5, 1); difficultyText.x = 0; difficultyText.y = -80; self.addChild(difficultyText); self.down = function (x, y, obj) { // Don't allow dungeon interaction if NPC dialogue is active or dungeons are disabled if (dialogueActive || dungeonsDisabled) { return; } // Track entry order for easy, normal and hard dungeons if (difficulty === 'easy' || difficulty === 'normal' || difficulty === 'hard') { var orderKey = 'map' + currentMap + '_' + difficulty + '_order'; // Ensure gameData exists and is an object if (!gameData || _typeof(gameData) !== 'object') { gameData = {}; } // Ensure gameData.dungeonEntryOrder exists as an object if (!gameData.dungeonEntryOrder || _typeof(gameData.dungeonEntryOrder) !== 'object') { gameData.dungeonEntryOrder = {}; } // Ensure the specific order key exists as an array - with additional safety checks try { if (!gameData.dungeonEntryOrder[orderKey] || !Array.isArray(gameData.dungeonEntryOrder[orderKey])) { gameData.dungeonEntryOrder[orderKey] = []; } // Add this dungeon index to entry order if not already present if (gameData.dungeonEntryOrder[orderKey].indexOf(index) === -1) { gameData.dungeonEntryOrder[orderKey].push(index); } } catch (e) { // Fallback: reinitialize dungeonEntryOrder if there's any error gameData.dungeonEntryOrder = {}; gameData.dungeonEntryOrder[orderKey] = [index]; } } currentState = 'combat'; currentDungeon = self; setupCombat(); }; return self; }); var Enemy = Container.expand(function (difficulty, enemyType) { var self = Container.call(this); // Select appropriate asset based on enemy type and current map var assetName = 'enemyBasic'; // default if (enemyType === 'basic') { if (currentMap === 1) { assetName = 'enemyBasic'; } else if (currentMap === 2) { assetName = 'enemyb2'; } else if (currentMap === 3) { assetName = 'enemyb3'; } else if (currentMap === 4) { assetName = 'enemyb4'; } } else if (enemyType === 'fast') { if (currentMap === 1) { assetName = 'enemyFast'; } else if (currentMap === 2) { assetName = 'enemyf2'; } else if (currentMap === 3) { assetName = 'enemyf3'; } else if (currentMap === 4) { assetName = 'enemyf4'; } } else if (enemyType === 'tank') { if (currentMap === 1) { assetName = 'enemyTank'; } else if (currentMap === 2) { assetName = 'enemyt2'; } else if (currentMap === 3) { assetName = 'enemyt3'; } else if (currentMap === 4) { assetName = 'enemyt4'; } } else if (enemyType === 'archer') { assetName = 'enemyArcher'; } else if (enemyType === 'boss') { if (currentMap === 1) { assetName = 'enemyBoss'; } else if (currentMap === 2) { assetName = 'enemys2'; } else if (currentMap === 3) { assetName = 'enemys3'; } else if (currentMap === 4) { assetName = 'enemys4'; } } var enemyGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.enemyType = enemyType || 'basic'; self.difficulty = difficulty; // Base stats for different enemy types var enemyStats = { basic: { health: 50, speed: 1.6, tint: 0xFFFFFF, experience: 10, damage: 20 }, fast: { health: 30, speed: 2.4, tint: 0x00FF00, experience: 15, damage: 20 }, tank: { health: 80, speed: 1, tint: 0xFF0000, experience: 25, damage: 30 }, archer: { health: 40, speed: 3, tint: 0x0000FF, experience: 20, damage: 50 }, boss: { health: 300, speed: 1, tint: 0xFF00FF, experience: 50, damage: 1000 } }; var stats = enemyStats[self.enemyType]; // Calculate map-based scaling multipliers var mapMultiplier = 1; if (currentMap === 2) { mapMultiplier = 1.2; // +20% for Map 2 } else if (currentMap === 3) { mapMultiplier = 1.4; // +40% for Map 3 } else if (currentMap === 4) { mapMultiplier = 1.6; // +60% for Map 4 } var healthMultiplier = { easy: 1, normal: 2, hard: 4, veryHard: 8 }; self.maxHealth = Math.floor(stats.health * healthMultiplier[difficulty] * mapMultiplier); self.health = self.maxHealth; self.speed = stats.speed; self.experienceReward = stats.experience; self.damage = Math.floor(stats.damage * mapMultiplier); // Apply visual tint based on enemy type enemyGraphics.tint = stats.tint; self.update = function () { self.x -= self.speed; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('enemyDeath').play(); playerCharacter.gainExperience(self.experienceReward); }; return self; }); var LootItem = Container.expand(function (type, rarity, x, y) { var self = Container.call(this); var lootGraphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); self.type = type; self.rarity = rarity; // Rarity colors var rarityColors = { common: 0x808080, rare: 0x4169e1, epic: 0x8a2be2, legendary: 0xffd700 }; lootGraphics.tint = rarityColors[rarity]; // Generate random stats based on rarity with specific ranges var statRanges = { common: { health: { min: 1, max: 19 }, damage: { min: 1, max: 9 }, defense: { min: 1, max: 4 } }, rare: { health: { min: 20, max: 49 }, damage: { min: 7, max: 19 }, defense: { min: 4, max: 9 } }, epic: { health: { min: 50, max: 79 }, damage: { min: 15, max: 29 }, defense: { min: 10, max: 17 } }, legendary: { health: { min: 79, max: 179 }, damage: { min: 29, max: 59 }, defense: { min: 17, max: 29 } } }; var ranges = statRanges[rarity]; self.healthBonus = Math.floor(Math.random() * (ranges.health.max - ranges.health.min + 1)) + ranges.health.min; self.damageBonus = Math.floor(Math.random() * (ranges.damage.max - ranges.damage.min + 1)) + ranges.damage.min; self.defenseBonus = Math.floor(Math.random() * (ranges.defense.max - ranges.defense.min + 1)) + ranges.defense.min; self.x = x; self.y = y; self.down = function (x, y, obj) { collectLoot(self); }; return self; }); var NPC = Container.expand(function (mapNumber) { var self = Container.call(this); // Select NPC asset based on map var npcAsset = 'map1man'; if (mapNumber === 2) { npcAsset = 'map2man'; } else if (mapNumber === 3) { npcAsset = 'map3man'; } else if (mapNumber === 4) { npcAsset = 'map4man'; } var npcGraphics = self.attachAsset(npcAsset, { anchorX: 0.5, anchorY: 0.5 }); self.mapNumber = mapNumber; self.speed = 3; self.isWalking = true; self.hasReachedPlayer = false; self.isWalkingAway = false; self.targetX = 1024; // Player position self.targetY = 1366; self.lastX = self.x; self.update = function () { if (self.isWalking && !self.hasReachedPlayer) { // Walk towards player var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 100) { // Stop when close to player self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } else { // Reached player self.hasReachedPlayer = true; self.isWalking = false; showNPCDialogue(self.mapNumber); } } else if (self.isWalkingAway) { // Walk away from player back to spawn point self.x += self.speed; if (self.x > 2200) { // Remove NPC when off screen self.destroy(); var index = npcs.indexOf(self); if (index > -1) { npcs.splice(index, 1); } } } }; self.walkAway = function () { self.isWalkingAway = true; self.isWalking = true; // Face right when walking away npcGraphics.scaleX = -1; }; return self; }); var OverworldMonster = Container.expand(function () { var self = Container.call(this); var monsterGraphics = self.attachAsset('ch23', { anchorX: 0.5, anchorY: 0.5 }); self.health = 1; self.maxHealth = 1; self.speed = 4; // Slow movement self.lastX = 0; self.lastY = 0; self.update = function () { // Track last position for collision detection self.lastX = self.x; self.lastY = self.y; // Move slowly towards player var dx = playerCharacter.x - self.x; var dy = playerCharacter.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 5) { self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; } }; self.takeDamage = function (damage) { self.health -= damage; if (self.health <= 0) { self.die(); } }; self.die = function () { LK.getSound('enemyDeath').play(); playerCharacter.gainExperience(1); // Update level bar if in overworld if (currentState === 'overworld') { updateLevelBar(); } // Always heal player by 1 health point when ch23 monster dies, but don't exceed max health if (playerCharacter.health < playerCharacter.maxHealth) { playerCharacter.health += 1; } // Update health display every time a monster dies if (typeof updateHealthBar === 'function') { updateHealthBar(); } if (healthText && healthText.parent) { healthText.setText('Health: ' + playerCharacter.health + '/' + playerCharacter.maxHealth); } // Store monster position for ring drop var monsterX = self.x; var monsterY = self.y; // Ring drop chances var randomChance = Math.random(); if (randomChance < 0.000001) { // 0.00000001 chance for legendary ring createRingDrop('legendary', monsterX, monsterY); } else if (randomChance < 0.00001) { // 0.0000001 chance for epic ring createRingDrop('epic', monsterX, monsterY); } else if (randomChance < 0.0001) { // 0.000001 chance for rare ring createRingDrop('rare', monsterX, monsterY); } else if (randomChance < 0.001) { // 0.00001 chance for common ring createRingDrop('common', monsterX, monsterY); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state // Character and world assets // Combat assets // Loot assets // UI assets // Sounds 80; function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } var currentState = 'overworld'; // 'overworld', 'combat', 'inventory' var currentMap = 1; var currentDungeon = null; var playerCharacter = new Character(); var dungeons = []; var arrows = []; var enemies = []; var lootItems = []; var overworldMonsters = []; var npcs = []; var currentNPC = null; var dialogueActive = false; var dialogueBox = null; var dialogueText = null; var saveButton = null; var ignoreButton = null; var dungeonsDisabled = false; var lastEnemySpawn = 0; var lastArrowShot = 0; var lastOverworldMonsterSpawn = 0; var lastOverworldArrowShot = 0; var combatTimer = 0; var enemiesKilled = 0; var targetKills = 15; var totalEnemiesSpawned = 0; var maxEnergy = 5; var currentEnergy = 5; var energyRegenTimer = 1; var energyRegenDelay = 50; // 0.3 seconds at 60fps var isRegeneratingEnergy = false; var wasEnergyDepleted = false; var energyBar = null; var energyBarBg = null; var levelBarBg = null; var levelBarFill = null; var experienceText = null; var levelBarText = null; var pendingNotifications = []; var notificationTimer = 0; var currentNotification = null; var healthBar = null; var healthBarBg = null; var healthBarText = null; // Aiming system variables var isAiming = false; var aimStartX = 0; var aimStartY = 0; var aimLine = null; var maxAimDistance = 300; // UI elements var levelText = new Text2('Level: 1', { size: 40, fill: 0xFFFFFF }); var healthText = new Text2('Health: 100/100', { size: 40, fill: 0xFFFFFF }); var backButton = null; var statsPanel = null; var statsText = null; // Map-specific dialogue messages var mapDialogues = { 1: "Hello adventurer. Please save our village. The Demon Emperor has created 10 dungeons nearby with different levels of danger. If we don't act fast, monsters inside may break loose!", 2: "Welcome to our town. I am the captain of the town guard.I heard you destroyed the dungeons around the villages connected to our town.Now dungeons have appeared near us as well. Please, help us.", 3: "Hello adventurer, I am the lord of this city.I’ve heard of your heroism, though I’m not sure of your strength.Our city is about to fall victim to the demon lord’s plan.Can you help us?", 4: "I am Arthur, king of the empire. Your fame has spread across the entire realm, adventurer.The demons seek to conquer the empire.If you manage to save us, I will betroth my daughter, Princess Diana, to you." }; // Initialize storage with defaults var gameData = { level: storage.level || 1, experience: storage.experience || 0, equipment: { helmet: null, boots: null, armor: null, bow: null, necklace: null, ring: null, gloves: null }, unlockedMaps: storage.unlockedMaps || 1, completedDungeons: storage.completedDungeons || {}, givenLoot: storage.givenLoot || {}, dungeonEntryOrder: {}, npcInteractionCompleted: storage.npcInteractionCompleted || {} }; // Reconstruct dungeonEntryOrder from flattened storage for (var key in storage) { if (key.indexOf('dungeonEntryOrder_') === 0) { var orderKey = key.substring('dungeonEntryOrder_'.length); gameData.dungeonEntryOrder[orderKey] = storage[key]; } } // Load equipment from storage if (storage.equipmentHelmetType) { gameData.equipment.helmet = { type: storage.equipmentHelmetType, rarity: storage.equipmentHelmetRarity, healthBonus: storage.equipmentHelmetHealthBonus, damageBonus: storage.equipmentHelmetDamageBonus, defenseBonus: storage.equipmentHelmetDefenseBonus }; } if (storage.equipmentBootsType) { gameData.equipment.boots = { type: storage.equipmentBootsType, rarity: storage.equipmentBootsRarity, healthBonus: storage.equipmentBootsHealthBonus, damageBonus: storage.equipmentBootsDamageBonus, defenseBonus: storage.equipmentBootsDefenseBonus }; } if (storage.equipmentArmorType) { gameData.equipment.armor = { type: storage.equipmentArmorType, rarity: storage.equipmentArmorRarity, healthBonus: storage.equipmentArmorHealthBonus, damageBonus: storage.equipmentArmorDamageBonus, defenseBonus: storage.equipmentArmorDefenseBonus }; } if (storage.equipmentBowType) { gameData.equipment.bow = { type: storage.equipmentBowType, rarity: storage.equipmentBowRarity, healthBonus: storage.equipmentBowHealthBonus, damageBonus: storage.equipmentBowDamageBonus, defenseBonus: storage.equipmentBowDefenseBonus }; } if (storage.equipmentNecklaceType) { gameData.equipment.necklace = { type: storage.equipmentNecklaceType, rarity: storage.equipmentNecklaceRarity, healthBonus: storage.equipmentNecklaceHealthBonus, damageBonus: storage.equipmentNecklaceDamageBonus, defenseBonus: storage.equipmentNecklaceDefenseBonus }; } if (storage.equipmentRingType) { gameData.equipment.ring = { type: storage.equipmentRingType, rarity: storage.equipmentRingRarity, healthBonus: storage.equipmentRingHealthBonus, damageBonus: storage.equipmentRingDamageBonus, defenseBonus: storage.equipmentRingDefenseBonus }; } if (storage.equipmentGlovesType) { gameData.equipment.gloves = { type: storage.equipmentGlovesType, rarity: storage.equipmentGlovesRarity, healthBonus: storage.equipmentGlovesHealthBonus, damageBonus: storage.equipmentGlovesDamageBonus, defenseBonus: storage.equipmentGlovesDefenseBonus }; } // Load saved data playerCharacter.level = gameData.level; playerCharacter.experience = gameData.experience; playerCharacter.equipment = gameData.equipment; playerCharacter.calculateStats(); // Update character appearance based on loaded equipment updateCharacterAppearance(); // Load dungeon progression gameData.completedDungeons = storage.completedDungeons || {}; gameData.givenLoot = storage.givenLoot || {}; currentMap = storage.unlockedMaps || 1; // Setup overworld function setupOverworld() { game.removeChildren(); // Add background based on current map var backgroundAsset = 'mapBackground'; // default for map 1 if (currentMap === 2) { backgroundAsset = 'backboard2'; } else if (currentMap === 3) { backgroundAsset = 'backboard3'; } else if (currentMap === 4) { backgroundAsset = 'backboard4'; } var background = game.attachAsset(backgroundAsset, { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Add character at center playerCharacter.x = 1024; playerCharacter.y = 1366; game.addChild(playerCharacter); // Create health bar above character createHealthBar(); // Create stats panel in bottom-right corner createStatsPanel(); // Check if NPC interaction is needed for this map var npcKey = 'map' + currentMap; if (!gameData.npcInteractionCompleted[npcKey]) { spawnNPC(currentMap); dungeonsDisabled = true; // Disable dungeons until NPC interaction is complete } else { dungeonsDisabled = false; } // Create dungeons in isometric grid dungeons = []; var allDifficulties = ['easy', 'easy', 'easy', 'easy', 'normal', 'normal', 'normal', 'hard', 'hard', 'veryHard']; // Function to check if position is valid (at least 300 units away from existing dungeons and 300 from player) function isValidPosition(x, y, existingDungeons) { // Check distance from player starting position (1024, 1366) var playerStartX = 1024; var playerStartY = 1366; var distanceFromPlayer = Math.sqrt(Math.pow(x - playerStartX, 2) + Math.pow(y - playerStartY, 2)); if (distanceFromPlayer < 300) { return false; } // Check distance from existing dungeons using Euclidean distance for (var i = 0; i < existingDungeons.length; i++) { var dx = x - existingDungeons[i].x; var dy = y - existingDungeons[i].y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 300) { return false; } } return true; } // Create all 10 dungeons but control their interaction based on progression var difficultyIndices = { easy: 0, normal: 0, hard: 0, veryHard: 0 }; for (var i = 0; i < allDifficulties.length; i++) { var difficulty = allDifficulties[i]; var dungeonIndex = difficultyIndices[difficulty]; var completedCount = getCompletedDungeonsForDifficulty(currentMap, difficulty); var isUnlocked = isDifficultyUnlocked(currentMap, difficulty); var isCompleted = dungeonIndex < completedCount; // Skip creating completed dungeons (they should disappear) if (isCompleted) { difficultyIndices[difficulty]++; continue; } var dungeon = new Dungeon(difficulty, dungeonIndex); difficultyIndices[difficulty]++; // Scale very hard dungeons by 30% if (difficulty === 'veryHard') { tween(dungeon, { scaleX: 1.3, scaleY: 1.3 }, { duration: 0 }); } // Scale easy dungeons by 38% smaller (0.62x total scale) if (difficulty === 'easy') { tween(dungeon, { scaleX: 0.62, scaleY: 0.62 }, { duration: 0 }); } // Scale hard dungeons by 10% if (difficulty === 'hard') { tween(dungeon, { scaleX: 1.1, scaleY: 1.1 }, { duration: 0 }); } // Apply visual effects based on lock status if (!isUnlocked) { // Dim locked dungeons dungeon.children[0].tint = 0x666666; dungeon.children[0].alpha = 0.5; // Disable interaction for locked dungeons dungeon.down = null; } // Set fixed positions for each dungeon based on difficulty and index var fixedPositions = { easy: [{ x: 200, y: 980 }, { x: 1300, y: 1600 }, { x: 380, y: 850 }, { x: 670, y: 1050 }], normal: [{ x: 400, y: 1280 }, { x: 450, y: 1650 }, { x: 1600, y: 1900 }], hard: [{ x: 500, y: 2200 }, { x: 900, y: 2000 }], veryHard: [{ x: 1300, y: 2400 }] }; var positions = fixedPositions[difficulty]; if (positions && dungeonIndex < positions.length) { dungeon.x = positions[dungeonIndex].x; dungeon.y = positions[dungeonIndex].y; } else { // Fallback to safe default position if something goes wrong dungeon.x = 1024; dungeon.y = 1500; } dungeons.push(dungeon); game.addChild(dungeon); } // Add level bar UI to overworld levelBarBg = game.attachAsset('levelBarBg', { anchorX: 0, anchorY: 0, x: 50, y: 2600 }); levelBarFill = game.attachAsset('levelBarFill', { anchorX: 0, anchorY: 0, x: 50, y: 2600 }); experienceText = new Text2('XP: 0/100', { size: 30, fill: 0xFFFFFF }); experienceText.x = 50; experienceText.y = 2630; game.addChild(experienceText); // Add level text below the level bar levelBarText = new Text2('Level: ' + playerCharacter.level, { size: 30, fill: 0xFFFFFF }); levelBarText.x = 50; levelBarText.y = 2660; game.addChild(levelBarText); updateLevelBar(); // Add UI levelText.setText('Level: ' + playerCharacter.level); levelText.x = 50; levelText.y = 150; game.addChild(levelText); // Add map indicator var mapText = new Text2('Map: ' + currentMap + ' (' + getMapRarity(currentMap) + ')\nKill the demon rabbits and obtain the ring.', { size: 40, fill: 0xFFFFFF }); mapText.x = 850; mapText.y = 100; game.addChild(mapText); // Add restart admin button var restartButton = game.attachAsset('backButton', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 100, y: 100 }); // Add restart button text var restartText = new Text2('Restart', { size: 24, fill: 0xFFFFFF }); restartText.anchor.set(0.5, 0.5); restartText.x = 2048 - 100; restartText.y = 100; game.addChild(restartText); // Next map button functionality removed } // Setup combat function setupCombat() { game.removeChildren(); // Add combat background based on current map var combatBackgroundAsset = 'combatBackground'; // default for map 1 if (currentMap === 2) { combatBackgroundAsset = 'combat2'; } else if (currentMap === 3) { combatBackgroundAsset = 'combat3'; } else if (currentMap === 4) { combatBackgroundAsset = 'combat4'; } var combatBg = game.attachAsset(combatBackgroundAsset, { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Position character fixed on left side for side-scrolling playerCharacter.x = 200; playerCharacter.y = 1866; game.addChild(playerCharacter); // Create health bar above character createHealthBar(); // Add back button backButton = game.attachAsset('backButton', { anchorX: 0.5, anchorY: 0.5, x: 100, y: 50 }); // Reset combat variables arrows = []; enemies = []; lootItems = []; overworldMonsters = []; lastEnemySpawn = 0; lastArrowShot = 0; lastOverworldMonsterSpawn = 0; lastOverworldArrowShot = 0; combatTimer = 0; enemiesKilled = 0; targetKills = 15; totalEnemiesSpawned = 0; // Add energy bar UI energyBarBg = game.attachAsset('energyBarBg', { anchorX: 0, anchorY: 0, x: 50, y: 2000 }); energyBar = game.attachAsset('energyBar', { anchorX: 0, anchorY: 0, x: 50, y: 2000 }); // Reset energy system currentEnergy = maxEnergy; energyRegenTimer = 1; isRegeneratingEnergy = false; wasEnergyDepleted = false; updateEnergyBar(); // Add level bar UI levelBarBg = game.attachAsset('levelBarBg', { anchorX: 0, anchorY: 0, x: 50, y: 2050 }); levelBarFill = game.attachAsset('levelBarFill', { anchorX: 0, anchorY: 0, x: 50, y: 2050 }); experienceText = new Text2('XP: 0/100', { size: 30, fill: 0xFFFFFF }); experienceText.x = 50; experienceText.y = 2080; game.addChild(experienceText); // Add level text below the level bar levelBarText = new Text2('Level: ' + playerCharacter.level, { size: 30, fill: 0xFFFFFF }); levelBarText.x = 50; levelBarText.y = 2110; game.addChild(levelBarText); updateLevelBar(); // Add UI updateCombatUI(); } function updateEnergyBar() { if (energyBar) { var energyPercentage = currentEnergy / maxEnergy; energyBar.width = 300 * energyPercentage; // Change color based on energy level if (energyPercentage > 0.6) { energyBar.tint = 0xFFD700; // Green } else if (energyPercentage > 0.3) { energyBar.tint = 0xffff00; // Yellow } else { energyBar.tint = 0xff0000; // Red } } } function updateLevelBar() { if (levelBarFill && experienceText) { var levelUpThreshold = playerCharacter.level * 100; var experiencePercentage = playerCharacter.experience / levelUpThreshold; levelBarFill.width = 400 * experiencePercentage; // Update experience text experienceText.setText('XP: ' + playerCharacter.experience + '/' + levelUpThreshold); // Update level text if (levelBarText) { levelBarText.setText('Level: ' + playerCharacter.level); } } } function updateHealthBar() { if (healthBar && healthBarText) { var healthPercentage = playerCharacter.health / playerCharacter.maxHealth; healthBar.width = 200 * healthPercentage; healthBarText.setText(playerCharacter.health + '/' + playerCharacter.maxHealth); } } function createStatsPanel() { if (statsPanel) { statsPanel.destroy(); } if (statsText) { statsText.destroy(); } // Create stats panel in bottom-right corner - enlarged to accommodate level bonuses statsPanel = game.attachAsset('statsPanel', { anchorX: 1, anchorY: 1, x: 2048 - 20, y: 2732 - 20, scaleX: 1.2, scaleY: 1.6 }); // Calculate total bonuses from equipment var totalHealthBonus = 0; var totalDamageBonus = 0; var totalDefenseBonus = 0; for (var slot in playerCharacter.equipment) { if (playerCharacter.equipment[slot]) { var item = playerCharacter.equipment[slot]; totalHealthBonus += item.healthBonus || 0; totalDamageBonus += item.damageBonus || 0; totalDefenseBonus += item.defenseBonus || 0; } } // Calculate level bonuses var levelHealthBonus = playerCharacter.level * 10; var levelDamageBonus = playerCharacter.level * 2; var levelDefenseBonus = playerCharacter.level * 1; // Create stats text with both equipment and level bonuses var statsContent = 'Equipment Bonuses:\n' + 'Health: +' + totalHealthBonus + '\n' + 'Damage: +' + totalDamageBonus + '\n' + 'Defense: +' + totalDefenseBonus + '\n\n' + 'Level ' + playerCharacter.level + ' Bonuses:\n' + 'Health: +' + levelHealthBonus + '\n' + 'Damage: +' + levelDamageBonus + '\n' + 'Defense: +' + levelDefenseBonus; statsText = new Text2(statsContent, { size: 28, fill: 0xFFFFFF }); statsText.anchor.set(0.5, 0.5); statsText.x = 2048 - 260; statsText.y = 2732 - 180; game.addChild(statsText); } function createHealthBar() { if (healthBarBg) { healthBarBg.destroy(); } if (healthBar) { healthBar.destroy(); } if (healthBarText) { healthBarText.destroy(); } // Create health bar background healthBarBg = game.attachAsset('healthBarBg', { anchorX: 0.5, anchorY: 0.5, x: playerCharacter.x, y: playerCharacter.y - 130 }); // Create health bar fill healthBar = game.attachAsset('healthBar', { anchorX: 0.5, anchorY: 0.5, x: playerCharacter.x, y: playerCharacter.y - 130 }); // Create health text healthBarText = new Text2(playerCharacter.health + '/' + playerCharacter.maxHealth, { size: 30, fill: 0xFFFFFF }); healthBarText.anchor.set(0.5, 1); healthBarText.x = playerCharacter.x; healthBarText.y = playerCharacter.y - 150; game.addChild(healthBarText); updateHealthBar(); } function updateCombatUI() { if (levelText.parent) { levelText.parent.removeChild(levelText); } if (healthText.parent) { healthText.parent.removeChild(healthText); } levelText.setText('Level: ' + playerCharacter.level); levelText.x = 50; levelText.y = 150; game.addChild(levelText); healthText.setText('Health: ' + playerCharacter.health + '/' + playerCharacter.maxHealth); healthText.x = 50; healthText.y = 200; game.addChild(healthText); // Update level bar updateLevelBar(); } function collectLoot(loot) { // Equip the item playerCharacter.equipment[loot.type] = loot; playerCharacter.calculateStats(); // Update character appearance updateCharacterAppearance(); // Update stats panel if in overworld if (currentState === 'overworld') { createStatsPanel(); } // Remove from game loot.destroy(); var index = lootItems.indexOf(loot); if (index > -1) { lootItems.splice(index, 1); } // Save game data saveGameData(); updateCombatUI(); } function getStatRangeForRarity(rarity) { var statRanges = { common: { health: { min: 1, max: 19, range: 19 }, damage: { min: 1, max: 9, range: 9 }, defense: { min: 1, max: 4, range: 4 } }, rare: { health: { min: 20, max: 49, range: 30 }, damage: { min: 7, max: 19, range: 13 }, defense: { min: 4, max: 9, range: 6 } }, epic: { health: { min: 50, max: 79, range: 30 }, damage: { min: 15, max: 29, range: 15 }, defense: { min: 10, max: 17, range: 8 } }, legendary: { health: { min: 79, max: 179, range: 101 }, damage: { min: 29, max: 59, range: 31 }, defense: { min: 17, max: 29, range: 13 } } }; return statRanges[rarity]; } function getMapRarity(mapNumber) { var rarities = ['common', 'rare', 'epic', 'legendary']; return rarities[Math.min(mapNumber - 1, 3)]; } function getDungeonProgressionKey(mapNumber, difficulty) { return 'map' + mapNumber + '_' + difficulty; } function getCompletedDungeonsForDifficulty(mapNumber, difficulty) { var key = getDungeonProgressionKey(mapNumber, difficulty); return gameData.completedDungeons[key] || 0; } function isDifficultyUnlocked(mapNumber, difficulty) { if (difficulty === 'easy') { return true; } if (difficulty === 'normal') { return getCompletedDungeonsForDifficulty(mapNumber, 'easy') >= 4; } if (difficulty === 'hard') { return getCompletedDungeonsForDifficulty(mapNumber, 'normal') >= 3; } if (difficulty === 'veryHard') { return getCompletedDungeonsForDifficulty(mapNumber, 'hard') >= 2; } return false; } function completeDungeon(mapNumber, difficulty, index) { var key = getDungeonProgressionKey(mapNumber, difficulty); gameData.completedDungeons[key] = (gameData.completedDungeons[key] || 0) + 1; // Give fixed loot based on difficulty and completion order var lootKey = 'map' + mapNumber + '_' + difficulty + '_' + index; if (!gameData.givenLoot[lootKey]) { var loot = getFixedLoot(mapNumber, difficulty, index); if (loot) { giveLoot(loot.type, loot.rarity); gameData.givenLoot[lootKey] = true; // Auto-equip necklace from hard dungeons if (difficulty === 'hard' && loot.type === 'necklace') { playerCharacter.equipment.necklace = { type: loot.type, rarity: loot.rarity, healthBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).health.range) + getStatRangeForRarity(loot.rarity).health.min, damageBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).damage.range) + getStatRangeForRarity(loot.rarity).damage.min, defenseBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).defense.range) + getStatRangeForRarity(loot.rarity).defense.min }; playerCharacter.calculateStats(); updateCharacterAppearance(); } // Auto-equip bow from very hard dungeons if (difficulty === 'veryHard' && loot.type === 'bow') { playerCharacter.equipment.bow = { type: loot.type, rarity: loot.rarity, healthBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).health.range) + getStatRangeForRarity(loot.rarity).health.min, damageBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).damage.range) + getStatRangeForRarity(loot.rarity).damage.min, defenseBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).defense.range) + getStatRangeForRarity(loot.rarity).defense.min }; playerCharacter.calculateStats(); updateCharacterAppearance(); } // Add notification for awarded item pendingNotifications.push({ type: loot.type, rarity: loot.rarity, message: 'Awarded: ' + loot.rarity + ' ' + loot.type }); } } // Check if we should advance to next map or win the game if (difficulty === 'veryHard' && getCompletedDungeonsForDifficulty(mapNumber, 'veryHard') >= 1) { // Map 4 Very Hard completion wins the game if (mapNumber === 4) { LK.showYouWin(); return; } gameData.unlockedMaps = Math.max(gameData.unlockedMaps, mapNumber + 1); currentMap = mapNumber + 1; if (mapNumber === 1) { currentMap = 'rare'; } // Reset dungeon completion and loot tracking for new map resetMapProgress(mapNumber + 1); } saveGameData(); LK.setTimeout(function () { currentState = 'overworld'; setupOverworld(); }, 1000); } function getFixedLoot(mapNumber, difficulty, index) { var rarity = getMapRarity(mapNumber); if (difficulty === 'easy') { // Only the first easy dungeon entered drops helmet var orderKey = 'map' + mapNumber + '_easy_order'; var entryOrder = gameData.dungeonEntryOrder[orderKey] || []; var entryPosition = entryOrder.indexOf(index); // Only the first entered easy dungeon (position 0) drops helmet if (entryPosition === 0) { // Set helmet rarity based on map var helmetRarity = 'common'; // Map 1 default if (mapNumber === 2) { helmetRarity = 'rare'; } else if (mapNumber === 3) { helmetRarity = 'epic'; } else if (mapNumber === 4) { helmetRarity = 'legendary'; } return { type: 'helmet', rarity: helmetRarity }; } // Other easy dungeons drop nothing return null; } else if (difficulty === 'normal') { var orderKey = 'map' + mapNumber + '_normal_order'; var entryOrder = gameData.dungeonEntryOrder[orderKey] || []; var entryPosition = entryOrder.indexOf(index); if (entryPosition === 0) { return { type: 'boots', rarity: rarity }; } if (entryPosition === 1) { return { type: 'gloves', rarity: rarity }; } return null; } else if (difficulty === 'hard') { var orderKey = 'map' + mapNumber + '_hard_order'; var entryOrder = gameData.dungeonEntryOrder[orderKey] || []; var entryPosition = entryOrder.indexOf(index); var availableAssets = ['armor', 'necklace']; if (entryPosition === 0 && availableAssets.includes('armor')) { return { type: 'armor', rarity: rarity }; } if (entryPosition === 1 && availableAssets.includes('necklace')) { return { type: 'necklace', rarity: rarity }; } return null; } else if (difficulty === 'veryHard') { if (mapNumber === 1) { return { type: 'bow', rarity: 'rare' }; } } return null; } function createRingDrop(rarity, x, y) { // Create a proper loot item with stats var statRanges = { common: { health: { min: 1, max: 19 }, damage: { min: 1, max: 9 }, defense: { min: 1, max: 4 } }, rare: { health: { min: 20, max: 49 }, damage: { min: 7, max: 19 }, defense: { min: 4, max: 9 } }, epic: { health: { min: 50, max: 79 }, damage: { min: 15, max: 29 }, defense: { min: 10, max: 17 } }, legendary: { health: { min: 79, max: 179 }, damage: { min: 29, max: 59 }, defense: { min: 17, max: 29 } } }; var ranges = statRanges[rarity]; var loot = { type: 'ring', rarity: rarity, healthBonus: Math.floor(Math.random() * (ranges.health.max - ranges.health.min + 1)) + ranges.health.min, damageBonus: Math.floor(Math.random() * (ranges.damage.max - ranges.damage.min + 1)) + ranges.damage.min, defenseBonus: Math.floor(Math.random() * (ranges.defense.max - ranges.defense.min + 1)) + ranges.defense.min }; // Create visual loot item at monster death position var visualLoot = new LootItem('ring', rarity, x, y); lootItems.push(visualLoot); game.addChild(visualLoot); LK.getSound('lootDrop').play(); } function giveLoot(type, rarity) { // Create a proper loot item with stats var statRanges = { common: { health: { min: 1, max: 19 }, damage: { min: 1, max: 9 }, defense: { min: 1, max: 4 } }, rare: { health: { min: 20, max: 49 }, damage: { min: 7, max: 19 }, defense: { min: 4, max: 9 } }, epic: { health: { min: 50, max: 79 }, damage: { min: 15, max: 29 }, defense: { min: 10, max: 17 } }, legendary: { health: { min: 79, max: 179 }, damage: { min: 29, max: 59 }, defense: { min: 17, max: 29 } } }; var ranges = statRanges[rarity]; var loot = { type: type, rarity: rarity, healthBonus: Math.floor(Math.random() * (ranges.health.max - ranges.health.min + 1)) + ranges.health.min, damageBonus: Math.floor(Math.random() * (ranges.damage.max - ranges.damage.min + 1)) + ranges.damage.min, defenseBonus: Math.floor(Math.random() * (ranges.defense.max - ranges.defense.min + 1)) + ranges.defense.min }; // If we're in overworld and it's a ring, create a visual loot item if (currentState === 'overworld' && type === 'ring') { // Find the last killed monster position or use a default position var dropX = playerCharacter.x + (Math.random() - 0.5) * 200; var dropY = playerCharacter.y + (Math.random() - 0.5) * 200; // Create visual loot item var visualLoot = new LootItem(type, rarity, dropX, dropY); lootItems.push(visualLoot); game.addChild(visualLoot); } else { // For other cases or when in combat, equip directly playerCharacter.equipment[type] = loot; playerCharacter.calculateStats(); // Update character appearance updateCharacterAppearance(); } LK.getSound('lootDrop').play(); } function resetMapProgress(mapNumber) { // Don't reset progress, just ensure we're tracking the new map } function getCharacterAsset() { // Count equipped gear pieces (excluding necklace and ring) var hasHelmet = playerCharacter.equipment.helmet !== null; var hasBoots = playerCharacter.equipment.boots !== null; var hasGloves = playerCharacter.equipment.gloves !== null; var hasArmor = playerCharacter.equipment.armor !== null; var hasBow = playerCharacter.equipment.bow !== null; // Get helmet rarity (determines the set's base tier) var helmetRarity = hasHelmet ? playerCharacter.equipment.helmet.rarity : null; var bowRarity = hasBow ? playerCharacter.equipment.bow.rarity : null; // If no helmet, use default character if (!hasHelmet) { return 'character'; } // Define rarity levels var rarityLevels = { 'common': 0, 'rare': 1, 'epic': 2, 'legendary': 3 }; var helmetLevel = rarityLevels[helmetRarity]; var bowLevel = bowRarity ? rarityLevels[bowRarity] : -1; // Map 1: Common set (helmet=common, bow=rare) if (helmetLevel === 0) { if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && !hasBow) { return 'ch1'; } if (hasHelmet && hasBoots && !hasGloves && !hasArmor && !hasBow) { return 'ch2'; } if (hasHelmet && hasBoots && hasGloves && !hasArmor && !hasBow) { return 'ch3'; } if (hasHelmet && hasBoots && hasGloves && hasArmor && !hasBow) { return 'ch4'; } if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 1) { return 'ch5'; } } // Map 2: Rare set (helmet=rare, bow=epic) if (helmetLevel === 1) { if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 2) { return 'ch6'; } if (hasHelmet && hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 2) { return 'ch7'; } if (hasHelmet && hasBoots && hasGloves && !hasArmor && hasBow && bowLevel === 2) { return 'ch8'; } if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 2) { return 'ch9'; } if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) { return 'ch10'; } } // Map 3: Epic set (helmet=epic, bow=legendary) if (helmetLevel === 2) { if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) { return 'ch11'; } if (hasHelmet && hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) { return 'ch12'; } if (hasHelmet && hasBoots && hasGloves && !hasArmor && hasBow && bowLevel === 3) { return 'ch13'; } if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) { return 'ch14'; } if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) { return 'ch15'; } } // Map 4: Legendary set (helmet=legendary, bow=legendary) if (helmetLevel === 3) { if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) { return 'ch16'; } if (hasHelmet && hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) { return 'ch17'; } if (hasHelmet && hasBoots && hasGloves && !hasArmor && hasBow && bowLevel === 3) { return 'ch18'; } if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) { return 'ch19'; } } // Fallback to default character return 'character'; } function updateCharacterAppearance() { // Get the appropriate asset based on equipment var assetName = getCharacterAsset(); // Remove current character graphics if (playerCharacter.children.length > 0) { playerCharacter.removeChildAt(0); } // Add new character graphics var characterGraphics = playerCharacter.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); } function showNotification(notification) { if (currentNotification) { // Remove existing notification currentNotification.destroy(); currentNotification = null; } // Create notification text var notificationText = new Text2(notification.message, { size: 60, fill: 0xFFFFFF }); notificationText.anchor.set(0.5, 0.5); notificationText.x = 1024; notificationText.y = 400; // Add background for notification var notificationBg = game.attachAsset('inventorySlot', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 400, scaleX: 8, scaleY: 1.5 }); notificationBg.tint = 0x000000; notificationBg.alpha = 0.8; game.addChild(notificationBg); game.addChild(notificationText); currentNotification = { text: notificationText, bg: notificationBg, destroy: function destroy() { if (this.text.parent) { this.text.parent.removeChild(this.text); } if (this.bg.parent) { this.bg.parent.removeChild(this.bg); } } }; // Auto-remove after 3 seconds LK.setTimeout(function () { if (currentNotification) { currentNotification.destroy(); currentNotification = null; } }, 3000); } function spawnNPC(mapNumber) { // Spawn NPC from right-middle of screen var npc = new NPC(mapNumber); npc.x = 2200; // Start from right side off-screen npc.y = 1366; // Middle height npcs.push(npc); game.addChild(npc); currentNPC = npc; } function showNPCDialogue(mapNumber) { dialogueActive = true; // Create dialogue box dialogueBox = game.attachAsset('dialogueBox', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 800 }); dialogueBox.tint = 0x000000; dialogueBox.alpha = 0.9; // Create dialogue text var message = mapDialogues[mapNumber]; // Add safety check for undefined message if (!message) { message = "Welcome, adventurer. Please help us with our troubles."; } dialogueText = new Text2(message, { size: 36, fill: 0xFFFFFF }); dialogueText.anchor.set(0.5, 0.5); dialogueText.x = 1024; dialogueText.y = 800; game.addChild(dialogueText); // Wrap text to fit in dialogue box var maxWidth = 1400; var words = message.split(' '); var lines = []; var currentLine = ''; for (var i = 0; i < words.length; i++) { var testLine = currentLine + words[i] + ' '; if (testLine.length * 20 > maxWidth && currentLine !== '') { lines.push(currentLine); currentLine = words[i] + ' '; } else { currentLine = testLine; } } if (currentLine !== '') { lines.push(currentLine); } dialogueText.setText(lines.join('\n')); // Create choice buttons saveButton = game.attachAsset('dialogueButton', { anchorX: 0.5, anchorY: 0.5, x: 724, y: 1000 }); saveButton.tint = 0x4caf50; var saveText = new Text2('Save', { size: 32, fill: 0xFFFFFF }); saveText.anchor.set(0.5, 0.5); saveText.x = 724; saveText.y = 1000; game.addChild(saveText); ignoreButton = game.attachAsset('ignoreButton', { anchorX: 0.5, anchorY: 0.5, x: 1324, y: 1000 }); ignoreButton.tint = 0xf44336; var ignoreText = new Text2('Ignore', { size: 32, fill: 0xFFFFFF }); ignoreText.anchor.set(0.5, 0.5); ignoreText.x = 1324; ignoreText.y = 1000; game.addChild(ignoreText); } function closeDialogue() { if (dialogueBox) { dialogueBox.destroy(); dialogueBox = null; } if (dialogueText) { dialogueText.destroy(); dialogueText = null; } if (saveButton) { saveButton.destroy(); saveButton = null; } if (ignoreButton) { ignoreButton.destroy(); ignoreButton = null; } // Remove button text elements var textElements = game.children.filter(function (child) { return child instanceof Text2 && (child.text === 'Save' || child.text === 'Ignore' || child.text === 'Thank you, adventurer. I await your return with good news.'); }); for (var i = 0; i < textElements.length; i++) { textElements[i].destroy(); } dialogueActive = false; } function handleNPCChoice(choice) { if (choice === 'save') { // Store references to button text elements before destroying buttons var saveTextElement = null; var ignoreTextElement = null; // Find and store references to button text elements for (var i = 0; i < game.children.length; i++) { var child = game.children[i]; if (child instanceof Text2) { if (child.text === 'Save') { saveTextElement = child; } else if (child.text === 'Ignore') { ignoreTextElement = child; } } } // Show thank you message closeDialogue(); // Remove button text elements if (saveTextElement && saveTextElement.parent) { saveTextElement.destroy(); } if (ignoreTextElement && ignoreTextElement.parent) { ignoreTextElement.destroy(); } // Create thank you dialogue dialogueBox = game.attachAsset('dialogueBox', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 800 }); dialogueBox.tint = 0x000000; dialogueBox.alpha = 0.9; var thankYouText = new Text2('Thank you, adventurer. I await your return with good news.', { size: 36, fill: 0xFFFFFF }); thankYouText.anchor.set(0.5, 0.5); thankYouText.x = 1024; thankYouText.y = 800; game.addChild(thankYouText); // Auto-close after 3 seconds and enable dungeons LK.setTimeout(function () { // Clean up thank you dialogue if (dialogueBox) { dialogueBox.destroy(); dialogueBox = null; } if (thankYouText && thankYouText.parent) { thankYouText.destroy(); } // Mark NPC interaction as completed var npcKey = 'map' + currentMap; gameData.npcInteractionCompleted[npcKey] = true; storage.npcInteractionCompleted = gameData.npcInteractionCompleted; dungeonsDisabled = false; // Make NPC walk away if (currentNPC) { currentNPC.walkAway(); } }, 3000); } else if (choice === 'ignore') { // End game LK.showGameOver(); } } function saveGameData() { storage.level = playerCharacter.level; storage.experience = playerCharacter.experience; storage.unlockedMaps = gameData.unlockedMaps; storage.completedDungeons = gameData.completedDungeons; storage.givenLoot = gameData.givenLoot; storage.npcInteractionCompleted = gameData.npcInteractionCompleted; // Flatten dungeonEntryOrder for storage compatibility for (var key in gameData.dungeonEntryOrder) { storage['dungeonEntryOrder_' + key] = gameData.dungeonEntryOrder[key]; } // Save equipment properties individually - only save if equipment exists if (playerCharacter.equipment.helmet) { storage.equipmentHelmetType = playerCharacter.equipment.helmet.type; storage.equipmentHelmetRarity = playerCharacter.equipment.helmet.rarity; storage.equipmentHelmetHealthBonus = playerCharacter.equipment.helmet.healthBonus; storage.equipmentHelmetDamageBonus = playerCharacter.equipment.helmet.damageBonus; storage.equipmentHelmetDefenseBonus = playerCharacter.equipment.helmet.defenseBonus; } else { storage.equipmentHelmetType = null; } if (playerCharacter.equipment.boots) { storage.equipmentBootsType = playerCharacter.equipment.boots.type; storage.equipmentBootsRarity = playerCharacter.equipment.boots.rarity; storage.equipmentBootsHealthBonus = playerCharacter.equipment.boots.healthBonus; storage.equipmentBootsDamageBonus = playerCharacter.equipment.boots.damageBonus; storage.equipmentBootsDefenseBonus = playerCharacter.equipment.boots.defenseBonus; } else { storage.equipmentBootsType = null; } if (playerCharacter.equipment.armor) { storage.equipmentArmorType = playerCharacter.equipment.armor.type; storage.equipmentArmorRarity = playerCharacter.equipment.armor.rarity; storage.equipmentArmorHealthBonus = playerCharacter.equipment.armor.healthBonus; storage.equipmentArmorDamageBonus = playerCharacter.equipment.armor.damageBonus; storage.equipmentArmorDefenseBonus = playerCharacter.equipment.armor.defenseBonus; } else { storage.equipmentArmorType = null; } if (playerCharacter.equipment.bow) { storage.equipmentBowType = playerCharacter.equipment.bow.type; storage.equipmentBowRarity = playerCharacter.equipment.bow.rarity; storage.equipmentBowHealthBonus = playerCharacter.equipment.bow.healthBonus; storage.equipmentBowDamageBonus = playerCharacter.equipment.bow.damageBonus; storage.equipmentBowDefenseBonus = playerCharacter.equipment.bow.defenseBonus; } else { storage.equipmentBowType = null; } if (playerCharacter.equipment.necklace) { storage.equipmentNecklaceType = playerCharacter.equipment.necklace.type; storage.equipmentNecklaceRarity = playerCharacter.equipment.necklace.rarity; storage.equipmentNecklaceHealthBonus = playerCharacter.equipment.necklace.healthBonus; storage.equipmentNecklaceDamageBonus = playerCharacter.equipment.necklace.damageBonus; storage.equipmentNecklaceDefenseBonus = playerCharacter.equipment.necklace.defenseBonus; } else { storage.equipmentNecklaceType = null; } if (playerCharacter.equipment.ring) { storage.equipmentRingType = playerCharacter.equipment.ring.type; storage.equipmentRingRarity = playerCharacter.equipment.ring.rarity; storage.equipmentRingHealthBonus = playerCharacter.equipment.ring.healthBonus; storage.equipmentRingDamageBonus = playerCharacter.equipment.ring.damageBonus; storage.equipmentRingDefenseBonus = playerCharacter.equipment.ring.defenseBonus; } else { storage.equipmentRingType = null; } if (playerCharacter.equipment.gloves) { storage.equipmentGlovesType = playerCharacter.equipment.gloves.type; storage.equipmentGlovesRarity = playerCharacter.equipment.gloves.rarity; storage.equipmentGlovesHealthBonus = playerCharacter.equipment.gloves.healthBonus; storage.equipmentGlovesDamageBonus = playerCharacter.equipment.gloves.damageBonus; storage.equipmentGlovesDefenseBonus = playerCharacter.equipment.gloves.defenseBonus; } else { storage.equipmentGlovesType = null; } } // Game input handlers var draggedCharacter = null; var dragOffset = { x: 0, y: 0 }; game.down = function (x, y, obj) { if (currentState === 'overworld') { // Handle NPC dialogue button clicks if (dialogueActive && saveButton && ignoreButton) { // Check if clicking on Save button - using direct coordinate comparison if (x >= 524 && x <= 924 && y >= 960 && y <= 1040) { handleNPCChoice('save'); return; } // Check if clicking on Ignore button - using direct coordinate comparison if (x >= 1124 && x <= 1524 && y >= 960 && y <= 1040) { handleNPCChoice('ignore'); return; } } // Next map button functionality removed // Check if clicking on restart button (positioned at top right) if (x >= 2048 - 160 && x <= 2048 - 40 && y >= 70 && y <= 130) { // Reset all storage data to initial values storage.level = 1; storage.experience = 0; storage.unlockedMaps = 1; storage.completedDungeons = {}; storage.givenLoot = {}; // Clear all flattened dungeonEntryOrder keys for (var key in storage) { if (key.indexOf('dungeonEntryOrder_') === 0) { storage[key] = null; } } // Reset all equipment storage storage.equipmentHelmetType = null; storage.equipmentHelmetRarity = null; storage.equipmentHelmetHealthBonus = null; storage.equipmentHelmetDamageBonus = null; storage.equipmentHelmetDefenseBonus = null; storage.equipmentBootsType = null; storage.equipmentBootsRarity = null; storage.equipmentBootsHealthBonus = null; storage.equipmentBootsDamageBonus = null; storage.equipmentBootsDefenseBonus = null; storage.equipmentArmorType = null; storage.equipmentArmorRarity = null; storage.equipmentArmorHealthBonus = null; storage.equipmentArmorDamageBonus = null; storage.equipmentArmorDefenseBonus = null; storage.equipmentBowType = null; storage.equipmentBowRarity = null; storage.equipmentBowHealthBonus = null; storage.equipmentBowDamageBonus = null; storage.equipmentBowDefenseBonus = null; storage.equipmentNecklaceType = null; storage.equipmentNecklaceRarity = null; storage.equipmentNecklaceHealthBonus = null; storage.equipmentNecklaceDamageBonus = null; storage.equipmentNecklaceDefenseBonus = null; storage.equipmentRingType = null; storage.equipmentRingRarity = null; storage.equipmentRingHealthBonus = null; storage.equipmentRingDamageBonus = null; storage.equipmentRingDefenseBonus = null; storage.equipmentGlovesType = null; storage.equipmentGlovesRarity = null; storage.equipmentGlovesHealthBonus = null; storage.equipmentGlovesDamageBonus = null; storage.equipmentGlovesDefenseBonus = null; // Reset all game state variables currentMap = 1; currentState = 'overworld'; currentDungeon = null; // Reset player character to initial state playerCharacter.level = 1; playerCharacter.experience = 0; playerCharacter.health = 100; playerCharacter.maxHealth = 100; playerCharacter.damage = 12; playerCharacter.defense = 0; playerCharacter.equipment = { helmet: null, boots: null, armor: null, bow: null, necklace: null, ring: null, gloves: null }; playerCharacter.calculateStats(); // Reset gameData to initial state gameData = { level: 1, experience: 0, equipment: { helmet: null, boots: null, armor: null, bow: null, necklace: null, ring: null, gloves: null }, unlockedMaps: 1, completedDungeons: {}, givenLoot: {}, dungeonEntryOrder: {}, npcInteractionCompleted: {} }; // Reset NPC system npcs = []; currentNPC = null; dialogueActive = false; dungeonsDisabled = false; if (dialogueBox) { dialogueBox.destroy(); dialogueBox = null; } if (dialogueText) { dialogueText.destroy(); dialogueText = null; } if (saveButton) { saveButton.destroy(); saveButton = null; } if (ignoreButton) { ignoreButton.destroy(); ignoreButton = null; } storage.npcInteractionCompleted = {}; // Reset all game arrays dungeons = []; arrows = []; enemies = []; lootItems = []; overworldMonsters = []; // Reset all timers and counters lastEnemySpawn = 0; lastArrowShot = 0; lastOverworldMonsterSpawn = 0; lastOverworldArrowShot = 0; combatTimer = 0; enemiesKilled = 0; totalEnemiesSpawned = 0; // Reset energy system currentEnergy = 5; energyRegenTimer = 1; isRegeneratingEnergy = false; wasEnergyDepleted = false; // Reset notification system pendingNotifications = []; notificationTimer = 0; if (currentNotification) { currentNotification.destroy(); currentNotification = null; } // Reset aiming system isAiming = false; if (aimLine) { aimLine.destroy(); aimLine = null; } // Reset drag system draggedCharacter = null; // Update character appearance to default updateCharacterAppearance(); // Setup fresh overworld setupOverworld(); return; } // Check if clicking on character var localPos = playerCharacter.toLocal({ x: x, y: y }); if (localPos.x >= -40 && localPos.x <= 40 && localPos.y >= -40 && localPos.y <= 40) { draggedCharacter = playerCharacter; dragOffset.x = x - playerCharacter.x; dragOffset.y = y - playerCharacter.y; } else { // Start aiming in overworld isAiming = true; aimStartX = x; aimStartY = y; // Create aiming line aimLine = game.attachAsset('aimLine', { anchorX: 0.5, anchorY: 0, x: playerCharacter.x, y: playerCharacter.y }); } } else if (currentState === 'combat' && backButton) { // Check if obj and obj.position exist, otherwise use x,y coordinates var localPos; if (obj && obj.position) { localPos = backButton.toLocal(obj.position); } else { // Convert game coordinates to local coordinates localPos = backButton.toLocal({ x: x, y: y }); } if (localPos.x >= -60 && localPos.x <= 60 && localPos.y >= -30 && localPos.y <= 30) { currentState = 'overworld'; setupOverworld(); } else { // Start aiming if energy available var canAim = currentEnergy > 0 && (!wasEnergyDepleted || currentEnergy === maxEnergy); if (canAim) { isAiming = true; aimStartX = x; aimStartY = y; // Create aiming line aimLine = game.attachAsset('aimLine', { anchorX: 0.5, anchorY: 0, x: playerCharacter.x, y: playerCharacter.y }); } } } }; game.move = function (x, y, obj) { if (currentState === 'overworld' && draggedCharacter) { draggedCharacter.x = x - dragOffset.x; draggedCharacter.y = y - dragOffset.y; // Update health bar position if (healthBarBg) { healthBarBg.x = draggedCharacter.x; healthBarBg.y = draggedCharacter.y - 50; } if (healthBar) { healthBar.x = draggedCharacter.x; healthBar.y = draggedCharacter.y - 50; } if (healthBarText) { healthBarText.x = draggedCharacter.x; healthBarText.y = draggedCharacter.y - 60; } } // Check for loot hover collection in overworld if (currentState === 'overworld') { for (var i = lootItems.length - 1; i >= 0; i--) { var loot = lootItems[i]; var dx = x - loot.x; var dy = y - loot.y; var distance = Math.sqrt(dx * dx + dy * dy); // If mouse is hovering over loot item (within 40 pixels) if (distance <= 40) { // Collect the loot playerCharacter.equipment[loot.type] = loot; playerCharacter.calculateStats(); updateCharacterAppearance(); // Update stats panel createStatsPanel(); // Remove from game loot.destroy(); lootItems.splice(i, 1); saveGameData(); break; } } } // Update aiming line in combat if (currentState === 'combat' && isAiming && aimLine) { var dx = x - aimStartX; var dy = y - aimStartY; var distance = Math.sqrt(dx * dx + dy * dy); // Limit aiming distance if (distance > maxAimDistance) { dx = dx / distance * maxAimDistance; dy = dy / distance * maxAimDistance; distance = maxAimDistance; } // Update aim line position and rotation aimLine.x = playerCharacter.x; aimLine.y = playerCharacter.y; aimLine.height = distance; aimLine.rotation = Math.atan2(dy, dx) + Math.PI / 2; } }; game.up = function (x, y, obj) { if (currentState === 'overworld' && draggedCharacter) { // Check if character is dropped on any dungeon for (var i = 0; i < dungeons.length; i++) { var dungeon = dungeons[i]; if (draggedCharacter.intersects(dungeon)) { currentState = 'combat'; currentDungeon = dungeon; setupCombat(); break; } } // Fire arrow in overworld if aiming if (isAiming) { // Calculate aim direction and power var dx = x - aimStartX; var dy = y - aimStartY; var distance = Math.sqrt(dx * dx + dy * dy); // Only fire if there's sufficient drag distance if (distance > 20) { // Limit maximum distance for power calculation if (distance > maxAimDistance) { dx = dx / distance * maxAimDistance; dy = dy / distance * maxAimDistance; distance = maxAimDistance; } // Calculate arrow velocity based on aim distance var power = distance / maxAimDistance; var baseSpeed = 8; var arrowSpeed = baseSpeed * (0.5 + power * 0.5); // Create and fire arrow var arrow = new Arrow(); arrow.x = playerCharacter.x; arrow.y = playerCharacter.y; // Set arrow velocity based on aim direction and power arrow.velocityX = dx / distance * arrowSpeed; arrow.velocityY = dy / distance * arrowSpeed; arrows.push(arrow); game.addChild(arrow); LK.getSound('shoot').play(); } // Clean up aiming isAiming = false; if (aimLine) { aimLine.destroy(); aimLine = null; } } draggedCharacter = null; } else if (currentState === 'combat' && isAiming) { // Calculate aim direction and power var dx = x - aimStartX; var dy = y - aimStartY; var distance = Math.sqrt(dx * dx + dy * dy); // Only fire if there's sufficient drag distance if (distance > 20) { // Limit maximum distance for power calculation if (distance > maxAimDistance) { dx = dx / distance * maxAimDistance; dy = dy / distance * maxAimDistance; distance = maxAimDistance; } // Calculate arrow velocity based on aim distance var power = distance / maxAimDistance; var baseSpeed = 8; var arrowSpeed = baseSpeed * (0.5 + power * 0.5); // Speed between 4 and 8 // Create and fire arrow var arrow = new Arrow(); arrow.x = playerCharacter.x; arrow.y = playerCharacter.y; // Set arrow velocity based on aim direction and power arrow.velocityX = dx / distance * arrowSpeed; arrow.velocityY = dy / distance * arrowSpeed; arrows.push(arrow); game.addChild(arrow); LK.getSound('shoot').play(); // Consume energy currentEnergy--; if (currentEnergy === 0) { wasEnergyDepleted = true; } // Reset regeneration timer energyRegenTimer = 0; isRegeneratingEnergy = false; updateEnergyBar(); } // Clean up aiming isAiming = false; if (aimLine) { aimLine.destroy(); aimLine = null; } } }; // Main game update loop game.update = function () { if (currentState === 'overworld') { // Process pending notifications if (pendingNotifications.length > 0 && !currentNotification) { var notification = pendingNotifications.shift(); showNotification(notification); } // Spawn overworld monsters more frequently (every 2 seconds) if (LK.ticks - lastOverworldMonsterSpawn > 120) { var monster = new OverworldMonster(); // Initialize targeting flag monster.hasBeenTargeted = false; // Spawn at random position around the edges var spawnSide = Math.floor(Math.random() * 4); if (spawnSide === 0) { // Top monster.x = Math.random() * 2048; monster.y = 200; } else if (spawnSide === 1) { // Right monster.x = 1900; monster.y = 200 + Math.random() * 2332; } else if (spawnSide === 2) { // Bottom monster.x = Math.random() * 2048; monster.y = 2532; } else { // Left monster.x = 100; monster.y = 200 + Math.random() * 2332; } overworldMonsters.push(monster); game.addChild(monster); lastOverworldMonsterSpawn = LK.ticks; } // Auto-shooting system for overworld monsters if (LK.ticks - lastOverworldArrowShot > 30) { // Shoot every 0.5 seconds // Find closest ch23 enemy within 1000 unit range var closestMonster = null; var closestDistance = 1000; // Max range for (var j = 0; j < overworldMonsters.length; j++) { var monster = overworldMonsters[j]; var dx = monster.x - playerCharacter.x; var dy = monster.y - playerCharacter.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= 1000 && distance < closestDistance) { closestMonster = monster; closestDistance = distance; } } // Shoot arrow at closest monster if (closestMonster) { var arrow = new Arrow(); arrow.x = playerCharacter.x; arrow.y = playerCharacter.y; arrow.target = closestMonster; arrow.isHoming = true; arrow.speed = 8; arrows.push(arrow); game.addChild(arrow); LK.getSound('shoot').play(); lastOverworldArrowShot = LK.ticks; } } // Update NPCs for (var i = npcs.length - 1; i >= 0; i--) { var npc = npcs[i]; // NPCs update automatically through their update method } // Update overworld monsters for (var i = overworldMonsters.length - 1; i >= 0; i--) { var monster = overworldMonsters[i]; // Check collision with player if (monster.intersects(playerCharacter)) { playerCharacter.health -= 10; monster.destroy(); overworldMonsters.splice(i, 1); if (playerCharacter.health <= 0) { LK.showGameOver(); } // Update health display healthText.setText('Health: ' + playerCharacter.health + '/' + playerCharacter.maxHealth); updateHealthBar(); continue; } } // Update arrows in overworld for (var i = arrows.length - 1; i >= 0; i--) { var arrow = arrows[i]; // Remove arrows that go off screen if (arrow.x > 2100 || arrow.x < -100 || arrow.y > 2800 || arrow.y < -100) { arrow.destroy(); arrows.splice(i, 1); continue; } // Check collision with overworld monsters for (var j = overworldMonsters.length - 1; j >= 0; j--) { var monster = overworldMonsters[j]; if (arrow.intersects(monster)) { monster.takeDamage(playerCharacter.damage); arrow.destroy(); arrows.splice(i, 1); if (monster.health <= 0) { monster.destroy(); overworldMonsters.splice(j, 1); } break; } } } } else if (currentState === 'combat') { combatTimer++; // Energy regeneration system if (currentEnergy < maxEnergy) { energyRegenTimer++; if (energyRegenTimer >= energyRegenDelay && !isRegeneratingEnergy) { isRegeneratingEnergy = true; } if (isRegeneratingEnergy) { // Regenerate energy over time using tween tween(this, {}, { duration: 200, onFinish: function onFinish() { if (currentEnergy < maxEnergy) { currentEnergy++; updateEnergyBar(); // Continue regenerating if not at max if (currentEnergy < maxEnergy) { energyRegenTimer = energyRegenDelay; } else { isRegeneratingEnergy = false; wasEnergyDepleted = false; // Reset depletion flag when fully regenerated } } } }); isRegeneratingEnergy = false; } } // Arrows are now fired on click instead of automatically // Spawn enemies from right side if (currentDungeon.difficulty === 'veryHard') { // For very hard dungeons, only spawn one boss enemy if (combatTimer - lastEnemySpawn > 120 && totalEnemiesSpawned === 0) { var enemy = new Enemy(currentDungeon.difficulty, 'boss'); enemy.x = 1900; enemy.y = 1800 + 300; enemies.push(enemy); game.addChild(enemy); totalEnemiesSpawned++; lastEnemySpawn = combatTimer; } } else { // Normal spawning for other difficulties if (combatTimer - lastEnemySpawn > 120 && totalEnemiesSpawned < 15) { // Define enemy types available for each difficulty var enemyTypesByDifficulty = { easy: ['basic', 'fast'], normal: ['basic', 'fast', 'tank'], hard: ['basic', 'fast', 'tank', 'archer'] }; var availableTypes = enemyTypesByDifficulty[currentDungeon.difficulty]; var randomType = availableTypes[Math.floor(Math.random() * availableTypes.length)]; var enemy = new Enemy(currentDungeon.difficulty, randomType); enemy.x = 1900; enemy.y = 1800; // Adjust Y position for enemyFast type enemies if (randomType === 'fast') { enemy.y = 1800 - 130; } // Adjust Y position for enemyArcher type enemies if (randomType === 'archer') { enemy.y = 1800 + 100; } // Adjust Y position for enemyTank type enemies if (randomType === 'tank') { enemy.y = 1800 + 200; } enemies.push(enemy); game.addChild(enemy); totalEnemiesSpawned++; lastEnemySpawn = combatTimer; } } // Update arrows for (var i = arrows.length - 1; i >= 0; i--) { var arrow = arrows[i]; // Remove arrows that go off screen in any direction if (arrow.x > 2100 || arrow.x < -100 || arrow.y > 2800 || arrow.y < -100) { arrow.destroy(); arrows.splice(i, 1); continue; } // Check collision with enemies for (var j = enemies.length - 1; j >= 0; j--) { var enemy = enemies[j]; if (arrow.intersects(enemy)) { enemy.takeDamage(playerCharacter.damage); // Add visual feedback with tween tween(enemy.children[0], { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(enemy.children[0], { tint: 0xFFFFFF }, { duration: 200 }); } }); arrow.destroy(); arrows.splice(i, 1); if (enemy.health <= 0) { enemy.destroy(); enemies.splice(j, 1); enemiesKilled++; // Check win condition if (currentDungeon.difficulty === 'veryHard') { // For very hard dungeons, complete when boss is killed if (enemies.length === 0) { completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index); // Check if we should advance to next map after boss defeat if (currentMap < 4) { // Advance to next map currentMap++; gameData.unlockedMaps = Math.max(gameData.unlockedMaps, currentMap); saveGameData(); } LK.setTimeout(function () { currentState = 'overworld'; setupOverworld(); }, 1000); } } else { // For other difficulties, complete when 15 enemies are killed if (enemiesKilled >= targetKills && enemies.length === 0) { completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index); LK.setTimeout(function () { currentState = 'overworld'; setupOverworld(); }, 1000); } } } break; } } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.x < -100) { enemy.destroy(); enemies.splice(i, 1); continue; } // Check if enemy and player are at same X coordinate for damage if (Math.abs(enemy.x - playerCharacter.x) < 50) { // Allow small tolerance for X coordinate matching playerCharacter.health -= Math.max(1, enemy.damage - playerCharacter.defense); enemy.destroy(); enemies.splice(i, 1); // Count enemy as killed even though it damaged the player enemiesKilled++; if (playerCharacter.health <= 0) { playerCharacter.health = 1; currentState = 'overworld'; setupOverworld(); updateHealthBar(); } else { // Check win condition for all difficulties if player survives if (currentDungeon.difficulty === 'veryHard') { // For boss dungeons, complete when boss is killed and advance to next map if (enemies.length === 0) { completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index); // Check if we should advance to next map after boss defeat if (currentMap < 4) { // Advance to next map currentMap++; gameData.unlockedMaps = Math.max(gameData.unlockedMaps, currentMap); saveGameData(); } LK.setTimeout(function () { currentState = 'overworld'; setupOverworld(); }, 1000); } } else { // For non-very-hard dungeons, complete when 15 enemies are killed if (enemiesKilled >= targetKills && enemies.length === 0) { completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index); LK.setTimeout(function () { currentState = 'overworld'; setupOverworld(); }, 1000); } } } updateCombatUI(); updateHealthBar(); } } // Update loot items (simple gravity effect) for (var i = 0; i < lootItems.length; i++) { var loot = lootItems[i]; if (loot.y < 1366) { loot.y += 2; } } } }; // Initialize the game setupOverworld();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Arrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.velocityX = 1; // Default rightward direction
self.velocityY = 0;
self.target = null; // For homing behavior
self.isHoming = false; // Flag to enable homing
self.update = function () {
if (self.isHoming && self.target && self.target.parent) {
// Homing behavior for overworld combat
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.velocityX = dx / distance * self.speed;
self.velocityY = dy / distance * self.speed;
}
}
// Move arrow
self.x += self.velocityX;
self.y += self.velocityY;
// Rotate arrow to face direction of movement
arrowGraphics.rotation = Math.atan2(self.velocityY, self.velocityX);
};
return self;
});
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
});
// Character stats
self.level = 1;
self.experience = 0;
self.health = 100;
self.maxHealth = 100;
self.damage = 12;
self.defense = 0;
// Equipment slots
self.equipment = {
helmet: null,
boots: null,
armor: null,
bow: null,
necklace: null,
ring: null,
gloves: null
};
self.calculateStats = function () {
self.maxHealth = 100 + self.level * 10;
self.damage = 12 + self.level * 2;
self.defense = 0 + self.level * 1;
// Add equipment bonuses
for (var slot in self.equipment) {
if (self.equipment[slot]) {
var item = self.equipment[slot];
self.maxHealth += item.healthBonus || 0;
self.damage += item.damageBonus || 0;
self.defense += item.defenseBonus || 0;
}
}
if (self.health > self.maxHealth) {
self.health = self.maxHealth;
}
// Update health bar if it exists
if (typeof updateHealthBar === 'function') {
updateHealthBar();
}
};
self.gainExperience = function (amount) {
self.experience += amount;
var levelUpThreshold = self.level * 100;
if (self.experience >= levelUpThreshold) {
self.level++;
self.experience -= levelUpThreshold;
self.calculateStats();
self.health = self.maxHealth;
LK.getSound('levelUp').play();
}
// Update level bar if in combat
if (currentState === 'combat') {
updateLevelBar();
}
};
return self;
});
var Dungeon = Container.expand(function (difficulty, index) {
var self = Container.call(this);
// Select dungeon asset based on current map
var dungeonAsset = 'dungeon'; // default for map 1
if (currentMap === 2) {
dungeonAsset = 'dungeon2';
} else if (currentMap === 3) {
dungeonAsset = 'dungeon3';
} else if (currentMap === 4) {
dungeonAsset = 'dungeon4';
}
var dungeonGraphics = self.attachAsset(dungeonAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.difficulty = difficulty;
self.index = index;
self.isHovered = false;
// Difficulty colors
var colors = {
easy: 0x90EE90,
normal: 0xFFD700,
hard: 0xFF8C00,
veryHard: 0xFF4500
};
dungeonGraphics.tint = colors[difficulty];
// Add difficulty text
var difficultyNames = {
easy: 'EASY',
normal: 'NORMAL',
hard: 'HARD',
veryHard: 'BOSS'
};
var difficultyText = new Text2(difficultyNames[difficulty], {
size: 50,
fill: 0xFFFFFF
});
difficultyText.anchor.set(0.5, 1);
difficultyText.x = 0;
difficultyText.y = -80;
self.addChild(difficultyText);
self.down = function (x, y, obj) {
// Don't allow dungeon interaction if NPC dialogue is active or dungeons are disabled
if (dialogueActive || dungeonsDisabled) {
return;
}
// Track entry order for easy, normal and hard dungeons
if (difficulty === 'easy' || difficulty === 'normal' || difficulty === 'hard') {
var orderKey = 'map' + currentMap + '_' + difficulty + '_order';
// Ensure gameData exists and is an object
if (!gameData || _typeof(gameData) !== 'object') {
gameData = {};
}
// Ensure gameData.dungeonEntryOrder exists as an object
if (!gameData.dungeonEntryOrder || _typeof(gameData.dungeonEntryOrder) !== 'object') {
gameData.dungeonEntryOrder = {};
}
// Ensure the specific order key exists as an array - with additional safety checks
try {
if (!gameData.dungeonEntryOrder[orderKey] || !Array.isArray(gameData.dungeonEntryOrder[orderKey])) {
gameData.dungeonEntryOrder[orderKey] = [];
}
// Add this dungeon index to entry order if not already present
if (gameData.dungeonEntryOrder[orderKey].indexOf(index) === -1) {
gameData.dungeonEntryOrder[orderKey].push(index);
}
} catch (e) {
// Fallback: reinitialize dungeonEntryOrder if there's any error
gameData.dungeonEntryOrder = {};
gameData.dungeonEntryOrder[orderKey] = [index];
}
}
currentState = 'combat';
currentDungeon = self;
setupCombat();
};
return self;
});
var Enemy = Container.expand(function (difficulty, enemyType) {
var self = Container.call(this);
// Select appropriate asset based on enemy type and current map
var assetName = 'enemyBasic'; // default
if (enemyType === 'basic') {
if (currentMap === 1) {
assetName = 'enemyBasic';
} else if (currentMap === 2) {
assetName = 'enemyb2';
} else if (currentMap === 3) {
assetName = 'enemyb3';
} else if (currentMap === 4) {
assetName = 'enemyb4';
}
} else if (enemyType === 'fast') {
if (currentMap === 1) {
assetName = 'enemyFast';
} else if (currentMap === 2) {
assetName = 'enemyf2';
} else if (currentMap === 3) {
assetName = 'enemyf3';
} else if (currentMap === 4) {
assetName = 'enemyf4';
}
} else if (enemyType === 'tank') {
if (currentMap === 1) {
assetName = 'enemyTank';
} else if (currentMap === 2) {
assetName = 'enemyt2';
} else if (currentMap === 3) {
assetName = 'enemyt3';
} else if (currentMap === 4) {
assetName = 'enemyt4';
}
} else if (enemyType === 'archer') {
assetName = 'enemyArcher';
} else if (enemyType === 'boss') {
if (currentMap === 1) {
assetName = 'enemyBoss';
} else if (currentMap === 2) {
assetName = 'enemys2';
} else if (currentMap === 3) {
assetName = 'enemys3';
} else if (currentMap === 4) {
assetName = 'enemys4';
}
}
var enemyGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.enemyType = enemyType || 'basic';
self.difficulty = difficulty;
// Base stats for different enemy types
var enemyStats = {
basic: {
health: 50,
speed: 1.6,
tint: 0xFFFFFF,
experience: 10,
damage: 20
},
fast: {
health: 30,
speed: 2.4,
tint: 0x00FF00,
experience: 15,
damage: 20
},
tank: {
health: 80,
speed: 1,
tint: 0xFF0000,
experience: 25,
damage: 30
},
archer: {
health: 40,
speed: 3,
tint: 0x0000FF,
experience: 20,
damage: 50
},
boss: {
health: 300,
speed: 1,
tint: 0xFF00FF,
experience: 50,
damage: 1000
}
};
var stats = enemyStats[self.enemyType];
// Calculate map-based scaling multipliers
var mapMultiplier = 1;
if (currentMap === 2) {
mapMultiplier = 1.2; // +20% for Map 2
} else if (currentMap === 3) {
mapMultiplier = 1.4; // +40% for Map 3
} else if (currentMap === 4) {
mapMultiplier = 1.6; // +60% for Map 4
}
var healthMultiplier = {
easy: 1,
normal: 2,
hard: 4,
veryHard: 8
};
self.maxHealth = Math.floor(stats.health * healthMultiplier[difficulty] * mapMultiplier);
self.health = self.maxHealth;
self.speed = stats.speed;
self.experienceReward = stats.experience;
self.damage = Math.floor(stats.damage * mapMultiplier);
// Apply visual tint based on enemy type
enemyGraphics.tint = stats.tint;
self.update = function () {
self.x -= self.speed;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('enemyDeath').play();
playerCharacter.gainExperience(self.experienceReward);
};
return self;
});
var LootItem = Container.expand(function (type, rarity, x, y) {
var self = Container.call(this);
var lootGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type;
self.rarity = rarity;
// Rarity colors
var rarityColors = {
common: 0x808080,
rare: 0x4169e1,
epic: 0x8a2be2,
legendary: 0xffd700
};
lootGraphics.tint = rarityColors[rarity];
// Generate random stats based on rarity with specific ranges
var statRanges = {
common: {
health: {
min: 1,
max: 19
},
damage: {
min: 1,
max: 9
},
defense: {
min: 1,
max: 4
}
},
rare: {
health: {
min: 20,
max: 49
},
damage: {
min: 7,
max: 19
},
defense: {
min: 4,
max: 9
}
},
epic: {
health: {
min: 50,
max: 79
},
damage: {
min: 15,
max: 29
},
defense: {
min: 10,
max: 17
}
},
legendary: {
health: {
min: 79,
max: 179
},
damage: {
min: 29,
max: 59
},
defense: {
min: 17,
max: 29
}
}
};
var ranges = statRanges[rarity];
self.healthBonus = Math.floor(Math.random() * (ranges.health.max - ranges.health.min + 1)) + ranges.health.min;
self.damageBonus = Math.floor(Math.random() * (ranges.damage.max - ranges.damage.min + 1)) + ranges.damage.min;
self.defenseBonus = Math.floor(Math.random() * (ranges.defense.max - ranges.defense.min + 1)) + ranges.defense.min;
self.x = x;
self.y = y;
self.down = function (x, y, obj) {
collectLoot(self);
};
return self;
});
var NPC = Container.expand(function (mapNumber) {
var self = Container.call(this);
// Select NPC asset based on map
var npcAsset = 'map1man';
if (mapNumber === 2) {
npcAsset = 'map2man';
} else if (mapNumber === 3) {
npcAsset = 'map3man';
} else if (mapNumber === 4) {
npcAsset = 'map4man';
}
var npcGraphics = self.attachAsset(npcAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.mapNumber = mapNumber;
self.speed = 3;
self.isWalking = true;
self.hasReachedPlayer = false;
self.isWalkingAway = false;
self.targetX = 1024; // Player position
self.targetY = 1366;
self.lastX = self.x;
self.update = function () {
if (self.isWalking && !self.hasReachedPlayer) {
// Walk towards player
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 100) {
// Stop when close to player
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
} else {
// Reached player
self.hasReachedPlayer = true;
self.isWalking = false;
showNPCDialogue(self.mapNumber);
}
} else if (self.isWalkingAway) {
// Walk away from player back to spawn point
self.x += self.speed;
if (self.x > 2200) {
// Remove NPC when off screen
self.destroy();
var index = npcs.indexOf(self);
if (index > -1) {
npcs.splice(index, 1);
}
}
}
};
self.walkAway = function () {
self.isWalkingAway = true;
self.isWalking = true;
// Face right when walking away
npcGraphics.scaleX = -1;
};
return self;
});
var OverworldMonster = Container.expand(function () {
var self = Container.call(this);
var monsterGraphics = self.attachAsset('ch23', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.maxHealth = 1;
self.speed = 4; // Slow movement
self.lastX = 0;
self.lastY = 0;
self.update = function () {
// Track last position for collision detection
self.lastX = self.x;
self.lastY = self.y;
// Move slowly towards player
var dx = playerCharacter.x - self.x;
var dy = playerCharacter.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
LK.getSound('enemyDeath').play();
playerCharacter.gainExperience(1);
// Update level bar if in overworld
if (currentState === 'overworld') {
updateLevelBar();
}
// Always heal player by 1 health point when ch23 monster dies, but don't exceed max health
if (playerCharacter.health < playerCharacter.maxHealth) {
playerCharacter.health += 1;
}
// Update health display every time a monster dies
if (typeof updateHealthBar === 'function') {
updateHealthBar();
}
if (healthText && healthText.parent) {
healthText.setText('Health: ' + playerCharacter.health + '/' + playerCharacter.maxHealth);
}
// Store monster position for ring drop
var monsterX = self.x;
var monsterY = self.y;
// Ring drop chances
var randomChance = Math.random();
if (randomChance < 0.000001) {
// 0.00000001 chance for legendary ring
createRingDrop('legendary', monsterX, monsterY);
} else if (randomChance < 0.00001) {
// 0.0000001 chance for epic ring
createRingDrop('epic', monsterX, monsterY);
} else if (randomChance < 0.0001) {
// 0.000001 chance for rare ring
createRingDrop('rare', monsterX, monsterY);
} else if (randomChance < 0.001) {
// 0.00001 chance for common ring
createRingDrop('common', monsterX, monsterY);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state
// Character and world assets
// Combat assets
// Loot assets
// UI assets
// Sounds
80;
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var currentState = 'overworld'; // 'overworld', 'combat', 'inventory'
var currentMap = 1;
var currentDungeon = null;
var playerCharacter = new Character();
var dungeons = [];
var arrows = [];
var enemies = [];
var lootItems = [];
var overworldMonsters = [];
var npcs = [];
var currentNPC = null;
var dialogueActive = false;
var dialogueBox = null;
var dialogueText = null;
var saveButton = null;
var ignoreButton = null;
var dungeonsDisabled = false;
var lastEnemySpawn = 0;
var lastArrowShot = 0;
var lastOverworldMonsterSpawn = 0;
var lastOverworldArrowShot = 0;
var combatTimer = 0;
var enemiesKilled = 0;
var targetKills = 15;
var totalEnemiesSpawned = 0;
var maxEnergy = 5;
var currentEnergy = 5;
var energyRegenTimer = 1;
var energyRegenDelay = 50; // 0.3 seconds at 60fps
var isRegeneratingEnergy = false;
var wasEnergyDepleted = false;
var energyBar = null;
var energyBarBg = null;
var levelBarBg = null;
var levelBarFill = null;
var experienceText = null;
var levelBarText = null;
var pendingNotifications = [];
var notificationTimer = 0;
var currentNotification = null;
var healthBar = null;
var healthBarBg = null;
var healthBarText = null;
// Aiming system variables
var isAiming = false;
var aimStartX = 0;
var aimStartY = 0;
var aimLine = null;
var maxAimDistance = 300;
// UI elements
var levelText = new Text2('Level: 1', {
size: 40,
fill: 0xFFFFFF
});
var healthText = new Text2('Health: 100/100', {
size: 40,
fill: 0xFFFFFF
});
var backButton = null;
var statsPanel = null;
var statsText = null;
// Map-specific dialogue messages
var mapDialogues = {
1: "Hello adventurer. Please save our village. The Demon Emperor has created 10 dungeons nearby with different levels of danger. If we don't act fast, monsters inside may break loose!",
2: "Welcome to our town. I am the captain of the town guard.I heard you destroyed the dungeons around the villages connected to our town.Now dungeons have appeared near us as well. Please, help us.",
3: "Hello adventurer, I am the lord of this city.I’ve heard of your heroism, though I’m not sure of your strength.Our city is about to fall victim to the demon lord’s plan.Can you help us?",
4: "I am Arthur, king of the empire. Your fame has spread across the entire realm, adventurer.The demons seek to conquer the empire.If you manage to save us, I will betroth my daughter, Princess Diana, to you."
};
// Initialize storage with defaults
var gameData = {
level: storage.level || 1,
experience: storage.experience || 0,
equipment: {
helmet: null,
boots: null,
armor: null,
bow: null,
necklace: null,
ring: null,
gloves: null
},
unlockedMaps: storage.unlockedMaps || 1,
completedDungeons: storage.completedDungeons || {},
givenLoot: storage.givenLoot || {},
dungeonEntryOrder: {},
npcInteractionCompleted: storage.npcInteractionCompleted || {}
};
// Reconstruct dungeonEntryOrder from flattened storage
for (var key in storage) {
if (key.indexOf('dungeonEntryOrder_') === 0) {
var orderKey = key.substring('dungeonEntryOrder_'.length);
gameData.dungeonEntryOrder[orderKey] = storage[key];
}
}
// Load equipment from storage
if (storage.equipmentHelmetType) {
gameData.equipment.helmet = {
type: storage.equipmentHelmetType,
rarity: storage.equipmentHelmetRarity,
healthBonus: storage.equipmentHelmetHealthBonus,
damageBonus: storage.equipmentHelmetDamageBonus,
defenseBonus: storage.equipmentHelmetDefenseBonus
};
}
if (storage.equipmentBootsType) {
gameData.equipment.boots = {
type: storage.equipmentBootsType,
rarity: storage.equipmentBootsRarity,
healthBonus: storage.equipmentBootsHealthBonus,
damageBonus: storage.equipmentBootsDamageBonus,
defenseBonus: storage.equipmentBootsDefenseBonus
};
}
if (storage.equipmentArmorType) {
gameData.equipment.armor = {
type: storage.equipmentArmorType,
rarity: storage.equipmentArmorRarity,
healthBonus: storage.equipmentArmorHealthBonus,
damageBonus: storage.equipmentArmorDamageBonus,
defenseBonus: storage.equipmentArmorDefenseBonus
};
}
if (storage.equipmentBowType) {
gameData.equipment.bow = {
type: storage.equipmentBowType,
rarity: storage.equipmentBowRarity,
healthBonus: storage.equipmentBowHealthBonus,
damageBonus: storage.equipmentBowDamageBonus,
defenseBonus: storage.equipmentBowDefenseBonus
};
}
if (storage.equipmentNecklaceType) {
gameData.equipment.necklace = {
type: storage.equipmentNecklaceType,
rarity: storage.equipmentNecklaceRarity,
healthBonus: storage.equipmentNecklaceHealthBonus,
damageBonus: storage.equipmentNecklaceDamageBonus,
defenseBonus: storage.equipmentNecklaceDefenseBonus
};
}
if (storage.equipmentRingType) {
gameData.equipment.ring = {
type: storage.equipmentRingType,
rarity: storage.equipmentRingRarity,
healthBonus: storage.equipmentRingHealthBonus,
damageBonus: storage.equipmentRingDamageBonus,
defenseBonus: storage.equipmentRingDefenseBonus
};
}
if (storage.equipmentGlovesType) {
gameData.equipment.gloves = {
type: storage.equipmentGlovesType,
rarity: storage.equipmentGlovesRarity,
healthBonus: storage.equipmentGlovesHealthBonus,
damageBonus: storage.equipmentGlovesDamageBonus,
defenseBonus: storage.equipmentGlovesDefenseBonus
};
}
// Load saved data
playerCharacter.level = gameData.level;
playerCharacter.experience = gameData.experience;
playerCharacter.equipment = gameData.equipment;
playerCharacter.calculateStats();
// Update character appearance based on loaded equipment
updateCharacterAppearance();
// Load dungeon progression
gameData.completedDungeons = storage.completedDungeons || {};
gameData.givenLoot = storage.givenLoot || {};
currentMap = storage.unlockedMaps || 1;
// Setup overworld
function setupOverworld() {
game.removeChildren();
// Add background based on current map
var backgroundAsset = 'mapBackground'; // default for map 1
if (currentMap === 2) {
backgroundAsset = 'backboard2';
} else if (currentMap === 3) {
backgroundAsset = 'backboard3';
} else if (currentMap === 4) {
backgroundAsset = 'backboard4';
}
var background = game.attachAsset(backgroundAsset, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Add character at center
playerCharacter.x = 1024;
playerCharacter.y = 1366;
game.addChild(playerCharacter);
// Create health bar above character
createHealthBar();
// Create stats panel in bottom-right corner
createStatsPanel();
// Check if NPC interaction is needed for this map
var npcKey = 'map' + currentMap;
if (!gameData.npcInteractionCompleted[npcKey]) {
spawnNPC(currentMap);
dungeonsDisabled = true; // Disable dungeons until NPC interaction is complete
} else {
dungeonsDisabled = false;
}
// Create dungeons in isometric grid
dungeons = [];
var allDifficulties = ['easy', 'easy', 'easy', 'easy', 'normal', 'normal', 'normal', 'hard', 'hard', 'veryHard'];
// Function to check if position is valid (at least 300 units away from existing dungeons and 300 from player)
function isValidPosition(x, y, existingDungeons) {
// Check distance from player starting position (1024, 1366)
var playerStartX = 1024;
var playerStartY = 1366;
var distanceFromPlayer = Math.sqrt(Math.pow(x - playerStartX, 2) + Math.pow(y - playerStartY, 2));
if (distanceFromPlayer < 300) {
return false;
}
// Check distance from existing dungeons using Euclidean distance
for (var i = 0; i < existingDungeons.length; i++) {
var dx = x - existingDungeons[i].x;
var dy = y - existingDungeons[i].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 300) {
return false;
}
}
return true;
}
// Create all 10 dungeons but control their interaction based on progression
var difficultyIndices = {
easy: 0,
normal: 0,
hard: 0,
veryHard: 0
};
for (var i = 0; i < allDifficulties.length; i++) {
var difficulty = allDifficulties[i];
var dungeonIndex = difficultyIndices[difficulty];
var completedCount = getCompletedDungeonsForDifficulty(currentMap, difficulty);
var isUnlocked = isDifficultyUnlocked(currentMap, difficulty);
var isCompleted = dungeonIndex < completedCount;
// Skip creating completed dungeons (they should disappear)
if (isCompleted) {
difficultyIndices[difficulty]++;
continue;
}
var dungeon = new Dungeon(difficulty, dungeonIndex);
difficultyIndices[difficulty]++;
// Scale very hard dungeons by 30%
if (difficulty === 'veryHard') {
tween(dungeon, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 0
});
}
// Scale easy dungeons by 38% smaller (0.62x total scale)
if (difficulty === 'easy') {
tween(dungeon, {
scaleX: 0.62,
scaleY: 0.62
}, {
duration: 0
});
}
// Scale hard dungeons by 10%
if (difficulty === 'hard') {
tween(dungeon, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 0
});
}
// Apply visual effects based on lock status
if (!isUnlocked) {
// Dim locked dungeons
dungeon.children[0].tint = 0x666666;
dungeon.children[0].alpha = 0.5;
// Disable interaction for locked dungeons
dungeon.down = null;
}
// Set fixed positions for each dungeon based on difficulty and index
var fixedPositions = {
easy: [{
x: 200,
y: 980
}, {
x: 1300,
y: 1600
}, {
x: 380,
y: 850
}, {
x: 670,
y: 1050
}],
normal: [{
x: 400,
y: 1280
}, {
x: 450,
y: 1650
}, {
x: 1600,
y: 1900
}],
hard: [{
x: 500,
y: 2200
}, {
x: 900,
y: 2000
}],
veryHard: [{
x: 1300,
y: 2400
}]
};
var positions = fixedPositions[difficulty];
if (positions && dungeonIndex < positions.length) {
dungeon.x = positions[dungeonIndex].x;
dungeon.y = positions[dungeonIndex].y;
} else {
// Fallback to safe default position if something goes wrong
dungeon.x = 1024;
dungeon.y = 1500;
}
dungeons.push(dungeon);
game.addChild(dungeon);
}
// Add level bar UI to overworld
levelBarBg = game.attachAsset('levelBarBg', {
anchorX: 0,
anchorY: 0,
x: 50,
y: 2600
});
levelBarFill = game.attachAsset('levelBarFill', {
anchorX: 0,
anchorY: 0,
x: 50,
y: 2600
});
experienceText = new Text2('XP: 0/100', {
size: 30,
fill: 0xFFFFFF
});
experienceText.x = 50;
experienceText.y = 2630;
game.addChild(experienceText);
// Add level text below the level bar
levelBarText = new Text2('Level: ' + playerCharacter.level, {
size: 30,
fill: 0xFFFFFF
});
levelBarText.x = 50;
levelBarText.y = 2660;
game.addChild(levelBarText);
updateLevelBar();
// Add UI
levelText.setText('Level: ' + playerCharacter.level);
levelText.x = 50;
levelText.y = 150;
game.addChild(levelText);
// Add map indicator
var mapText = new Text2('Map: ' + currentMap + ' (' + getMapRarity(currentMap) + ')\nKill the demon rabbits and obtain the ring.', {
size: 40,
fill: 0xFFFFFF
});
mapText.x = 850;
mapText.y = 100;
game.addChild(mapText);
// Add restart admin button
var restartButton = game.attachAsset('backButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 100,
y: 100
});
// Add restart button text
var restartText = new Text2('Restart', {
size: 24,
fill: 0xFFFFFF
});
restartText.anchor.set(0.5, 0.5);
restartText.x = 2048 - 100;
restartText.y = 100;
game.addChild(restartText);
// Next map button functionality removed
}
// Setup combat
function setupCombat() {
game.removeChildren();
// Add combat background based on current map
var combatBackgroundAsset = 'combatBackground'; // default for map 1
if (currentMap === 2) {
combatBackgroundAsset = 'combat2';
} else if (currentMap === 3) {
combatBackgroundAsset = 'combat3';
} else if (currentMap === 4) {
combatBackgroundAsset = 'combat4';
}
var combatBg = game.attachAsset(combatBackgroundAsset, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Position character fixed on left side for side-scrolling
playerCharacter.x = 200;
playerCharacter.y = 1866;
game.addChild(playerCharacter);
// Create health bar above character
createHealthBar();
// Add back button
backButton = game.attachAsset('backButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
y: 50
});
// Reset combat variables
arrows = [];
enemies = [];
lootItems = [];
overworldMonsters = [];
lastEnemySpawn = 0;
lastArrowShot = 0;
lastOverworldMonsterSpawn = 0;
lastOverworldArrowShot = 0;
combatTimer = 0;
enemiesKilled = 0;
targetKills = 15;
totalEnemiesSpawned = 0;
// Add energy bar UI
energyBarBg = game.attachAsset('energyBarBg', {
anchorX: 0,
anchorY: 0,
x: 50,
y: 2000
});
energyBar = game.attachAsset('energyBar', {
anchorX: 0,
anchorY: 0,
x: 50,
y: 2000
});
// Reset energy system
currentEnergy = maxEnergy;
energyRegenTimer = 1;
isRegeneratingEnergy = false;
wasEnergyDepleted = false;
updateEnergyBar();
// Add level bar UI
levelBarBg = game.attachAsset('levelBarBg', {
anchorX: 0,
anchorY: 0,
x: 50,
y: 2050
});
levelBarFill = game.attachAsset('levelBarFill', {
anchorX: 0,
anchorY: 0,
x: 50,
y: 2050
});
experienceText = new Text2('XP: 0/100', {
size: 30,
fill: 0xFFFFFF
});
experienceText.x = 50;
experienceText.y = 2080;
game.addChild(experienceText);
// Add level text below the level bar
levelBarText = new Text2('Level: ' + playerCharacter.level, {
size: 30,
fill: 0xFFFFFF
});
levelBarText.x = 50;
levelBarText.y = 2110;
game.addChild(levelBarText);
updateLevelBar();
// Add UI
updateCombatUI();
}
function updateEnergyBar() {
if (energyBar) {
var energyPercentage = currentEnergy / maxEnergy;
energyBar.width = 300 * energyPercentage;
// Change color based on energy level
if (energyPercentage > 0.6) {
energyBar.tint = 0xFFD700; // Green
} else if (energyPercentage > 0.3) {
energyBar.tint = 0xffff00; // Yellow
} else {
energyBar.tint = 0xff0000; // Red
}
}
}
function updateLevelBar() {
if (levelBarFill && experienceText) {
var levelUpThreshold = playerCharacter.level * 100;
var experiencePercentage = playerCharacter.experience / levelUpThreshold;
levelBarFill.width = 400 * experiencePercentage;
// Update experience text
experienceText.setText('XP: ' + playerCharacter.experience + '/' + levelUpThreshold);
// Update level text
if (levelBarText) {
levelBarText.setText('Level: ' + playerCharacter.level);
}
}
}
function updateHealthBar() {
if (healthBar && healthBarText) {
var healthPercentage = playerCharacter.health / playerCharacter.maxHealth;
healthBar.width = 200 * healthPercentage;
healthBarText.setText(playerCharacter.health + '/' + playerCharacter.maxHealth);
}
}
function createStatsPanel() {
if (statsPanel) {
statsPanel.destroy();
}
if (statsText) {
statsText.destroy();
}
// Create stats panel in bottom-right corner - enlarged to accommodate level bonuses
statsPanel = game.attachAsset('statsPanel', {
anchorX: 1,
anchorY: 1,
x: 2048 - 20,
y: 2732 - 20,
scaleX: 1.2,
scaleY: 1.6
});
// Calculate total bonuses from equipment
var totalHealthBonus = 0;
var totalDamageBonus = 0;
var totalDefenseBonus = 0;
for (var slot in playerCharacter.equipment) {
if (playerCharacter.equipment[slot]) {
var item = playerCharacter.equipment[slot];
totalHealthBonus += item.healthBonus || 0;
totalDamageBonus += item.damageBonus || 0;
totalDefenseBonus += item.defenseBonus || 0;
}
}
// Calculate level bonuses
var levelHealthBonus = playerCharacter.level * 10;
var levelDamageBonus = playerCharacter.level * 2;
var levelDefenseBonus = playerCharacter.level * 1;
// Create stats text with both equipment and level bonuses
var statsContent = 'Equipment Bonuses:\n' + 'Health: +' + totalHealthBonus + '\n' + 'Damage: +' + totalDamageBonus + '\n' + 'Defense: +' + totalDefenseBonus + '\n\n' + 'Level ' + playerCharacter.level + ' Bonuses:\n' + 'Health: +' + levelHealthBonus + '\n' + 'Damage: +' + levelDamageBonus + '\n' + 'Defense: +' + levelDefenseBonus;
statsText = new Text2(statsContent, {
size: 28,
fill: 0xFFFFFF
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 2048 - 260;
statsText.y = 2732 - 180;
game.addChild(statsText);
}
function createHealthBar() {
if (healthBarBg) {
healthBarBg.destroy();
}
if (healthBar) {
healthBar.destroy();
}
if (healthBarText) {
healthBarText.destroy();
}
// Create health bar background
healthBarBg = game.attachAsset('healthBarBg', {
anchorX: 0.5,
anchorY: 0.5,
x: playerCharacter.x,
y: playerCharacter.y - 130
});
// Create health bar fill
healthBar = game.attachAsset('healthBar', {
anchorX: 0.5,
anchorY: 0.5,
x: playerCharacter.x,
y: playerCharacter.y - 130
});
// Create health text
healthBarText = new Text2(playerCharacter.health + '/' + playerCharacter.maxHealth, {
size: 30,
fill: 0xFFFFFF
});
healthBarText.anchor.set(0.5, 1);
healthBarText.x = playerCharacter.x;
healthBarText.y = playerCharacter.y - 150;
game.addChild(healthBarText);
updateHealthBar();
}
function updateCombatUI() {
if (levelText.parent) {
levelText.parent.removeChild(levelText);
}
if (healthText.parent) {
healthText.parent.removeChild(healthText);
}
levelText.setText('Level: ' + playerCharacter.level);
levelText.x = 50;
levelText.y = 150;
game.addChild(levelText);
healthText.setText('Health: ' + playerCharacter.health + '/' + playerCharacter.maxHealth);
healthText.x = 50;
healthText.y = 200;
game.addChild(healthText);
// Update level bar
updateLevelBar();
}
function collectLoot(loot) {
// Equip the item
playerCharacter.equipment[loot.type] = loot;
playerCharacter.calculateStats();
// Update character appearance
updateCharacterAppearance();
// Update stats panel if in overworld
if (currentState === 'overworld') {
createStatsPanel();
}
// Remove from game
loot.destroy();
var index = lootItems.indexOf(loot);
if (index > -1) {
lootItems.splice(index, 1);
}
// Save game data
saveGameData();
updateCombatUI();
}
function getStatRangeForRarity(rarity) {
var statRanges = {
common: {
health: {
min: 1,
max: 19,
range: 19
},
damage: {
min: 1,
max: 9,
range: 9
},
defense: {
min: 1,
max: 4,
range: 4
}
},
rare: {
health: {
min: 20,
max: 49,
range: 30
},
damage: {
min: 7,
max: 19,
range: 13
},
defense: {
min: 4,
max: 9,
range: 6
}
},
epic: {
health: {
min: 50,
max: 79,
range: 30
},
damage: {
min: 15,
max: 29,
range: 15
},
defense: {
min: 10,
max: 17,
range: 8
}
},
legendary: {
health: {
min: 79,
max: 179,
range: 101
},
damage: {
min: 29,
max: 59,
range: 31
},
defense: {
min: 17,
max: 29,
range: 13
}
}
};
return statRanges[rarity];
}
function getMapRarity(mapNumber) {
var rarities = ['common', 'rare', 'epic', 'legendary'];
return rarities[Math.min(mapNumber - 1, 3)];
}
function getDungeonProgressionKey(mapNumber, difficulty) {
return 'map' + mapNumber + '_' + difficulty;
}
function getCompletedDungeonsForDifficulty(mapNumber, difficulty) {
var key = getDungeonProgressionKey(mapNumber, difficulty);
return gameData.completedDungeons[key] || 0;
}
function isDifficultyUnlocked(mapNumber, difficulty) {
if (difficulty === 'easy') {
return true;
}
if (difficulty === 'normal') {
return getCompletedDungeonsForDifficulty(mapNumber, 'easy') >= 4;
}
if (difficulty === 'hard') {
return getCompletedDungeonsForDifficulty(mapNumber, 'normal') >= 3;
}
if (difficulty === 'veryHard') {
return getCompletedDungeonsForDifficulty(mapNumber, 'hard') >= 2;
}
return false;
}
function completeDungeon(mapNumber, difficulty, index) {
var key = getDungeonProgressionKey(mapNumber, difficulty);
gameData.completedDungeons[key] = (gameData.completedDungeons[key] || 0) + 1;
// Give fixed loot based on difficulty and completion order
var lootKey = 'map' + mapNumber + '_' + difficulty + '_' + index;
if (!gameData.givenLoot[lootKey]) {
var loot = getFixedLoot(mapNumber, difficulty, index);
if (loot) {
giveLoot(loot.type, loot.rarity);
gameData.givenLoot[lootKey] = true;
// Auto-equip necklace from hard dungeons
if (difficulty === 'hard' && loot.type === 'necklace') {
playerCharacter.equipment.necklace = {
type: loot.type,
rarity: loot.rarity,
healthBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).health.range) + getStatRangeForRarity(loot.rarity).health.min,
damageBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).damage.range) + getStatRangeForRarity(loot.rarity).damage.min,
defenseBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).defense.range) + getStatRangeForRarity(loot.rarity).defense.min
};
playerCharacter.calculateStats();
updateCharacterAppearance();
}
// Auto-equip bow from very hard dungeons
if (difficulty === 'veryHard' && loot.type === 'bow') {
playerCharacter.equipment.bow = {
type: loot.type,
rarity: loot.rarity,
healthBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).health.range) + getStatRangeForRarity(loot.rarity).health.min,
damageBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).damage.range) + getStatRangeForRarity(loot.rarity).damage.min,
defenseBonus: Math.floor(Math.random() * getStatRangeForRarity(loot.rarity).defense.range) + getStatRangeForRarity(loot.rarity).defense.min
};
playerCharacter.calculateStats();
updateCharacterAppearance();
}
// Add notification for awarded item
pendingNotifications.push({
type: loot.type,
rarity: loot.rarity,
message: 'Awarded: ' + loot.rarity + ' ' + loot.type
});
}
}
// Check if we should advance to next map or win the game
if (difficulty === 'veryHard' && getCompletedDungeonsForDifficulty(mapNumber, 'veryHard') >= 1) {
// Map 4 Very Hard completion wins the game
if (mapNumber === 4) {
LK.showYouWin();
return;
}
gameData.unlockedMaps = Math.max(gameData.unlockedMaps, mapNumber + 1);
currentMap = mapNumber + 1;
if (mapNumber === 1) {
currentMap = 'rare';
}
// Reset dungeon completion and loot tracking for new map
resetMapProgress(mapNumber + 1);
}
saveGameData();
LK.setTimeout(function () {
currentState = 'overworld';
setupOverworld();
}, 1000);
}
function getFixedLoot(mapNumber, difficulty, index) {
var rarity = getMapRarity(mapNumber);
if (difficulty === 'easy') {
// Only the first easy dungeon entered drops helmet
var orderKey = 'map' + mapNumber + '_easy_order';
var entryOrder = gameData.dungeonEntryOrder[orderKey] || [];
var entryPosition = entryOrder.indexOf(index);
// Only the first entered easy dungeon (position 0) drops helmet
if (entryPosition === 0) {
// Set helmet rarity based on map
var helmetRarity = 'common'; // Map 1 default
if (mapNumber === 2) {
helmetRarity = 'rare';
} else if (mapNumber === 3) {
helmetRarity = 'epic';
} else if (mapNumber === 4) {
helmetRarity = 'legendary';
}
return {
type: 'helmet',
rarity: helmetRarity
};
}
// Other easy dungeons drop nothing
return null;
} else if (difficulty === 'normal') {
var orderKey = 'map' + mapNumber + '_normal_order';
var entryOrder = gameData.dungeonEntryOrder[orderKey] || [];
var entryPosition = entryOrder.indexOf(index);
if (entryPosition === 0) {
return {
type: 'boots',
rarity: rarity
};
}
if (entryPosition === 1) {
return {
type: 'gloves',
rarity: rarity
};
}
return null;
} else if (difficulty === 'hard') {
var orderKey = 'map' + mapNumber + '_hard_order';
var entryOrder = gameData.dungeonEntryOrder[orderKey] || [];
var entryPosition = entryOrder.indexOf(index);
var availableAssets = ['armor', 'necklace'];
if (entryPosition === 0 && availableAssets.includes('armor')) {
return {
type: 'armor',
rarity: rarity
};
}
if (entryPosition === 1 && availableAssets.includes('necklace')) {
return {
type: 'necklace',
rarity: rarity
};
}
return null;
} else if (difficulty === 'veryHard') {
if (mapNumber === 1) {
return {
type: 'bow',
rarity: 'rare'
};
}
}
return null;
}
function createRingDrop(rarity, x, y) {
// Create a proper loot item with stats
var statRanges = {
common: {
health: {
min: 1,
max: 19
},
damage: {
min: 1,
max: 9
},
defense: {
min: 1,
max: 4
}
},
rare: {
health: {
min: 20,
max: 49
},
damage: {
min: 7,
max: 19
},
defense: {
min: 4,
max: 9
}
},
epic: {
health: {
min: 50,
max: 79
},
damage: {
min: 15,
max: 29
},
defense: {
min: 10,
max: 17
}
},
legendary: {
health: {
min: 79,
max: 179
},
damage: {
min: 29,
max: 59
},
defense: {
min: 17,
max: 29
}
}
};
var ranges = statRanges[rarity];
var loot = {
type: 'ring',
rarity: rarity,
healthBonus: Math.floor(Math.random() * (ranges.health.max - ranges.health.min + 1)) + ranges.health.min,
damageBonus: Math.floor(Math.random() * (ranges.damage.max - ranges.damage.min + 1)) + ranges.damage.min,
defenseBonus: Math.floor(Math.random() * (ranges.defense.max - ranges.defense.min + 1)) + ranges.defense.min
};
// Create visual loot item at monster death position
var visualLoot = new LootItem('ring', rarity, x, y);
lootItems.push(visualLoot);
game.addChild(visualLoot);
LK.getSound('lootDrop').play();
}
function giveLoot(type, rarity) {
// Create a proper loot item with stats
var statRanges = {
common: {
health: {
min: 1,
max: 19
},
damage: {
min: 1,
max: 9
},
defense: {
min: 1,
max: 4
}
},
rare: {
health: {
min: 20,
max: 49
},
damage: {
min: 7,
max: 19
},
defense: {
min: 4,
max: 9
}
},
epic: {
health: {
min: 50,
max: 79
},
damage: {
min: 15,
max: 29
},
defense: {
min: 10,
max: 17
}
},
legendary: {
health: {
min: 79,
max: 179
},
damage: {
min: 29,
max: 59
},
defense: {
min: 17,
max: 29
}
}
};
var ranges = statRanges[rarity];
var loot = {
type: type,
rarity: rarity,
healthBonus: Math.floor(Math.random() * (ranges.health.max - ranges.health.min + 1)) + ranges.health.min,
damageBonus: Math.floor(Math.random() * (ranges.damage.max - ranges.damage.min + 1)) + ranges.damage.min,
defenseBonus: Math.floor(Math.random() * (ranges.defense.max - ranges.defense.min + 1)) + ranges.defense.min
};
// If we're in overworld and it's a ring, create a visual loot item
if (currentState === 'overworld' && type === 'ring') {
// Find the last killed monster position or use a default position
var dropX = playerCharacter.x + (Math.random() - 0.5) * 200;
var dropY = playerCharacter.y + (Math.random() - 0.5) * 200;
// Create visual loot item
var visualLoot = new LootItem(type, rarity, dropX, dropY);
lootItems.push(visualLoot);
game.addChild(visualLoot);
} else {
// For other cases or when in combat, equip directly
playerCharacter.equipment[type] = loot;
playerCharacter.calculateStats();
// Update character appearance
updateCharacterAppearance();
}
LK.getSound('lootDrop').play();
}
function resetMapProgress(mapNumber) {
// Don't reset progress, just ensure we're tracking the new map
}
function getCharacterAsset() {
// Count equipped gear pieces (excluding necklace and ring)
var hasHelmet = playerCharacter.equipment.helmet !== null;
var hasBoots = playerCharacter.equipment.boots !== null;
var hasGloves = playerCharacter.equipment.gloves !== null;
var hasArmor = playerCharacter.equipment.armor !== null;
var hasBow = playerCharacter.equipment.bow !== null;
// Get helmet rarity (determines the set's base tier)
var helmetRarity = hasHelmet ? playerCharacter.equipment.helmet.rarity : null;
var bowRarity = hasBow ? playerCharacter.equipment.bow.rarity : null;
// If no helmet, use default character
if (!hasHelmet) {
return 'character';
}
// Define rarity levels
var rarityLevels = {
'common': 0,
'rare': 1,
'epic': 2,
'legendary': 3
};
var helmetLevel = rarityLevels[helmetRarity];
var bowLevel = bowRarity ? rarityLevels[bowRarity] : -1;
// Map 1: Common set (helmet=common, bow=rare)
if (helmetLevel === 0) {
if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && !hasBow) {
return 'ch1';
}
if (hasHelmet && hasBoots && !hasGloves && !hasArmor && !hasBow) {
return 'ch2';
}
if (hasHelmet && hasBoots && hasGloves && !hasArmor && !hasBow) {
return 'ch3';
}
if (hasHelmet && hasBoots && hasGloves && hasArmor && !hasBow) {
return 'ch4';
}
if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 1) {
return 'ch5';
}
}
// Map 2: Rare set (helmet=rare, bow=epic)
if (helmetLevel === 1) {
if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 2) {
return 'ch6';
}
if (hasHelmet && hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 2) {
return 'ch7';
}
if (hasHelmet && hasBoots && hasGloves && !hasArmor && hasBow && bowLevel === 2) {
return 'ch8';
}
if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 2) {
return 'ch9';
}
if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) {
return 'ch10';
}
}
// Map 3: Epic set (helmet=epic, bow=legendary)
if (helmetLevel === 2) {
if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) {
return 'ch11';
}
if (hasHelmet && hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) {
return 'ch12';
}
if (hasHelmet && hasBoots && hasGloves && !hasArmor && hasBow && bowLevel === 3) {
return 'ch13';
}
if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) {
return 'ch14';
}
if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) {
return 'ch15';
}
}
// Map 4: Legendary set (helmet=legendary, bow=legendary)
if (helmetLevel === 3) {
if (hasHelmet && !hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) {
return 'ch16';
}
if (hasHelmet && hasBoots && !hasGloves && !hasArmor && hasBow && bowLevel === 3) {
return 'ch17';
}
if (hasHelmet && hasBoots && hasGloves && !hasArmor && hasBow && bowLevel === 3) {
return 'ch18';
}
if (hasHelmet && hasBoots && hasGloves && hasArmor && hasBow && bowLevel === 3) {
return 'ch19';
}
}
// Fallback to default character
return 'character';
}
function updateCharacterAppearance() {
// Get the appropriate asset based on equipment
var assetName = getCharacterAsset();
// Remove current character graphics
if (playerCharacter.children.length > 0) {
playerCharacter.removeChildAt(0);
}
// Add new character graphics
var characterGraphics = playerCharacter.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
}
function showNotification(notification) {
if (currentNotification) {
// Remove existing notification
currentNotification.destroy();
currentNotification = null;
}
// Create notification text
var notificationText = new Text2(notification.message, {
size: 60,
fill: 0xFFFFFF
});
notificationText.anchor.set(0.5, 0.5);
notificationText.x = 1024;
notificationText.y = 400;
// Add background for notification
var notificationBg = game.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 400,
scaleX: 8,
scaleY: 1.5
});
notificationBg.tint = 0x000000;
notificationBg.alpha = 0.8;
game.addChild(notificationBg);
game.addChild(notificationText);
currentNotification = {
text: notificationText,
bg: notificationBg,
destroy: function destroy() {
if (this.text.parent) {
this.text.parent.removeChild(this.text);
}
if (this.bg.parent) {
this.bg.parent.removeChild(this.bg);
}
}
};
// Auto-remove after 3 seconds
LK.setTimeout(function () {
if (currentNotification) {
currentNotification.destroy();
currentNotification = null;
}
}, 3000);
}
function spawnNPC(mapNumber) {
// Spawn NPC from right-middle of screen
var npc = new NPC(mapNumber);
npc.x = 2200; // Start from right side off-screen
npc.y = 1366; // Middle height
npcs.push(npc);
game.addChild(npc);
currentNPC = npc;
}
function showNPCDialogue(mapNumber) {
dialogueActive = true;
// Create dialogue box
dialogueBox = game.attachAsset('dialogueBox', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 800
});
dialogueBox.tint = 0x000000;
dialogueBox.alpha = 0.9;
// Create dialogue text
var message = mapDialogues[mapNumber];
// Add safety check for undefined message
if (!message) {
message = "Welcome, adventurer. Please help us with our troubles.";
}
dialogueText = new Text2(message, {
size: 36,
fill: 0xFFFFFF
});
dialogueText.anchor.set(0.5, 0.5);
dialogueText.x = 1024;
dialogueText.y = 800;
game.addChild(dialogueText);
// Wrap text to fit in dialogue box
var maxWidth = 1400;
var words = message.split(' ');
var lines = [];
var currentLine = '';
for (var i = 0; i < words.length; i++) {
var testLine = currentLine + words[i] + ' ';
if (testLine.length * 20 > maxWidth && currentLine !== '') {
lines.push(currentLine);
currentLine = words[i] + ' ';
} else {
currentLine = testLine;
}
}
if (currentLine !== '') {
lines.push(currentLine);
}
dialogueText.setText(lines.join('\n'));
// Create choice buttons
saveButton = game.attachAsset('dialogueButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 724,
y: 1000
});
saveButton.tint = 0x4caf50;
var saveText = new Text2('Save', {
size: 32,
fill: 0xFFFFFF
});
saveText.anchor.set(0.5, 0.5);
saveText.x = 724;
saveText.y = 1000;
game.addChild(saveText);
ignoreButton = game.attachAsset('ignoreButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1324,
y: 1000
});
ignoreButton.tint = 0xf44336;
var ignoreText = new Text2('Ignore', {
size: 32,
fill: 0xFFFFFF
});
ignoreText.anchor.set(0.5, 0.5);
ignoreText.x = 1324;
ignoreText.y = 1000;
game.addChild(ignoreText);
}
function closeDialogue() {
if (dialogueBox) {
dialogueBox.destroy();
dialogueBox = null;
}
if (dialogueText) {
dialogueText.destroy();
dialogueText = null;
}
if (saveButton) {
saveButton.destroy();
saveButton = null;
}
if (ignoreButton) {
ignoreButton.destroy();
ignoreButton = null;
}
// Remove button text elements
var textElements = game.children.filter(function (child) {
return child instanceof Text2 && (child.text === 'Save' || child.text === 'Ignore' || child.text === 'Thank you, adventurer. I await your return with good news.');
});
for (var i = 0; i < textElements.length; i++) {
textElements[i].destroy();
}
dialogueActive = false;
}
function handleNPCChoice(choice) {
if (choice === 'save') {
// Store references to button text elements before destroying buttons
var saveTextElement = null;
var ignoreTextElement = null;
// Find and store references to button text elements
for (var i = 0; i < game.children.length; i++) {
var child = game.children[i];
if (child instanceof Text2) {
if (child.text === 'Save') {
saveTextElement = child;
} else if (child.text === 'Ignore') {
ignoreTextElement = child;
}
}
}
// Show thank you message
closeDialogue();
// Remove button text elements
if (saveTextElement && saveTextElement.parent) {
saveTextElement.destroy();
}
if (ignoreTextElement && ignoreTextElement.parent) {
ignoreTextElement.destroy();
}
// Create thank you dialogue
dialogueBox = game.attachAsset('dialogueBox', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 800
});
dialogueBox.tint = 0x000000;
dialogueBox.alpha = 0.9;
var thankYouText = new Text2('Thank you, adventurer. I await your return with good news.', {
size: 36,
fill: 0xFFFFFF
});
thankYouText.anchor.set(0.5, 0.5);
thankYouText.x = 1024;
thankYouText.y = 800;
game.addChild(thankYouText);
// Auto-close after 3 seconds and enable dungeons
LK.setTimeout(function () {
// Clean up thank you dialogue
if (dialogueBox) {
dialogueBox.destroy();
dialogueBox = null;
}
if (thankYouText && thankYouText.parent) {
thankYouText.destroy();
}
// Mark NPC interaction as completed
var npcKey = 'map' + currentMap;
gameData.npcInteractionCompleted[npcKey] = true;
storage.npcInteractionCompleted = gameData.npcInteractionCompleted;
dungeonsDisabled = false;
// Make NPC walk away
if (currentNPC) {
currentNPC.walkAway();
}
}, 3000);
} else if (choice === 'ignore') {
// End game
LK.showGameOver();
}
}
function saveGameData() {
storage.level = playerCharacter.level;
storage.experience = playerCharacter.experience;
storage.unlockedMaps = gameData.unlockedMaps;
storage.completedDungeons = gameData.completedDungeons;
storage.givenLoot = gameData.givenLoot;
storage.npcInteractionCompleted = gameData.npcInteractionCompleted;
// Flatten dungeonEntryOrder for storage compatibility
for (var key in gameData.dungeonEntryOrder) {
storage['dungeonEntryOrder_' + key] = gameData.dungeonEntryOrder[key];
}
// Save equipment properties individually - only save if equipment exists
if (playerCharacter.equipment.helmet) {
storage.equipmentHelmetType = playerCharacter.equipment.helmet.type;
storage.equipmentHelmetRarity = playerCharacter.equipment.helmet.rarity;
storage.equipmentHelmetHealthBonus = playerCharacter.equipment.helmet.healthBonus;
storage.equipmentHelmetDamageBonus = playerCharacter.equipment.helmet.damageBonus;
storage.equipmentHelmetDefenseBonus = playerCharacter.equipment.helmet.defenseBonus;
} else {
storage.equipmentHelmetType = null;
}
if (playerCharacter.equipment.boots) {
storage.equipmentBootsType = playerCharacter.equipment.boots.type;
storage.equipmentBootsRarity = playerCharacter.equipment.boots.rarity;
storage.equipmentBootsHealthBonus = playerCharacter.equipment.boots.healthBonus;
storage.equipmentBootsDamageBonus = playerCharacter.equipment.boots.damageBonus;
storage.equipmentBootsDefenseBonus = playerCharacter.equipment.boots.defenseBonus;
} else {
storage.equipmentBootsType = null;
}
if (playerCharacter.equipment.armor) {
storage.equipmentArmorType = playerCharacter.equipment.armor.type;
storage.equipmentArmorRarity = playerCharacter.equipment.armor.rarity;
storage.equipmentArmorHealthBonus = playerCharacter.equipment.armor.healthBonus;
storage.equipmentArmorDamageBonus = playerCharacter.equipment.armor.damageBonus;
storage.equipmentArmorDefenseBonus = playerCharacter.equipment.armor.defenseBonus;
} else {
storage.equipmentArmorType = null;
}
if (playerCharacter.equipment.bow) {
storage.equipmentBowType = playerCharacter.equipment.bow.type;
storage.equipmentBowRarity = playerCharacter.equipment.bow.rarity;
storage.equipmentBowHealthBonus = playerCharacter.equipment.bow.healthBonus;
storage.equipmentBowDamageBonus = playerCharacter.equipment.bow.damageBonus;
storage.equipmentBowDefenseBonus = playerCharacter.equipment.bow.defenseBonus;
} else {
storage.equipmentBowType = null;
}
if (playerCharacter.equipment.necklace) {
storage.equipmentNecklaceType = playerCharacter.equipment.necklace.type;
storage.equipmentNecklaceRarity = playerCharacter.equipment.necklace.rarity;
storage.equipmentNecklaceHealthBonus = playerCharacter.equipment.necklace.healthBonus;
storage.equipmentNecklaceDamageBonus = playerCharacter.equipment.necklace.damageBonus;
storage.equipmentNecklaceDefenseBonus = playerCharacter.equipment.necklace.defenseBonus;
} else {
storage.equipmentNecklaceType = null;
}
if (playerCharacter.equipment.ring) {
storage.equipmentRingType = playerCharacter.equipment.ring.type;
storage.equipmentRingRarity = playerCharacter.equipment.ring.rarity;
storage.equipmentRingHealthBonus = playerCharacter.equipment.ring.healthBonus;
storage.equipmentRingDamageBonus = playerCharacter.equipment.ring.damageBonus;
storage.equipmentRingDefenseBonus = playerCharacter.equipment.ring.defenseBonus;
} else {
storage.equipmentRingType = null;
}
if (playerCharacter.equipment.gloves) {
storage.equipmentGlovesType = playerCharacter.equipment.gloves.type;
storage.equipmentGlovesRarity = playerCharacter.equipment.gloves.rarity;
storage.equipmentGlovesHealthBonus = playerCharacter.equipment.gloves.healthBonus;
storage.equipmentGlovesDamageBonus = playerCharacter.equipment.gloves.damageBonus;
storage.equipmentGlovesDefenseBonus = playerCharacter.equipment.gloves.defenseBonus;
} else {
storage.equipmentGlovesType = null;
}
}
// Game input handlers
var draggedCharacter = null;
var dragOffset = {
x: 0,
y: 0
};
game.down = function (x, y, obj) {
if (currentState === 'overworld') {
// Handle NPC dialogue button clicks
if (dialogueActive && saveButton && ignoreButton) {
// Check if clicking on Save button - using direct coordinate comparison
if (x >= 524 && x <= 924 && y >= 960 && y <= 1040) {
handleNPCChoice('save');
return;
}
// Check if clicking on Ignore button - using direct coordinate comparison
if (x >= 1124 && x <= 1524 && y >= 960 && y <= 1040) {
handleNPCChoice('ignore');
return;
}
}
// Next map button functionality removed
// Check if clicking on restart button (positioned at top right)
if (x >= 2048 - 160 && x <= 2048 - 40 && y >= 70 && y <= 130) {
// Reset all storage data to initial values
storage.level = 1;
storage.experience = 0;
storage.unlockedMaps = 1;
storage.completedDungeons = {};
storage.givenLoot = {};
// Clear all flattened dungeonEntryOrder keys
for (var key in storage) {
if (key.indexOf('dungeonEntryOrder_') === 0) {
storage[key] = null;
}
}
// Reset all equipment storage
storage.equipmentHelmetType = null;
storage.equipmentHelmetRarity = null;
storage.equipmentHelmetHealthBonus = null;
storage.equipmentHelmetDamageBonus = null;
storage.equipmentHelmetDefenseBonus = null;
storage.equipmentBootsType = null;
storage.equipmentBootsRarity = null;
storage.equipmentBootsHealthBonus = null;
storage.equipmentBootsDamageBonus = null;
storage.equipmentBootsDefenseBonus = null;
storage.equipmentArmorType = null;
storage.equipmentArmorRarity = null;
storage.equipmentArmorHealthBonus = null;
storage.equipmentArmorDamageBonus = null;
storage.equipmentArmorDefenseBonus = null;
storage.equipmentBowType = null;
storage.equipmentBowRarity = null;
storage.equipmentBowHealthBonus = null;
storage.equipmentBowDamageBonus = null;
storage.equipmentBowDefenseBonus = null;
storage.equipmentNecklaceType = null;
storage.equipmentNecklaceRarity = null;
storage.equipmentNecklaceHealthBonus = null;
storage.equipmentNecklaceDamageBonus = null;
storage.equipmentNecklaceDefenseBonus = null;
storage.equipmentRingType = null;
storage.equipmentRingRarity = null;
storage.equipmentRingHealthBonus = null;
storage.equipmentRingDamageBonus = null;
storage.equipmentRingDefenseBonus = null;
storage.equipmentGlovesType = null;
storage.equipmentGlovesRarity = null;
storage.equipmentGlovesHealthBonus = null;
storage.equipmentGlovesDamageBonus = null;
storage.equipmentGlovesDefenseBonus = null;
// Reset all game state variables
currentMap = 1;
currentState = 'overworld';
currentDungeon = null;
// Reset player character to initial state
playerCharacter.level = 1;
playerCharacter.experience = 0;
playerCharacter.health = 100;
playerCharacter.maxHealth = 100;
playerCharacter.damage = 12;
playerCharacter.defense = 0;
playerCharacter.equipment = {
helmet: null,
boots: null,
armor: null,
bow: null,
necklace: null,
ring: null,
gloves: null
};
playerCharacter.calculateStats();
// Reset gameData to initial state
gameData = {
level: 1,
experience: 0,
equipment: {
helmet: null,
boots: null,
armor: null,
bow: null,
necklace: null,
ring: null,
gloves: null
},
unlockedMaps: 1,
completedDungeons: {},
givenLoot: {},
dungeonEntryOrder: {},
npcInteractionCompleted: {}
};
// Reset NPC system
npcs = [];
currentNPC = null;
dialogueActive = false;
dungeonsDisabled = false;
if (dialogueBox) {
dialogueBox.destroy();
dialogueBox = null;
}
if (dialogueText) {
dialogueText.destroy();
dialogueText = null;
}
if (saveButton) {
saveButton.destroy();
saveButton = null;
}
if (ignoreButton) {
ignoreButton.destroy();
ignoreButton = null;
}
storage.npcInteractionCompleted = {};
// Reset all game arrays
dungeons = [];
arrows = [];
enemies = [];
lootItems = [];
overworldMonsters = [];
// Reset all timers and counters
lastEnemySpawn = 0;
lastArrowShot = 0;
lastOverworldMonsterSpawn = 0;
lastOverworldArrowShot = 0;
combatTimer = 0;
enemiesKilled = 0;
totalEnemiesSpawned = 0;
// Reset energy system
currentEnergy = 5;
energyRegenTimer = 1;
isRegeneratingEnergy = false;
wasEnergyDepleted = false;
// Reset notification system
pendingNotifications = [];
notificationTimer = 0;
if (currentNotification) {
currentNotification.destroy();
currentNotification = null;
}
// Reset aiming system
isAiming = false;
if (aimLine) {
aimLine.destroy();
aimLine = null;
}
// Reset drag system
draggedCharacter = null;
// Update character appearance to default
updateCharacterAppearance();
// Setup fresh overworld
setupOverworld();
return;
}
// Check if clicking on character
var localPos = playerCharacter.toLocal({
x: x,
y: y
});
if (localPos.x >= -40 && localPos.x <= 40 && localPos.y >= -40 && localPos.y <= 40) {
draggedCharacter = playerCharacter;
dragOffset.x = x - playerCharacter.x;
dragOffset.y = y - playerCharacter.y;
} else {
// Start aiming in overworld
isAiming = true;
aimStartX = x;
aimStartY = y;
// Create aiming line
aimLine = game.attachAsset('aimLine', {
anchorX: 0.5,
anchorY: 0,
x: playerCharacter.x,
y: playerCharacter.y
});
}
} else if (currentState === 'combat' && backButton) {
// Check if obj and obj.position exist, otherwise use x,y coordinates
var localPos;
if (obj && obj.position) {
localPos = backButton.toLocal(obj.position);
} else {
// Convert game coordinates to local coordinates
localPos = backButton.toLocal({
x: x,
y: y
});
}
if (localPos.x >= -60 && localPos.x <= 60 && localPos.y >= -30 && localPos.y <= 30) {
currentState = 'overworld';
setupOverworld();
} else {
// Start aiming if energy available
var canAim = currentEnergy > 0 && (!wasEnergyDepleted || currentEnergy === maxEnergy);
if (canAim) {
isAiming = true;
aimStartX = x;
aimStartY = y;
// Create aiming line
aimLine = game.attachAsset('aimLine', {
anchorX: 0.5,
anchorY: 0,
x: playerCharacter.x,
y: playerCharacter.y
});
}
}
}
};
game.move = function (x, y, obj) {
if (currentState === 'overworld' && draggedCharacter) {
draggedCharacter.x = x - dragOffset.x;
draggedCharacter.y = y - dragOffset.y;
// Update health bar position
if (healthBarBg) {
healthBarBg.x = draggedCharacter.x;
healthBarBg.y = draggedCharacter.y - 50;
}
if (healthBar) {
healthBar.x = draggedCharacter.x;
healthBar.y = draggedCharacter.y - 50;
}
if (healthBarText) {
healthBarText.x = draggedCharacter.x;
healthBarText.y = draggedCharacter.y - 60;
}
}
// Check for loot hover collection in overworld
if (currentState === 'overworld') {
for (var i = lootItems.length - 1; i >= 0; i--) {
var loot = lootItems[i];
var dx = x - loot.x;
var dy = y - loot.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If mouse is hovering over loot item (within 40 pixels)
if (distance <= 40) {
// Collect the loot
playerCharacter.equipment[loot.type] = loot;
playerCharacter.calculateStats();
updateCharacterAppearance();
// Update stats panel
createStatsPanel();
// Remove from game
loot.destroy();
lootItems.splice(i, 1);
saveGameData();
break;
}
}
}
// Update aiming line in combat
if (currentState === 'combat' && isAiming && aimLine) {
var dx = x - aimStartX;
var dy = y - aimStartY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Limit aiming distance
if (distance > maxAimDistance) {
dx = dx / distance * maxAimDistance;
dy = dy / distance * maxAimDistance;
distance = maxAimDistance;
}
// Update aim line position and rotation
aimLine.x = playerCharacter.x;
aimLine.y = playerCharacter.y;
aimLine.height = distance;
aimLine.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
};
game.up = function (x, y, obj) {
if (currentState === 'overworld' && draggedCharacter) {
// Check if character is dropped on any dungeon
for (var i = 0; i < dungeons.length; i++) {
var dungeon = dungeons[i];
if (draggedCharacter.intersects(dungeon)) {
currentState = 'combat';
currentDungeon = dungeon;
setupCombat();
break;
}
}
// Fire arrow in overworld if aiming
if (isAiming) {
// Calculate aim direction and power
var dx = x - aimStartX;
var dy = y - aimStartY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only fire if there's sufficient drag distance
if (distance > 20) {
// Limit maximum distance for power calculation
if (distance > maxAimDistance) {
dx = dx / distance * maxAimDistance;
dy = dy / distance * maxAimDistance;
distance = maxAimDistance;
}
// Calculate arrow velocity based on aim distance
var power = distance / maxAimDistance;
var baseSpeed = 8;
var arrowSpeed = baseSpeed * (0.5 + power * 0.5);
// Create and fire arrow
var arrow = new Arrow();
arrow.x = playerCharacter.x;
arrow.y = playerCharacter.y;
// Set arrow velocity based on aim direction and power
arrow.velocityX = dx / distance * arrowSpeed;
arrow.velocityY = dy / distance * arrowSpeed;
arrows.push(arrow);
game.addChild(arrow);
LK.getSound('shoot').play();
}
// Clean up aiming
isAiming = false;
if (aimLine) {
aimLine.destroy();
aimLine = null;
}
}
draggedCharacter = null;
} else if (currentState === 'combat' && isAiming) {
// Calculate aim direction and power
var dx = x - aimStartX;
var dy = y - aimStartY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only fire if there's sufficient drag distance
if (distance > 20) {
// Limit maximum distance for power calculation
if (distance > maxAimDistance) {
dx = dx / distance * maxAimDistance;
dy = dy / distance * maxAimDistance;
distance = maxAimDistance;
}
// Calculate arrow velocity based on aim distance
var power = distance / maxAimDistance;
var baseSpeed = 8;
var arrowSpeed = baseSpeed * (0.5 + power * 0.5); // Speed between 4 and 8
// Create and fire arrow
var arrow = new Arrow();
arrow.x = playerCharacter.x;
arrow.y = playerCharacter.y;
// Set arrow velocity based on aim direction and power
arrow.velocityX = dx / distance * arrowSpeed;
arrow.velocityY = dy / distance * arrowSpeed;
arrows.push(arrow);
game.addChild(arrow);
LK.getSound('shoot').play();
// Consume energy
currentEnergy--;
if (currentEnergy === 0) {
wasEnergyDepleted = true;
}
// Reset regeneration timer
energyRegenTimer = 0;
isRegeneratingEnergy = false;
updateEnergyBar();
}
// Clean up aiming
isAiming = false;
if (aimLine) {
aimLine.destroy();
aimLine = null;
}
}
};
// Main game update loop
game.update = function () {
if (currentState === 'overworld') {
// Process pending notifications
if (pendingNotifications.length > 0 && !currentNotification) {
var notification = pendingNotifications.shift();
showNotification(notification);
}
// Spawn overworld monsters more frequently (every 2 seconds)
if (LK.ticks - lastOverworldMonsterSpawn > 120) {
var monster = new OverworldMonster();
// Initialize targeting flag
monster.hasBeenTargeted = false;
// Spawn at random position around the edges
var spawnSide = Math.floor(Math.random() * 4);
if (spawnSide === 0) {
// Top
monster.x = Math.random() * 2048;
monster.y = 200;
} else if (spawnSide === 1) {
// Right
monster.x = 1900;
monster.y = 200 + Math.random() * 2332;
} else if (spawnSide === 2) {
// Bottom
monster.x = Math.random() * 2048;
monster.y = 2532;
} else {
// Left
monster.x = 100;
monster.y = 200 + Math.random() * 2332;
}
overworldMonsters.push(monster);
game.addChild(monster);
lastOverworldMonsterSpawn = LK.ticks;
}
// Auto-shooting system for overworld monsters
if (LK.ticks - lastOverworldArrowShot > 30) {
// Shoot every 0.5 seconds
// Find closest ch23 enemy within 1000 unit range
var closestMonster = null;
var closestDistance = 1000; // Max range
for (var j = 0; j < overworldMonsters.length; j++) {
var monster = overworldMonsters[j];
var dx = monster.x - playerCharacter.x;
var dy = monster.y - playerCharacter.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= 1000 && distance < closestDistance) {
closestMonster = monster;
closestDistance = distance;
}
}
// Shoot arrow at closest monster
if (closestMonster) {
var arrow = new Arrow();
arrow.x = playerCharacter.x;
arrow.y = playerCharacter.y;
arrow.target = closestMonster;
arrow.isHoming = true;
arrow.speed = 8;
arrows.push(arrow);
game.addChild(arrow);
LK.getSound('shoot').play();
lastOverworldArrowShot = LK.ticks;
}
}
// Update NPCs
for (var i = npcs.length - 1; i >= 0; i--) {
var npc = npcs[i];
// NPCs update automatically through their update method
}
// Update overworld monsters
for (var i = overworldMonsters.length - 1; i >= 0; i--) {
var monster = overworldMonsters[i];
// Check collision with player
if (monster.intersects(playerCharacter)) {
playerCharacter.health -= 10;
monster.destroy();
overworldMonsters.splice(i, 1);
if (playerCharacter.health <= 0) {
LK.showGameOver();
}
// Update health display
healthText.setText('Health: ' + playerCharacter.health + '/' + playerCharacter.maxHealth);
updateHealthBar();
continue;
}
}
// Update arrows in overworld
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
// Remove arrows that go off screen
if (arrow.x > 2100 || arrow.x < -100 || arrow.y > 2800 || arrow.y < -100) {
arrow.destroy();
arrows.splice(i, 1);
continue;
}
// Check collision with overworld monsters
for (var j = overworldMonsters.length - 1; j >= 0; j--) {
var monster = overworldMonsters[j];
if (arrow.intersects(monster)) {
monster.takeDamage(playerCharacter.damage);
arrow.destroy();
arrows.splice(i, 1);
if (monster.health <= 0) {
monster.destroy();
overworldMonsters.splice(j, 1);
}
break;
}
}
}
} else if (currentState === 'combat') {
combatTimer++;
// Energy regeneration system
if (currentEnergy < maxEnergy) {
energyRegenTimer++;
if (energyRegenTimer >= energyRegenDelay && !isRegeneratingEnergy) {
isRegeneratingEnergy = true;
}
if (isRegeneratingEnergy) {
// Regenerate energy over time using tween
tween(this, {}, {
duration: 200,
onFinish: function onFinish() {
if (currentEnergy < maxEnergy) {
currentEnergy++;
updateEnergyBar();
// Continue regenerating if not at max
if (currentEnergy < maxEnergy) {
energyRegenTimer = energyRegenDelay;
} else {
isRegeneratingEnergy = false;
wasEnergyDepleted = false; // Reset depletion flag when fully regenerated
}
}
}
});
isRegeneratingEnergy = false;
}
}
// Arrows are now fired on click instead of automatically
// Spawn enemies from right side
if (currentDungeon.difficulty === 'veryHard') {
// For very hard dungeons, only spawn one boss enemy
if (combatTimer - lastEnemySpawn > 120 && totalEnemiesSpawned === 0) {
var enemy = new Enemy(currentDungeon.difficulty, 'boss');
enemy.x = 1900;
enemy.y = 1800 + 300;
enemies.push(enemy);
game.addChild(enemy);
totalEnemiesSpawned++;
lastEnemySpawn = combatTimer;
}
} else {
// Normal spawning for other difficulties
if (combatTimer - lastEnemySpawn > 120 && totalEnemiesSpawned < 15) {
// Define enemy types available for each difficulty
var enemyTypesByDifficulty = {
easy: ['basic', 'fast'],
normal: ['basic', 'fast', 'tank'],
hard: ['basic', 'fast', 'tank', 'archer']
};
var availableTypes = enemyTypesByDifficulty[currentDungeon.difficulty];
var randomType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
var enemy = new Enemy(currentDungeon.difficulty, randomType);
enemy.x = 1900;
enemy.y = 1800;
// Adjust Y position for enemyFast type enemies
if (randomType === 'fast') {
enemy.y = 1800 - 130;
}
// Adjust Y position for enemyArcher type enemies
if (randomType === 'archer') {
enemy.y = 1800 + 100;
}
// Adjust Y position for enemyTank type enemies
if (randomType === 'tank') {
enemy.y = 1800 + 200;
}
enemies.push(enemy);
game.addChild(enemy);
totalEnemiesSpawned++;
lastEnemySpawn = combatTimer;
}
}
// Update arrows
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
// Remove arrows that go off screen in any direction
if (arrow.x > 2100 || arrow.x < -100 || arrow.y > 2800 || arrow.y < -100) {
arrow.destroy();
arrows.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var enemy = enemies[j];
if (arrow.intersects(enemy)) {
enemy.takeDamage(playerCharacter.damage);
// Add visual feedback with tween
tween(enemy.children[0], {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(enemy.children[0], {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
arrow.destroy();
arrows.splice(i, 1);
if (enemy.health <= 0) {
enemy.destroy();
enemies.splice(j, 1);
enemiesKilled++;
// Check win condition
if (currentDungeon.difficulty === 'veryHard') {
// For very hard dungeons, complete when boss is killed
if (enemies.length === 0) {
completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index);
// Check if we should advance to next map after boss defeat
if (currentMap < 4) {
// Advance to next map
currentMap++;
gameData.unlockedMaps = Math.max(gameData.unlockedMaps, currentMap);
saveGameData();
}
LK.setTimeout(function () {
currentState = 'overworld';
setupOverworld();
}, 1000);
}
} else {
// For other difficulties, complete when 15 enemies are killed
if (enemiesKilled >= targetKills && enemies.length === 0) {
completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index);
LK.setTimeout(function () {
currentState = 'overworld';
setupOverworld();
}, 1000);
}
}
}
break;
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.x < -100) {
enemy.destroy();
enemies.splice(i, 1);
continue;
}
// Check if enemy and player are at same X coordinate for damage
if (Math.abs(enemy.x - playerCharacter.x) < 50) {
// Allow small tolerance for X coordinate matching
playerCharacter.health -= Math.max(1, enemy.damage - playerCharacter.defense);
enemy.destroy();
enemies.splice(i, 1);
// Count enemy as killed even though it damaged the player
enemiesKilled++;
if (playerCharacter.health <= 0) {
playerCharacter.health = 1;
currentState = 'overworld';
setupOverworld();
updateHealthBar();
} else {
// Check win condition for all difficulties if player survives
if (currentDungeon.difficulty === 'veryHard') {
// For boss dungeons, complete when boss is killed and advance to next map
if (enemies.length === 0) {
completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index);
// Check if we should advance to next map after boss defeat
if (currentMap < 4) {
// Advance to next map
currentMap++;
gameData.unlockedMaps = Math.max(gameData.unlockedMaps, currentMap);
saveGameData();
}
LK.setTimeout(function () {
currentState = 'overworld';
setupOverworld();
}, 1000);
}
} else {
// For non-very-hard dungeons, complete when 15 enemies are killed
if (enemiesKilled >= targetKills && enemies.length === 0) {
completeDungeon(currentMap, currentDungeon.difficulty, currentDungeon.index);
LK.setTimeout(function () {
currentState = 'overworld';
setupOverworld();
}, 1000);
}
}
}
updateCombatUI();
updateHealthBar();
}
}
// Update loot items (simple gravity effect)
for (var i = 0; i < lootItems.length; i++) {
var loot = lootItems[i];
if (loot.y < 1366) {
loot.y += 2;
}
}
}
};
// Initialize the game
setupOverworld();
arrow. In-Game asset. 2d. High contrast. No shadows
pixel necklace. In-Game asset. 2d. High contrast. No shadows
mağaranın hafif üstünden görünüşü olsun
brown pixel 2dd game boots. In-Game asset. 2d. High contrast. No shadows
pixel brown game helmet. In-Game asset. 2d. High contrast. No shadows
pixel brown gloves. In-Game asset. 2d. High contrast. No shadows
pixel gold ring for 2d game. In-Game asset. 2d. High contrast. No shadows
blue pixel bow for 2d game archer. In-Game asset. 2d. High contrast. No shadows
brown pixel light armor for 2d games. In-Game asset. 2d. High contrast. No shadows
kahve rengi kask ekle karaktere
karaktere kahverengi bot giydir bileklerine kadar
kahverengi eldiven giydir karaktere
kahverengi hafif zırh giydir karaktere
karakter elinde tuttuğu yayı mavi yap
kaskı mavi yap
adamın sadece ayakakbılarını mavi renge çevirir misin
adamın eldivenlerini mavi renge çevirir misin
koyu kahverengi olan bütün zırhı kaskı yayı ayakkabıyı eldiveni mavi renk yapar mısın
yayı parlak mor renk yapar mısın
kaskıda parlak mor renk yapar mısın
ayakkabıları da parlak mor renk yapar mısın
koyu kahverengi olan bütün zırhı kaskı yayı ayakkabıyı eldiveni parlak mor renk yapar mısın
yayı parlak altın rengi yapar mısın
kaskını parlak altın rengine çevirir misin
ayakkabılarını parlak altın rengine çevirir misin
eldivenlerini parlak altın rengine çevirir misin
koyu kahverengi olan bütün zırhını kaskını ayakkabısını eldivenini parlak yayını altın rengine çevirir misin
eldivenlerini parlak mor renk yapar mısın
demon rabbit with red eyes. In-Game asset. 2d. High contrast. No shadows
sağ üst köşeye çevresinde eski tahta çitler olan çok küçük bir köy ekle
sağ üst köşeye bir kasaba ekle
sağ üst köşeye surla çevrili bir şehir ekle
sağ üstteki şehrin grafik açısından boyutunu çok değiştirmeden belki sadece biraz daha uzaktan bakar gibi ama çok daha gelişmiş bir şehre dönüştürür müsün
bu zindan girişini bir tık daha korkutucu göster mesela etrafındaki taşlarında üstünde kemikler en üst ortadaki taşında kemikten bir hayvvan başı gibi
mağaranın grafik ayarları ve boyutunu bozmadan taşların üzerinden lavlar akıyor gibi gözüksün zindan girişinin ortasındada kırmızı korkutucu bir sihirli geçit olsun
zindan girişi taşlarının üstüne mavi maden damarları gibi çizgiler ekle
kemik görünümünde bir kurt. In-Game asset. 2d. High contrast. No shadows
kemikten bir şovalye istiyorum biraz kambur dursun. In-Game asset. 2d. High contrast. No shadows
yıldırımlarla kaplı bir canavar istiyorum hafif kambur duruşlu biraz zombi gibi. In-Game asset. 2d. High contrast. No shadows
lavlarla kaplı bir canavar istiyorum kambur ve biraz iri yapılı. In-Game asset. 2d. High contrast. No shadows
kemikten oluşsun
yıldırımdan bir kartala dönüştür
sağdan sola doğru uçuyor gibi bir görüntü olsun
sağdan sola yürüyen bir cehennem şeytanı. In-Game asset. 2d. High contrast. No shadows
güçlü ve iri yapılı şeytan kral sağdan sola yürüyormuş gibi. In-Game asset. 2d. High contrast. No shadows
ölümsüz kemikler kralı sağdan sola yürüyor gibi. In-Game asset. 2d. High contrast. No shadows
aslan ve insan karışımı yıldırımla kaplı bir canavar kralı sağdan sola yürüyen. In-Game asset. 2d. High contrast. No shadows
Images Sounds Music Images › map1man map1man Size 200 × 200 Aspect Ratio Preserve Aspect Ratio Shape Box Color #365bb5 bastonlu yaşlı uzun sakallı kambur kel bir dede sağdan sola yürüyen üstünde ortaçağ fakir köylerinde köyün muhtarı kıyafeti olsun. In-Game asset. 2d. High contrast. No shadows. In-Game asset. 2d. High contrast. No shadows
şovalye kıyafetli orta yaşlı bir adam belinde kılıcıyla sağdan sola yürür gibi. In-Game asset. 2d. High contrast. No shadows
bir orta çağ şehrinin soylu dükü görünümünde yap orta yaşlarda sağdan solda yürür gibi. In-Game asset. 2d. High contrast. No shadows
bir imparatorluğun kralı , sağdan sola yürür gibi. In-Game asset. 2d. High contrast. No shadows