User prompt
give asset for when character goes up for image
User prompt
when player goes up give that name for asset to give good image for going up
User prompt
make moving animation like when player goes right look right when moving to left look left when gettin up show behind of him ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make one tiles size 3 time bigger because it seems tiles getting each crossed fix it
User prompt
u did 3x3 tiles but make tiles length 3 times dont forget that
User prompt
make tiles 3x3
User prompt
make per biomes tiles not 2x2 give it 1x1
User prompt
make creatures spawning rate increase a bit
User prompt
i dont wanna seeing the black are i mean out of world part do when we getting there make sure and dont show these black area but do still moving in border
User prompt
remove that out of world asset and when player came nexto world border chage the rotion on screen
User prompt
make out of world tiles for i can change the colour
User prompt
make out of world black area give to asset for making some diffrent
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'indexOf')' in or related to this line: 'if (self.children[j] instanceof Text2 && self.children[j].y === labelY && self.children[j].text.indexOf(cubixTypes[i].name) === 0) {' Line Number: 916
User prompt
make that collection menu on upright of screen make it bigger and give the assets image for changeing
User prompt
change the creatures name on assests menu by what they are with name and elements
User prompt
change the creatures name on section menu by what they are with name and elements
User prompt
change the creatures name on sectin by what they are with name and elements
User prompt
u have to give all creatures image to assets section to can change image
User prompt
make on all biomes have one more own tiles to show
User prompt
in code make pokeball redefine to cubix the are cubix
User prompt
Please fix the bug: 'Uncaught ReferenceError: updatePokeballInventoryText is not defined' in or related to this line: 'updatePokeballInventoryText();' Line Number: 779
User prompt
Please fix the bug: 'ReferenceError: pokeballTypes is not defined' in or related to this line: 'for (var i = 0; i < pokeballTypes.length; i++) {' Line Number: 574
User prompt
remove the pokeballs text and rename it cubix and when we clicked the colection menu show it us
User prompt
Please fix the bug: 'playerPokeballs is not defined' in or related to this line: 'pokeballInventoryText.setText('Pokeballs: ' + 'Basic(' + (playerPokeballs.basic || 0) + ') ' + 'Uncommon(' + (playerPokeballs.uncommon || 0) + ') ' + 'Rare(' + (playerPokeballs.rare || 0) + ') ' + 'Legendary(' + (playerPokeballs.legendary || 0) + ')');' Line Number: 2105
User prompt
make the player need to collect attempting like pokeball and make some lands on the pokeball thing to collect and make it total pokeball system and make basic one uncommon one rare and legendary pokeball and when its getting rarity makes more easier catching chance
/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1", { discoveredCreatures: {}, playerPosition: { x: 10, y: 10 }, unlockedAreas: { starter: true } }); var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Collection class to manage discovered creatures var Collection = Container.expand(function () { var self = Container.call(this); // Collection components self.visible = false; self.background = null; self.scrollContainer = null; self.closeButton = null; // Initialize collection screen self.init = function () { // Create darkened background self.background = self.attachAsset('grassTile', { anchorX: 0, anchorY: 0, width: 2048, height: 2732 }); self.background.alpha = 0.9; self.background.tint = 0x000000; // Create scroll container for creatures self.scrollContainer = new Container(); self.addChild(self.scrollContainer); self.scrollContainer.x = 50; self.scrollContainer.y = 200; // Add title var titleText = new Text2('CREATURE COLLECTION', { size: 80, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0); titleText.x = 1024; titleText.y = 50; self.addChild(titleText); // Create close button self.closeButton = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: 1900, y: 100 }); self.closeButton.interactive = true; // Add X to close button var closeText = new Text2('X', { size: 60, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.x = 1900; closeText.y = 100; self.addChild(closeText); // Hide initially self.visible = false; return self; }; // Show collection screen and populate with discovered creatures self.show = function () { self.visible = true; // Clear existing creatures while (self.scrollContainer.children.length > 0) { self.scrollContainer.removeChild(self.scrollContainer.children[0]); } // Get discovered creatures from storage var discoveredCreatures = storage.discoveredCreatures || {}; var creatureCount = Object.keys(discoveredCreatures).length; // Show count var countText = new Text2('DISCOVERED: ' + creatureCount + ' / 90', { size: 50, fill: 0xFFFFFF }); countText.anchor.set(0.5, 0); countText.x = 1024; countText.y = 150; self.addChild(countText); // Create grid of discovered creatures var gridX = 0; var gridY = 0; var itemsPerRow = 5; var cellSize = 380; // First add "undiscovered" placeholders for all possible combinations var allTypes = ['bug', 'dark', 'dragon', 'electric', 'fairy', 'fighting', 'fire', 'flying', 'ghost', 'grass', 'ground', 'ice', 'normal', 'poison', 'psychic', 'rock', 'steel', 'water']; var allRarities = ['common', 'uncommon', 'rare', 'epic', 'legendary']; for (var t = 0; t < allTypes.length; t++) { for (var r = 0; r < allRarities.length; r++) { var type = allTypes[t]; var rarity = allRarities[r]; var key = type + '-' + rarity; // Calculate grid position gridX = (allRarities.length * t + r) % itemsPerRow; gridY = Math.floor((allRarities.length * t + r) / itemsPerRow); // Create cell container var cell = new Container(); cell.x = gridX * cellSize; cell.y = gridY * cellSize; self.scrollContainer.addChild(cell); if (discoveredCreatures[key]) { // Discovered creature var creatureGraphic = LK.getAsset(type + 'Creature', { anchorX: 0.5, anchorY: 0.5, x: cellSize / 2, y: cellSize / 2 - 40 }); // Apply rarity styling var rarityColor = 0xFFFFFF; if (rarity === 'uncommon') rarityColor = 0x00FF00; if (rarity === 'rare') rarityColor = 0x0000FF; if (rarity === 'epic') rarityColor = 0xFF00FF; if (rarity === 'legendary') rarityColor = 0xFFD700; // Make rarer creatures slightly larger var scale = 1.0; if (rarity === 'uncommon') scale = 1.1; if (rarity === 'rare') scale = 1.2; if (rarity === 'epic') scale = 1.3; if (rarity === 'legendary') scale = 1.5; creatureGraphic.scale.set(scale, scale); cell.addChild(creatureGraphic); // Add info text var infoText = new Text2(type.toUpperCase() + '\n' + rarity.toUpperCase(), { size: 30, fill: 0xFFFFFF }); infoText.anchor.set(0.5, 0); infoText.x = cellSize / 2; infoText.y = cellSize / 2 + 40; cell.addChild(infoText); } else { // Undiscovered creature (shadow) var undiscoveredGraphic = LK.getAsset(type + 'Creature', { anchorX: 0.5, anchorY: 0.5, x: cellSize / 2, y: cellSize / 2 }); undiscoveredGraphic.alpha = 0.2; undiscoveredGraphic.tint = 0x000000; cell.addChild(undiscoveredGraphic); // Add question mark var questionText = new Text2('?', { size: 60, fill: 0x999999 }); questionText.anchor.set(0.5, 0.5); questionText.x = cellSize / 2; questionText.y = cellSize / 2; cell.addChild(questionText); } } } }; // Hide collection screen self.hide = function () { self.visible = false; }; return self; }); // Creature class for encounters var Creature = Container.expand(function () { var self = Container.call(this); // --- Creature Database: 100 unique creatures with names, images, and evolutions --- self.creatureList = [ // Example: {id: 0, name: "Sproutle", type: "grass", image: "grassCreature", evolvesTo: 1} // ... 100 creatures, each with unique name, type, image, and evolution chain ]; // Generate 100 unique creatures with types, images, and names (function () { var types = ['bug', 'dark', 'dragon', 'electric', 'fairy', 'fighting', 'fire', 'flying', 'ghost', 'grass', 'ground', 'ice', 'normal', 'poison', 'psychic', 'rock', 'steel', 'water']; var baseNames = ["Sproutle", "Aquapup", "Pyrokit", "Stonox", "Zephyro", "Lumina", "Frosty", "Venoma", "Spectra", "Boulder", "Voltix", "Mystwing", "Shadeon", "Petalyn", "Scorcher", "Chillfin", "Ironclad", "Pebblit", "Gustlet", "Dewdrop", "Emberly", "Thornet", "Mossy", "Duskleaf", "Blazetail", "Crystowl", "Sableye", "Toxifin", "Glimmer", "Rubble", "Cinderpaw", "Leaflet", "Mudkip", "Glacier", "Fanglet", "Wispurr", "Bramble", "Sparky", "Tidelet", "Cobalite", "Garnet", "Quartz", "Onyx", "Topaz", "Opal", "Jade", "Amber", "Ruby", "Sapphire", "Emerald", "Coral", "Shellby", "Ripple", "Torrent", "Breeze", "Nimbus", "Tempest", "Cyclone", "Blizzard", "Icicle", "Flicker", "Glowbug", "Mothra", "Scarab", "Beetle", "Antler", "Staggle", "Hornet", "Buzzly", "Flutter", "Puddle", "Splashy", "Drizzle", "Rainy", "Stormy", "Thunder", "Bolt", "Sparkle", "Shocker", "Zaplet", "Mystic", "Rune", "Oracle", "Wraith", "Shade", "Phantom", "Polter", "Ghast", "Specter", "Spirit", "Bash", "Crush", "Smash", "Pummel", "Thump", "Rumble", "Gravel", "Dusty", "Rocky", "Pebble"]; // Shuffle baseNames for uniqueness for (var i = baseNames.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = baseNames[i]; baseNames[i] = baseNames[j]; baseNames[j] = temp; } // Assign types and images in a round-robin fashion for (var i = 0; i < 100; i++) { var type = types[i % types.length]; var name = baseNames[i % baseNames.length] + (i >= baseNames.length ? String(i) : ""); var image = type + "Creature"; var evolvesTo = i + 1 < 100 && Math.random() < 0.7 ? i + 1 : null; // 70% chance to evolve to next self.creatureList.push({ id: i, name: name, type: type, image: image, evolvesTo: evolvesTo }); } })(); // Creature properties self.creatureId = 0; // Index in creatureList self.type = 'normal'; // Default type self.rarity = 'common'; // Default rarity self.gridX = 0; self.gridY = 0; self.captured = false; self.name = ''; self.image = ''; self.evolvesTo = null; // Rarity colors self.rarityColors = { common: 0xFFFFFF, uncommon: 0x00FF00, rare: 0x0000FF, epic: 0xFF00FF, legendary: 0xFFD700 }; // Type to biome mapping self.typeBiomeMap = { bug: 'forest', dark: 'mountain', dragon: 'mountain', electric: 'grass', fairy: 'forest', fighting: 'mountain', fire: 'desert', flying: 'grass', ghost: 'mountain', grass: 'grass', ground: 'desert', ice: 'mountain', normal: 'grass', poison: 'forest', psychic: 'grass', rock: 'mountain', steel: 'urban', water: 'water' }; // Initialize creature with specific id, or type/rarity/position self.init = function (type, rarity, gridX, gridY, creatureId) { // If creatureId is provided, use it if (typeof creatureId === "number" && self.creatureList[creatureId]) { var c = self.creatureList[creatureId]; self.creatureId = c.id; self.type = c.type; self.name = c.name; self.image = c.image; self.evolvesTo = c.evolvesTo; } else { // Pick a random creature for the biome var candidates = []; for (var i = 0; i < self.creatureList.length; i++) { if (!type || self.creatureList[i].type === type) { candidates.push(self.creatureList[i]); } } var c = candidates[Math.floor(Math.random() * candidates.length)]; self.creatureId = c.id; self.type = c.type; self.name = c.name; self.image = c.image; self.evolvesTo = c.evolvesTo; } self.rarity = rarity || self.getRandomRarity(); self.gridX = gridX; self.gridY = gridY; // Create creature graphic var creatureGraphics = self.attachAsset(self.image, { anchorX: 0.5, anchorY: 0.5 }); // Apply rarity visual effect (glow or color) var rarityColor = self.rarityColors[self.rarity] || 0xFFFFFF; // Add glow effect for rare+ creatures if (self.rarity !== 'common') { // Make rarer creatures slightly larger var scale = 1.0; if (self.rarity === 'uncommon') scale = 1.1; if (self.rarity === 'rare') scale = 1.2; if (self.rarity === 'epic') scale = 1.3; if (self.rarity === 'legendary') scale = 1.5; creatureGraphics.scale.set(scale, scale); } // Position creature self.x = self.gridX * TILE_SIZE + TILE_SIZE / 2; // Center of tile self.y = self.gridY * TILE_SIZE + TILE_SIZE / 2; // Center of tile // Add name label var nameText = new Text2(self.name, { size: 36, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0); nameText.x = 0; nameText.y = 60; self.addChild(nameText); // If this creature can evolve, show evolution arrow if (self.evolvesTo !== null && typeof self.creatureList[self.evolvesTo] !== "undefined") { var evoText = new Text2("Evolves to: " + self.creatureList[self.evolvesTo].name, { size: 24, fill: 0xFFD700 }); evoText.anchor.set(0.5, 0); evoText.x = 0; evoText.y = 100; self.addChild(evoText); } return self; }; // Generate a random creature type, weighted by biome self.getRandomType = function (biome) { // Default types for each biome var biomeTypes = { grass: ['normal', 'grass', 'bug', 'flying', 'electric', 'psychic'], water: ['water', 'ice', 'flying'], desert: ['ground', 'fire', 'rock'], forest: ['bug', 'grass', 'poison', 'fairy'], mountain: ['rock', 'fighting', 'dragon', 'dark', 'ghost', 'ice'], street: ['steel', 'electric', 'poison', 'normal', 'fighting'], urban: ['steel', 'electric', 'normal', 'psychic'] }; // Get types for the provided biome, or use all types var types = biome ? biomeTypes[biome] : Object.keys(self.typeBiomeMap); // Select random type from available options return types[Math.floor(Math.random() * types.length)]; }; // Generate a random rarity based on configured probabilities self.getRandomRarity = function () { var rand = Math.random(); if (rand < 0.65) return 'common'; if (rand < 0.85) return 'uncommon'; if (rand < 0.95) return 'rare'; if (rand < 0.99) return 'epic'; return 'legendary'; }; // Animate creature during encounter self.animate = function () { // Bounce animation var originalY = self.y; // Use tween to create a bouncing effect tween(self, { y: originalY - 20 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { y: originalY }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { // Repeat animation self.animate(); } }); } }); }; // Stop animations self.stopAnimation = function () { tween.stop(self, { y: true }); }; // Evolve this creature to its next form, if possible self.evolve = function () { if (self.evolvesTo !== null && typeof self.creatureList[self.evolvesTo] !== "undefined") { var next = self.creatureList[self.evolvesTo]; // Remove all children (graphics, name, evo text) while (self.children.length > 0) self.removeChild(self.children[0]); self.creatureId = next.id; self.type = next.type; self.name = next.name; self.image = next.image; self.evolvesTo = next.evolvesTo; // Re-add graphics and labels var creatureGraphics = self.attachAsset(self.image, { anchorX: 0.5, anchorY: 0.5 }); creatureGraphics.scale.set(1.2, 1.2); var nameText = new Text2(self.name, { size: 36, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0); nameText.x = 0; nameText.y = 60; self.addChild(nameText); if (self.evolvesTo !== null && typeof self.creatureList[self.evolvesTo] !== "undefined") { var evoText = new Text2("Evolves to: " + self.creatureList[self.evolvesTo].name, { size: 24, fill: 0xFFD700 }); evoText.anchor.set(0.5, 0); evoText.x = 0; evoText.y = 100; self.addChild(evoText); } } }; return self; }); // Directional pad control for player movement var DPad = Container.expand(function () { var self = Container.call(this); // DPad components self.padBase = null; self.upButton = null; self.downButton = null; self.leftButton = null; self.rightButton = null; // Initialize dpad with buttons self.init = function () { // Create base of DPad self.padBase = self.attachAsset('dpad', { anchorX: 0.5, anchorY: 0.5 }); // Create directional buttons self.upButton = self.createButton(0, -65); self.downButton = self.createButton(0, 65); self.leftButton = self.createButton(-65, 0); self.rightButton = self.createButton(65, 0); // Position the entire DPad in the bottom left of the screen self.x = 200; self.y = 2500; return self; }; // Create a directional button at relative x,y from center self.createButton = function (relX, relY) { var button = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: relX, y: relY }); button.interactive = true; return button; }; return self; }); // Encounter class to handle creature encounters var Encounter = Container.expand(function () { var self = Container.call(this); // Encounter state self.active = false; self.creature = null; self.captureRing = null; self.captureTarget = null; self.targetSpeed = 5; self.targetDirection = 1; self.captureAttempts = 0; self.maxAttempts = 3; // Initialize encounter screen self.init = function () { // Darkened background var bg = self.attachAsset('grassTile', { anchorX: 0, anchorY: 0, width: 2048, height: 2732 }); bg.alpha = 0.7; bg.tint = 0x000000; // Initially hidden self.visible = false; return self; }; // Start encounter with a specific creature self.startEncounter = function (creature) { self.active = true; self.visible = true; self.creature = creature; self.captureAttempts = 0; // --- Pokeball selection UI --- self.selectedPokeball = 'basic'; // Default // Only allow selection of pokeballs the player has function getAvailablePokeballs() { var arr = []; for (var i = 0; i < pokeballTypes.length; i++) { if (playerPokeballs[pokeballTypes[i].rarity] > 0) arr.push(pokeballTypes[i]); } return arr; } var availableBalls = getAvailablePokeballs(); if (availableBalls.length === 0) { // No pokeballs, cannot attempt capture var noBallText = new Text2('No Pokeballs!\nFind more to catch creatures.', { size: 70, fill: 0xff0000 }); noBallText.anchor.set(0.5, 0.5); noBallText.x = 1024; noBallText.y = 1500; self.addChild(noBallText); LK.setTimeout(function () { self.endEncounter(); }, 1800); return; } // Show pokeball selection buttons self.pokeballButtons = []; for (var i = 0; i < pokeballTypes.length; i++) { var pType = pokeballTypes[i]; var btn = self.attachAsset('captureRing', { anchorX: 0.5, anchorY: 0.5, x: 700 + i * 200, y: 1600 }); btn.scale.set(0.3, 0.3); btn.tint = pType.color; btn.interactive = true; (function (type) { btn.down = function () { self.selectedPokeball = type.rarity; // Highlight selected for (var j = 0; j < self.pokeballButtons.length; j++) { self.pokeballButtons[j].alpha = self.pokeballButtons[j].rarity === type.rarity ? 1.0 : 0.5; } }; })(pType); btn.rarity = pType.rarity; if (playerPokeballs[pType.rarity] <= 0) btn.alpha = 0.2; self.addChild(btn); self.pokeballButtons.push(btn); // Add label var label = new Text2(pType.name + ' (' + (playerPokeballs[pType.rarity] || 0) + ')', { size: 30, fill: pType.color }); label.anchor.set(0.5, 0); label.x = 700 + i * 200; label.y = 1670; self.addChild(label); if (i === 0) btn.alpha = 1.0;else btn.alpha = 0.5; } // Add creature to encounter screen self.addChild(creature); creature.x = 1024; // Center horizontally creature.y = 1000; // Position in upper portion of screen // Make creature interactive to allow clicking directly on it creature.interactive = true; creature.down = function () { self.attemptCapture(); }; // Start creature animation creature.animate(); // Create capture interface self.createCaptureInterface(); // Play encounter sound LK.getSound('encounter').play(); }; // Create the capture ring and target interface self.createCaptureInterface = function () { // Capture ring self.captureRing = self.attachAsset('captureRing', { anchorX: 0.5, anchorY: 0.5, x: 1024, // Center horizontally y: 1800 // Lower portion of screen }); // Moving target self.captureTarget = self.attachAsset('captureTarget', { anchorX: 0.5, anchorY: 0.5, x: 964, // Start position (left side of ring) y: 1800 // Same Y as ring }); // Capture button var captureButton = self.attachAsset('grassTile', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2200, width: 300, height: 100 }); captureButton.tint = 0xFF0000; // Capture button text var captureText = new Text2('CAPTURE', { size: 60, fill: 0xFFFFFF }); captureText.anchor.set(0.5, 0.5); captureText.x = 1024; captureText.y = 2200; self.addChild(captureText); // Capture button event captureButton.interactive = true; captureButton.down = function () { self.attemptCapture(); }; // Leave button var leaveButton = self.attachAsset('grassTile', { anchorX: 0.5, anchorY: 0.5, x: 1700, y: 2200, width: 300, height: 100 }); leaveButton.tint = 0x888888; var leaveText = new Text2('LEAVE', { size: 60, fill: 0xFFFFFF }); leaveText.anchor.set(0.5, 0.5); leaveText.x = 1700; leaveText.y = 2200; self.addChild(leaveText); leaveButton.interactive = true; leaveButton.down = function () { // End encounter immediately, treat as fail/escape self.endEncounter(); }; // Info text about the creature var infoText = new Text2('TYPE: ' + self.creature.type.toUpperCase() + '\n' + 'RARITY: ' + self.creature.rarity.toUpperCase() + '\n' + 'ATTEMPTS: ' + (self.maxAttempts - self.captureAttempts) + ' REMAINING', { size: 50, fill: 0xFFFFFF }); infoText.anchor.set(0.5, 0); infoText.x = 1024; infoText.y = 2300; self.addChild(infoText); }; // Update function for moving the target self.update = function () { if (!self.active || !self.captureTarget) return; // Move target back and forth across the ring self.captureTarget.x += self.targetSpeed * self.targetDirection; // Reverse direction at edges if (self.captureTarget.x > 1084) { // Right edge of ring self.targetDirection = -1; } else if (self.captureTarget.x < 964) { // Left edge of ring self.targetDirection = 1; } // Make target move faster based on creature rarity var speedMultiplier = 1; if (self.creature) { if (self.creature.rarity === 'uncommon') speedMultiplier = 1.2; if (self.creature.rarity === 'rare') speedMultiplier = 1.5; if (self.creature.rarity === 'epic') speedMultiplier = 1.8; if (self.creature.rarity === 'legendary') speedMultiplier = 2.2; } self.targetSpeed = 5 * speedMultiplier; }; // Attempt to capture the creature self.attemptCapture = function () { // Play capture sound LK.getSound('capture').play(); self.captureAttempts++; // Use selected pokeball, reduce inventory var selectedType = pokeballTypes[0]; for (var i = 0; i < pokeballTypes.length; i++) { if (pokeballTypes[i].rarity === self.selectedPokeball) { selectedType = pokeballTypes[i]; break; } } if (playerPokeballs[selectedType.rarity] > 0) { playerPokeballs[selectedType.rarity]--; updatePokeballInventoryText(); // Update pokeball button labels if (self.pokeballButtons) { for (var i = 0; i < self.pokeballButtons.length; i++) { var labelY = 1670; var label = null; for (var j = 0; j < self.children.length; j++) { if (self.children[j] instanceof Text2 && self.children[j].y === labelY && self.children[j].text.indexOf(pokeballTypes[i].name) === 0) { label = self.children[j]; break; } } if (label) label.setText(pokeballTypes[i].name + ' (' + (playerPokeballs[pokeballTypes[i].rarity] || 0) + ')'); if (playerPokeballs[pokeballTypes[i].rarity] <= 0) self.pokeballButtons[i].alpha = 0.2; } } } // Check if target is in center zone (successful capture) var distanceFromCenter = Math.abs(self.captureTarget.x - 1024); var successZone = 15; // Size of "perfect" zone // Adjust success zone based on rarity (harder for rarer creatures) if (self.creature.rarity === 'uncommon') successZone = 13; if (self.creature.rarity === 'rare') successZone = 10; if (self.creature.rarity === 'epic') successZone = 8; if (self.creature.rarity === 'legendary') successZone = 5; var captured = false; if (distanceFromCenter <= successZone) { // Use pokeball catch bonus var baseChance = 0.5; if (self.creature.rarity === 'common') baseChance = 0.5; if (self.creature.rarity === 'uncommon') baseChance = 0.35; if (self.creature.rarity === 'rare') baseChance = 0.2; if (self.creature.rarity === 'epic') baseChance = 0.1; if (self.creature.rarity === 'legendary') baseChance = 0.05; var finalChance = baseChance + (selectedType.catchBonus || 0); if (Math.random() < finalChance) captured = true; } if (captured) { // Success! self.captureSuccess(); } else if (self.captureAttempts >= self.maxAttempts) { // Failed after max attempts self.captureFail(); } else { // Update attempts remaining text var remainingText = self.children.filter(function (child) { return child instanceof Text2 && child.y === 2300; })[0]; if (remainingText) { remainingText.setText('TYPE: ' + self.creature.type.toUpperCase() + '\n' + 'RARITY: ' + self.creature.rarity.toUpperCase() + '\n' + 'ATTEMPTS: ' + (self.maxAttempts - self.captureAttempts) + ' REMAINING'); } } }; // Handle successful capture self.captureSuccess = function () { // Play success sound LK.getSound('success').play(); // Add to collection if (!storage.discoveredCreatures) { storage.discoveredCreatures = {}; } var creatureKey = self.creature.type + '-' + self.creature.rarity; storage.discoveredCreatures[creatureKey] = true; // Mark the tile as caught so it cannot be encountered again until respawn if (typeof gameMap !== "undefined" && typeof player !== "undefined") { var tile = gameMap.getTileAt(player.gridX, player.gridY); if (tile) { tile._creatureCaught = true; tile.hasCreature = false; // Remove creature visual if present if (tile._creatureVisual) { tile.removeChild(tile._creatureVisual); tile._creatureVisual = null; } // Remove encounterIndicator if present (yellow circle) for (var i = tile.children.length - 1; i >= 0; i--) { if (tile.children[i].assetId === 'encounterIndicator') { tile.removeChild(tile.children[i]); } } } } // Show success message var successText = new Text2('CAPTURED!', { size: 100, fill: 0x00FF00 }); successText.anchor.set(0.5, 0.5); successText.x = 1024; successText.y = 1400; self.addChild(successText); // Flash screen LK.effects.flashScreen(0x00FF00, 500); // End encounter after delay LK.setTimeout(function () { self.endEncounter(); }, 1500); }; // Handle failed capture self.captureFail = function () { // Show fail message var failText = new Text2('ESCAPED!', { size: 100, fill: 0xFF0000 }); failText.anchor.set(0.5, 0.5); failText.x = 1024; failText.y = 1400; self.addChild(failText); // Flash screen LK.effects.flashScreen(0xFF0000, 500); // Despawn the creature from the map if present if (typeof gameMap !== "undefined" && typeof player !== "undefined") { var tile = gameMap.getTileAt(player.gridX, player.gridY); if (tile) { tile.hasCreature = false; tile._creatureCaught = false; // Not caught, but despawned // Remove indicator if present for (var i = tile.children.length - 1; i >= 0; i--) { if (tile.children[i].assetId === 'encounterIndicator') { tile.removeChild(tile.children[i]); } } // Remove from mapCreatures/mapCreatureTiles/mapCreatureLabels if present if (typeof mapCreatureTiles !== "undefined") { for (var k = mapCreatureTiles.length - 1; k >= 0; k--) { if (mapCreatureTiles[k] === tile) { if (mapCreatureLabels[k]) { tile.removeChild(mapCreatureLabels[k]); mapCreatureLabels.splice(k, 1); } mapCreatures.splice(k, 1); mapCreatureTiles.splice(k, 1); } } } } } // End encounter after delay LK.setTimeout(function () { self.endEncounter(); }, 1500); }; // End the current encounter self.endEncounter = function () { if (self.creature) { self.creature.stopAnimation(); } self.active = false; self.visible = false; // Remove all children while (self.children.length > 0) { self.removeChild(self.children[0]); } self.creature = null; self.captureRing = null; self.captureTarget = null; // Reinitialize for next encounter self.init(); // Also set encounterActive to false so game resumes if (typeof encounterActive !== "undefined") { encounterActive = false; } }; return self; }); // GameMap represents the entire game map with different biomes var GameMap = Container.expand(function () { var self = Container.call(this); self.mapWidth = 60; // Number of tiles horizontally (max size, increased) self.mapHeight = 60; // Number of tiles vertically (max size, increased) self.tileSize = TILE_SIZE; // Size of each tile in pixels (increased to match TILE_SIZE) self.tiles = []; // 2D array to hold all map tiles // Biome generation parameters self.biomeTypes = ['grass', 'water', 'desert', 'forest', 'mountain', 'street', 'urban']; self.biomeSeeds = []; // Points from which biomes spread // Initialize map with biomes self.initMap = function () { // Create empty 2D array for tiles for (var y = 0; y < self.mapHeight; y++) { self.tiles[y] = []; for (var x = 0; x < self.mapWidth; x++) { self.tiles[y][x] = null; } } // Place biome seeds self.placeBiomeSeeds(); // Generate biomes from seeds self.generateBiomes(); // --- Flood fill to ensure all tiles are connected (no black dots) --- var visited = []; for (var y = 0; y < self.mapHeight; y++) { visited[y] = []; for (var x = 0; x < self.mapWidth; x++) { visited[y][x] = false; } } // Start flood fill from center of map var startX = Math.floor(self.mapWidth / 2); var startY = Math.floor(self.mapHeight / 2); var startBiome = self.tiles[startY][startX]; var queue = [{ x: startX, y: startY }]; visited[startY][startX] = true; while (queue.length > 0) { var pos = queue.shift(); var dirs = [{ dx: 1, dy: 0 }, { dx: -1, dy: 0 }, { dx: 0, dy: 1 }, { dx: 0, dy: -1 }]; for (var d = 0; d < dirs.length; d++) { var nx = pos.x + dirs[d].dx; var ny = pos.y + dirs[d].dy; if (nx >= 0 && nx < self.mapWidth && ny >= 0 && ny < self.mapHeight && !visited[ny][nx]) { // Only connectable if not "dark" if (self.tiles[ny][nx] !== "dark") { visited[ny][nx] = true; queue.push({ x: nx, y: ny }); } } } } // Any tile not visited is unreachable, so set it to the most common neighbor biome for (var y = 0; y < self.mapHeight; y++) { for (var x = 0; x < self.mapWidth; x++) { if (!visited[y][x]) { // Find most common neighbor biome (not dark) var biomeCount = {}; for (var dy = -1; dy <= 1; dy++) { for (var dx = -1; dx <= 1; dx++) { var nx = x + dx; var ny = y + dy; if (nx >= 0 && nx < self.mapWidth && ny >= 0 && ny < self.mapHeight && (dx !== 0 || dy !== 0)) { var b = self.tiles[ny][nx]; if (b && b !== "dark") { if (!biomeCount[b]) biomeCount[b] = 0; biomeCount[b]++; } } } } // Pick the most common neighbor biome, fallback to "grass" var maxCount = 0; var bestBiome = "grass"; for (var b in biomeCount) { if (biomeCount[b] > maxCount) { maxCount = biomeCount[b]; bestBiome = b; } } self.tiles[y][x] = bestBiome; } } } // Create tiles based on biome map self.createTiles(); return self; }; // Place seed points for biome generation self.placeBiomeSeeds = function () { // Place multiple seeds for each biome for more, smaller biomes for (var i = 0; i < self.biomeTypes.length; i++) { var seedsPerBiome = 3; // Default for most biomes // Make mountain biomes much smaller by reducing their seed count if (self.biomeTypes[i] === 'mountain') { seedsPerBiome = 1; // Only 1 seed for mountain biome for much smaller area } for (var j = 0; j < seedsPerBiome; j++) { var seed = { x: Math.floor(Math.random() * self.mapWidth), y: Math.floor(Math.random() * self.mapHeight), biome: self.biomeTypes[i] }; self.biomeSeeds.push(seed); } } }; // Generate biomes from seed points using a simple distance-based algorithm self.generateBiomes = function () { for (var y = 0; y < self.mapHeight; y++) { for (var x = 0; x < self.mapWidth; x++) { // Find closest biome seed var closestDist = Number.MAX_VALUE; var closestBiome = 'grass'; // Default for (var i = 0; i < self.biomeSeeds.length; i++) { var seed = self.biomeSeeds[i]; // Reduce the effective radius for each seed, so biomes are smaller var dist = Math.sqrt(Math.pow(x - seed.x, 2) + Math.pow(y - seed.y, 2)) * 0.35; // Reduce multiplier for smaller biomes // Make mountain biomes even smaller by increasing the distance multiplier if (seed.biome === 'mountain') { dist *= 3.5; // Make mountain biomes much smaller } // Add more randomness to make borders less regular and biomes patchier dist += Math.random() * 6 - 3; if (dist < closestDist) { closestDist = dist; closestBiome = seed.biome; } } self.tiles[y][x] = closestBiome; } } // Add some randomness and blending between biomes self.refineBiomes(); }; // Add more natural transitions between biomes with street patterns self.refineBiomes = function () { // Create a deep copy of tiles array var tempTiles = []; for (var y = 0; y < self.mapHeight; y++) { tempTiles[y] = []; for (var x = 0; x < self.mapWidth; x++) { tempTiles[y][x] = self.tiles[y][x]; } } // Create street grid patterns // Find street biome seed var streetSeedX = -1; var streetSeedY = -1; for (var i = 0; i < self.biomeSeeds.length; i++) { if (self.biomeSeeds[i].biome === 'street') { streetSeedX = self.biomeSeeds[i].x; streetSeedY = self.biomeSeeds[i].y; break; } } // If we have a street seed, create grid pattern around it if (streetSeedX >= 0 && streetSeedY >= 0) { // Create street grid - horizontal and vertical streets var streetRadius = 8; // Size of urban area var streetGridSpacing = 3; // Distance between streets for (var y = Math.max(0, streetSeedY - streetRadius); y < Math.min(self.mapHeight, streetSeedY + streetRadius); y++) { for (var x = Math.max(0, streetSeedX - streetRadius); x < Math.min(self.mapWidth, streetSeedX + streetRadius); x++) { // Check if within street biome core radius var distToSeed = Math.sqrt(Math.pow(x - streetSeedX, 2) + Math.pow(y - streetSeedY, 2)); if (distToSeed <= streetRadius) { // Street grid pattern: create roads at regular intervals if (x % streetGridSpacing === 0 || y % streetGridSpacing === 0) { self.tiles[y][x] = 'street'; } // Building blocks between streets else if (self.tiles[y][x] === 'street') { // Determine building type based on position in grid - some variety within urban areas if ((x + y) % 2 === 0) { self.tiles[y][x] = 'street'; // Keep as urban (tall buildings) } else { // Mix with nearby biomes occasionally for parks, etc. if (Math.random() < 0.3) { var nearbyNonStreetBiome = ''; // Look for any non-street biome nearby for (var ny = y - 1; ny <= y + 1; ny++) { for (var nx = x - 1; nx <= x + 1; nx++) { if (ny >= 0 && ny < self.mapHeight && nx >= 0 && nx < self.mapWidth) { if (self.tiles[ny][nx] !== 'street' && self.tiles[ny][nx] !== undefined) { nearbyNonStreetBiome = self.tiles[ny][nx]; break; } } } if (nearbyNonStreetBiome) break; } // If found nearby non-street biome, use it, otherwise keep as street if (nearbyNonStreetBiome) { self.tiles[y][x] = nearbyNonStreetBiome; } } } } } } } } // Now apply regular biome smoothing for (var y = 1; y < self.mapHeight - 1; y++) { for (var x = 1; x < self.mapWidth - 1; x++) { // Skip street grid areas var distToStreetSeed = streetSeedX >= 0 ? Math.sqrt(Math.pow(x - streetSeedX, 2) + Math.pow(y - streetSeedY, 2)) : Number.MAX_VALUE; if (distToStreetSeed <= streetRadius && (x % streetGridSpacing === 0 || y % streetGridSpacing === 0)) { continue; // Skip street grid lines } // Count neighboring biomes var biomeCount = {}; for (var ny = y - 1; ny <= y + 1; ny++) { for (var nx = x - 1; nx <= x + 1; nx++) { // Check that neighbor coordinates are valid if (ny >= 0 && ny < self.mapHeight && nx >= 0 && nx < self.mapWidth) { var neighborBiome = tempTiles[ny][nx]; if (neighborBiome) { if (!biomeCount[neighborBiome]) { biomeCount[neighborBiome] = 0; } biomeCount[neighborBiome]++; } } } } // Random chance to convert to most common neighbor if (Math.random() < 0.4) { var mostCommon = tempTiles[y][x]; var maxCount = 0; for (var biome in biomeCount) { if (biomeCount.hasOwnProperty(biome) && biomeCount[biome] > maxCount) { maxCount = biomeCount[biome]; mostCommon = biome; } } self.tiles[y][x] = mostCommon; } } } }; // Create actual tile objects based on the biome map self.createTiles = function () { for (var y = 0; y < self.mapHeight; y++) { for (var x = 0; x < self.mapWidth; x++) { var biomeType = self.tiles[y][x]; var tile = new MapTile().initTile(biomeType, x, y); tile.tileSize = TILE_SIZE; self.addChild(tile); } } }; // Get tile at grid coordinates self.getTileAt = function (gridX, gridY) { // Ensure coordinates are within bounds if (gridX < 0 || gridX >= self.mapWidth || gridY < 0 || gridY >= self.mapHeight) { return null; } // Find the tile in the children for (var i = 0; i < self.children.length; i++) { var tile = self.children[i]; if (tile.gridX === gridX && tile.gridY === gridY) { return tile; } } return null; }; return self; }); // MapTile represents a single tile in the game grid var MapTile = Container.expand(function () { var self = Container.call(this); self.biomeType = 'grass'; // Default biome self.tileSize = TILE_SIZE; // Set to match TILE_SIZE for correct movement alignment self.hasCreature = false; self.explored = false; self.x = 0; self.y = 0; self.gridX = 0; self.gridY = 0; // Initialize the tile with a specific biome self.initTile = function (biomeType, gridX, gridY) { self.biomeType = biomeType; self.gridX = gridX; self.gridY = gridY; self.x = gridX * self.tileSize; self.y = gridY * self.tileSize; // Get the appropriate tile asset based on biome var assetId = self.biomeType + 'Tile'; // Use unique image for urban biome if (self.biomeType === 'urban') assetId = 'urbanTile'; // Create a container to hold 4 quadrant images self.quadrantTiles = []; for (var qy = 0; qy < 2; qy++) { for (var qx = 0; qx < 2; qx++) { var quad = self.attachAsset(assetId, { anchorX: 0, anchorY: 0, width: TILE_SIZE / 2 + 4, // Add 4px overlap to eliminate black dots height: TILE_SIZE / 2 + 4, // Add 4px overlap to eliminate black dots x: qx * TILE_SIZE / 2 - (qx > 0 ? 2 : 0), y: qy * TILE_SIZE / 2 - (qy > 0 ? 2 : 0) }); quad.alpha = 0.9; self.quadrantTiles.push(quad); } } // No fog overlay for undiscovered tiles (fog removed) self.fogOverlay = null; // Random chance to spawn a creature based on biome (further reduced for less frequent spawning) if (Math.random() < 0.02) { self.hasCreature = true; var indicator = self.attachAsset('encounterIndicator', { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); indicator.alpha = 0.7; } return self; }; // Mark tile as explored self.explore = function () { if (!self.explored) { self.explored = true; // Hide fog overlay if present if (self.fogOverlay) { self.fogOverlay.visible = false; } // Add to explored areas in storage var exploredKey = self.gridX + "," + self.gridY; if (!storage.exploredTiles) storage.exploredTiles = {}; storage.exploredTiles[exploredKey] = true; } }; return self; }); // MiniMap class to show explored areas var MiniMap = Container.expand(function () { var self = Container.call(this); // MiniMap components self.mapBackground = null; self.playerMarker = null; self.tileMarkers = []; self.mapSize = 300; self.scale = 0.1; // How much of the full map to show // Initialize minimap self.init = function () { // Create background self.mapBackground = self.attachAsset('minimap', { anchorX: 0, anchorY: 0, width: self.mapSize, height: self.mapSize }); // Create player marker self.playerMarker = self.attachAsset('minimapMarker', { anchorX: 0.5, anchorY: 0.5, x: self.mapSize / 2, y: self.mapSize / 2 }); // Position minimap in top right corner self.x = 2048 - self.mapSize - 20; self.y = 20; return self; }; // Update minimap with current player position and explored tiles self.update = function (player, gameMap) { // Check if player and gameMap are defined if (!player || !gameMap) return; // Update player marker position var centerX = self.mapSize / 2; var centerY = self.mapSize / 2; // Position marker relative to center self.playerMarker.x = centerX; self.playerMarker.y = centerY; // Clear existing tile markers for (var i = 0; i < self.tileMarkers.length; i++) { self.removeChild(self.tileMarkers[i]); } self.tileMarkers = []; // Add markers for visible tiles around player var visionRadius = 2; // Match the reduced player vision radius for (var y = Math.floor(player.gridY - visionRadius); y <= Math.ceil(player.gridY + visionRadius); y++) { for (var x = Math.floor(player.gridX - visionRadius); x <= Math.ceil(player.gridX + visionRadius); x++) { var dx = x - player.gridX; var dy = y - player.gridY; if (Math.sqrt(dx * dx + dy * dy) <= visionRadius + 0.2) { var tile = gameMap.getTileAt(x, y); if (tile && tile.explored) { // Create marker with biome color var color = 0x33cc33; // Default grass if (tile.biomeType === 'water') color = 0x3399ff; if (tile.biomeType === 'desert') color = 0xe6cc99; if (tile.biomeType === 'forest') color = 0x006600; if (tile.biomeType === 'mountain') color = 0x999999; if (tile.biomeType === 'street') color = 0x666666; if (tile.biomeType === 'urban') color = 0x4444aa; var marker = new Container(); var markerGraphics = LK.getAsset('grassTile', { anchorX: 0, anchorY: 0, width: 10, height: 10 }); markerGraphics.tint = color; marker.addChild(markerGraphics); // Position marker relative to player marker.x = centerX + (x - player.gridX) * 10; marker.y = centerY + (y - player.gridY) * 10; self.addChild(marker); self.tileMarkers.push(marker); } } } } }; return self; }); // Player character with integrated vision logic var Player = Container.expand(function () { var self = Container.call(this); // Player properties self.gridX = 10; // Starting X position on grid (max map) self.gridY = 10; // Starting Y position on grid (max map) self.moveSpeed = 0.2; // Movement animation speed self.moving = false; self.lastGridX = 10; self.lastGridY = 10; self.visionRadius = 2; // Reduced vision radius in blocks // Remove sub-tile logic: always center player in tile self.subTileX = 0; self.subTileY = 0; // New: quadrant label self.quadrantLabel = null; // Initialize player with graphics self.init = function () { var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, y: -44 // Move player image 44px higher for better visual centering }); // Make player a bit bigger for better visibility playerGraphics.scale.set(0.34, 0.34); // Add quadrant label self.quadrantLabel = new Text2('', { size: 40, fill: 0xffff00 }); self.quadrantLabel.anchor.set(0.5, 1); self.quadrantLabel.x = 0; self.quadrantLabel.y = -TILE_SIZE / 4; self.addChild(self.quadrantLabel); // Load position from storage if available if (storage.playerPosition) { self.gridX = storage.playerPosition.x; self.gridY = storage.playerPosition.y; self.lastGridX = self.gridX; self.lastGridY = self.gridY; // Always force subTileX and subTileY to 0 (center) self.subTileX = 0; self.subTileY = 0; } else { // If not in storage, spawn at center of map self.gridX = 15; self.gridY = 15; self.lastGridX = 15; self.lastGridY = 15; self.subTileX = 0; self.subTileY = 0; } // Update position based on grid coordinates self.updatePosition(); // Always visually center player on screen after spawn self.x = self.gridX * TILE_SIZE + TILE_SIZE / 2; self.y = self.gridY * TILE_SIZE + TILE_SIZE / 2; return self; }; // Move player to specific grid coordinates self.moveToGrid = function (newGridX, newGridY) { if (self.moving) return; // Don't interrupt current movement // Store last position for comparison self.lastGridX = self.gridX; self.lastGridY = self.gridY; // Prevent movement onto dark (black) blocks if (typeof gameMap !== "undefined") { var targetTile = gameMap.getTileAt(newGridX, newGridY); if (targetTile && targetTile.biomeType === "dark") { // Block movement, do not update grid position return; } // Water entry animation: if moving into water, play animation if (targetTile && targetTile.biomeType === "water") { // Animate player "sinking" and "bobbing" in water var originalY = self.y; tween(self, { y: originalY + 30 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { var bobCount = 0; function bob() { if (bobCount < 3) { tween(self, { y: originalY + 20 }, { duration: 120, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { y: originalY + 30 }, { duration: 120, easing: tween.easeInOut, onFinish: function onFinish() { bobCount++; bob(); } }); } }); } else { // After bobbing, ensure player is at correct Y for water tile var finalY = self.gridY * TILE_SIZE + TILE_SIZE / 2; self.y = finalY; } } bob(); } }); } } // Update grid position (no sub-tile logic) self.gridX = newGridX; self.gridY = newGridY; self.subTileX = 0; self.subTileY = 0; // Save position to storage storage.playerPosition = { x: self.gridX, y: self.gridY, subTileX: self.subTileX, subTileY: self.subTileY }; // Animate movement and update vision after movement finishes self.moving = true; // Move to the center of the tile var offsetX = TILE_SIZE / 2; var offsetY = TILE_SIZE / 2; var targetMapX = self.gridX * TILE_SIZE + offsetX; var targetMapY = self.gridY * TILE_SIZE + offsetY; // Animate the map and player so the player appears to move smoothly and always stays centered on screen var startPlayerX = self.x; var startPlayerY = self.y; var startMapX = typeof gameMap !== "undefined" ? gameMap.x : 0; var startMapY = typeof gameMap !== "undefined" ? gameMap.y : 0; var endPlayerX = 1024; var endPlayerY = 1366; var endMapX = 1024 - targetMapX; var endMapY = 1366 - targetMapY; var playerTweenDuration = 350; var visionTweenDuration = 200; // Animate vision update to be much faster than player movement if (typeof gameMap !== "undefined" && typeof self.updateVision === "function") { var visionObj = { progress: 0 }; tween(visionObj, { progress: 1 }, { duration: visionTweenDuration, easing: tween.easeOut, onUpdate: function onUpdate() { self.updateVision(gameMap); } }); } // Animate both player and map for smooth movement tween({ t: 0 }, { t: 1 }, { duration: playerTweenDuration, easing: tween.easeOut, onUpdate: function onUpdate(obj) { // Interpolate player position (moves on map) self.x = startPlayerX + (targetMapX - startPlayerX) * obj.t; self.y = startPlayerY + (targetMapY - startPlayerY) * obj.t; // Animate map so player is always centered if (typeof gameMap !== "undefined") { gameMap.x = startMapX + (endMapX - startMapX) * obj.t; gameMap.y = startMapY + (endMapY - startMapY) * obj.t; } }, onFinish: function onFinish() { self.moving = false; // Snap to final positions self.x = targetMapX; // If current tile is water, keep the "bobbing" offset, else restore to normal var currentTile = typeof gameMap !== "undefined" ? gameMap.getTileAt(self.gridX, self.gridY) : null; if (currentTile && currentTile.biomeType === "water") { self.y = targetMapY; // Appear lower in water } else { self.y = targetMapY - 30; // Move player image 30px above tile center for better centering // If coming from water, stop any water bobbing animation and reset Y if (typeof tween !== "undefined") { tween.stop(self, { y: true }); } } if (typeof gameMap !== "undefined") { gameMap.x = endMapX; gameMap.y = endMapY; } // Only update vision after movement is complete for optimized vision if (typeof gameMap !== "undefined") { self.updateVision(gameMap); } } }); // Always clear quadrant label since player is always at center if (self.quadrantLabel) self.quadrantLabel.setText(''); // Call onQuadrantChange with 'CENTER' label for compatibility if (typeof self.onQuadrantChange === "function") { self.onQuadrantChange(self.gridX, self.gridY, self.subTileX, self.subTileY, 'CENTER'); } }; // Update position based on current grid coordinates (no animation) self.updatePosition = function () { // Position player at the center of the tile (no sub-tile logic) var offsetX = TILE_SIZE / 2; var offsetY = TILE_SIZE / 2; self.x = self.gridX * TILE_SIZE + offsetX; // If current tile is water, show player lower (no -30 offset) var currentTile = typeof gameMap !== "undefined" ? gameMap.getTileAt(self.gridX, self.gridY) : null; if (currentTile && currentTile.biomeType === "water") { self.y = self.gridY * TILE_SIZE + offsetY; } else { self.y = self.gridY * TILE_SIZE + offsetY - 30; // Move player image 30px above tile center for better centering } // Hide player sprite if on dark (black) block if (typeof gameMap !== "undefined") { var tile = gameMap.getTileAt(self.gridX, self.gridY); if (tile && tile.biomeType === "dark") { self.visible = false; } else { self.visible = true; // Mark this tile as discovered if not already if (tile && !tile.explored) { tile.explore(); } } } else { self.visible = true; } // No longer force sub-tile to 0 (center) when updating position // Only update vision if not moving, to optimize vision updates if (!self.moving && typeof gameMap !== "undefined" && typeof self.updateVision === "function") { self.updateVision(gameMap); } // Always clear quadrant label since player is always at center if (self.quadrantLabel) self.quadrantLabel.setText(''); // Call onQuadrantChange with 'CENTER' label for compatibility if (typeof self.onQuadrantChange === "function") { self.onQuadrantChange(self.gridX, self.gridY, self.subTileX, self.subTileY, 'CENTER'); } // Ensure player is always above the tile and centered after update if (typeof game !== "undefined" && self.parent !== game) { game.addChild(self); } self.scale.set(gameMap.scale.x, gameMap.scale.y); self.visible = true; // Always visually center player on screen after updatePosition // Now: player moves on map, so set to map position self.x = self.gridX * TILE_SIZE + TILE_SIZE / 2; self.y = self.gridY * TILE_SIZE + TILE_SIZE / 2; // Always bring player to top of display list if (typeof game !== "undefined" && typeof game.children !== "undefined") { if (game.children.indexOf(self) !== -1) { game.removeChild(self); game.addChild(self); } } }; // Update vision: show/hide tiles based on a circular vision area, and save seen tiles as memory self.updateVision = function (gameMap) { // Vision area: always center player in the middle of the vision area for optimized movement var visionRadius = 2; // Reduced from 3 to 2 for even smaller vision var centerX = self.gridX; var centerY = self.gridY; var minX = Math.max(0, Math.floor(centerX - visionRadius)); var maxX = Math.min(MAP_WIDTH - 1, Math.ceil(centerX + visionRadius)); var minY = Math.max(0, Math.floor(centerY - visionRadius)); var maxY = Math.min(MAP_HEIGHT - 1, Math.ceil(centerY + visionRadius)); for (var y = 0; y < MAP_HEIGHT; y++) { for (var x = 0; x < MAP_WIDTH; x++) { var tile = gameMap.getTileAt(x, y); if (tile) { // Calculate distance from player - properly centered now var dx = x - centerX; var dy = y - centerY; // Only show tiles within a circular vision area (Euclidean distance <= visionRadius + 0.2 for smoothness) if (Math.sqrt(dx * dx + dy * dy) <= visionRadius + 0.2) { tile.visible = true; // Mark as explored and save to memory if (!tile.explored) { tile.explored = true; var exploredKey = x + "," + y; if (!storage.exploredTiles) storage.exploredTiles = {}; storage.exploredTiles[exploredKey] = true; } // No fog overlay to hide (fog removed) // --- Show both spawned and despawned creatures in vision --- // In vision area: show both spawned and despawned creatures // Remove all faded indicators first to avoid duplicates for (var c = tile.children.length - 1; c >= 0; c--) { if (tile.children[c].assetId === 'encounterIndicator' && tile.children[c].alpha === 0.25) { tile.removeChild(tile.children[c]); } } // If a creature is currently spawned, mark that this tile has ever had a creature if (tile.hasCreature) { tile._everHadCreature = true; } // Show indicator for spawned creature if (tile.hasCreature) { // Remove encounterIndicator if present, since we will show the creature image instead for (var c = tile.children.length - 1; c >= 0; c--) { if (tile.children[c].assetId === 'encounterIndicator' && tile.children[c].alpha === 0.7) { tile.removeChild(tile.children[c]); } } // --- Show creature visually on top of tile --- if (!tile._creatureVisual) { // Find the type of the creature for this tile from mapCreatures/mapCreatureTiles var creatureType = null; if (typeof mapCreatures !== "undefined" && typeof mapCreatureTiles !== "undefined") { for (var mci = 0; mci < mapCreatureTiles.length; mci++) { if (mapCreatureTiles[mci] === tile) { creatureType = mapCreatures[mci]; break; } } } // If not found, fallback to biomeType if (!creatureType) { // Use a default type for biome if (typeof Creature !== "undefined") { creatureType = new Creature().getRandomType(tile.biomeType); } else { creatureType = "normal"; } } // If biome is urban, use urbanCreature if (tile.biomeType === "urban") { creatureType = "urban"; } // Show the creature visually using the correct image for the type var creatureVisual = LK.getAsset(creatureType + "Creature", { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); creatureVisual.scale.set(1.2, 1.2); creatureVisual.alpha = 1.0; tile.addChild(creatureVisual); tile._creatureVisual = creatureVisual; } // Always bring creature visual to top if (tile._creatureVisual && tile.children.indexOf(tile._creatureVisual) !== -1) { tile.removeChild(tile._creatureVisual); tile.addChild(tile._creatureVisual); } } // Remove creature visual if not hasCreature if (!tile.hasCreature && tile._creatureVisual) { tile.removeChild(tile._creatureVisual); tile._creatureVisual = null; } // Show faded indicator for despawned creature (if ever had creature, but not currently spawned) if (tile._everHadCreature && !tile.hasCreature) { var alreadyFaded = false; for (var c = 0; c < tile.children.length; c++) { if (tile.children[c].assetId === 'encounterIndicator' && tile.children[c].alpha === 0.25) { alreadyFaded = true; break; } } if (!alreadyFaded) { var fadedIndicator = tile.attachAsset('encounterIndicator', { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); fadedIndicator.alpha = 0.25; } // Remove creature visual if not hasCreature if (tile._creatureVisual) { tile.removeChild(tile._creatureVisual); tile._creatureVisual = null; } } // --- End show both spawned and despawned creatures in vision --- } else { // Out of vision: hide all encounter indicators (spawned and despawned) in foggy/unvision area for (var c = tile.children.length - 1; c >= 0; c--) { if (tile.children[c].assetId === 'encounterIndicator') { tile.removeChild(tile.children[c]); } } // Hide all creatures in foggy area: do not show any indicators or creatures tile.visible = true; // No fog overlay to show (fog removed) } } } // Called whenever the player moves to a new quadrant of a tile self.onQuadrantChange = function (gridX, gridY, subTileX, subTileY, label) { // Example: you can add your own logic here to check which quadrant the player is in // For example, trigger something if in DOWN LEFT // gridX, gridY: tile coordinates // subTileX, subTileY: 0 or 1 (0=left/up, 1=right/down) // label: string label of the quadrant }; // Example: log which quadrant the player is in // console.log("Player is at tile (" + gridX + "," + gridY + ") in quadrant: " + label); // You can add more logic here, e.g.: // if (subTileX === 0 && subTileY === 1) { /* DOWN LEFT */ } // if (subTileX === 1 && subTileY === 0) { /* UP RIGHT */ } // etc. } ; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Unique image for urban biome // Sounds // Encounter indicator // UI elements // Creatures by elemental type // Map tiles // Player character // Storage for saving game progress // Tween for animations // Game dimensions and settings // JSON helper functions for parsing and stringifying 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); } function parseJSON(str) { var result = {}; if (typeof str === 'string') { try { var pairs = str.slice(1, -1).split(','); for (var i = 0; i < pairs.length; i++) { var pair = pairs[i].trim().split(':'); var key = pair[0].replace(/["']/g, '').trim(); var value = pair[1].trim(); if (value.startsWith('[') && value.endsWith(']')) { result[key] = parseJSONArray(value); } else if (value.startsWith('{') && value.endsWith('}')) { result[key] = parseJSON(value); } else if (value === 'true') { result[key] = true; } else if (value === 'false') { result[key] = false; } else if (!isNaN(value)) { result[key] = Number(value); } else { result[key] = value.replace(/["']/g, ''); } } } catch (e) { console.log('Error parsing JSON: ', e); } } return result; } function parseJSONArray(str) { var result = []; if (typeof str === 'string') { try { var items = str.slice(1, -1).split(','); for (var i = 0; i < items.length; i++) { var item = items[i].trim(); if (item.startsWith('[') && item.endsWith(']')) { result.push(parseJSONArray(item)); } else if (item.startsWith('{') && item.endsWith('}')) { result.push(parseJSON(item)); } else if (item === 'true') { result.push(true); } else if (item === 'false') { result.push(false); } else if (!isNaN(item)) { result.push(Number(item)); } else { result.push(item.replace(/["']/g, '')); } } } catch (e) { console.log('Error parsing JSON array: ', e); } } return result; } function stringifyJSON(obj) { if (Array.isArray(obj)) { var items = []; for (var i = 0; i < obj.length; i++) { items.push(stringifyJSON(obj[i])); } return '[' + items.join(',') + ']'; } else if (_typeof(obj) === 'object' && obj !== null) { var pairs = []; for (var key in obj) { if (obj.hasOwnProperty(key)) { pairs.push('"' + key + '":' + stringifyJSON(obj[key])); } } return '{' + pairs.join(',') + '}'; } else if (typeof obj === 'string') { return '"' + obj.replace(/"/g, '\\"') + '"'; } else { return String(obj); } } var TILE_SIZE = 400; // Double the default tile size var MAP_WIDTH = 80; // Make the map much larger var MAP_HEIGHT = 80; // Make the map much larger // Vision radius is now set in Player.updateVision for optimized vision area // Initialize game objects var gameMap = game.addChild(new GameMap().initMap()); gameMap.scale.set(1.5, 1.5); // Zoom in the map for a closer look // Double the size of the tile at (0,0) if (gameMap.tiles && gameMap.tiles[0] && gameMap.tiles[0][0]) { gameMap.tiles[0][0] = "grass"; var tileObj = gameMap.getTileAt(0, 0); if (tileObj) { tileObj.biomeType = "grass"; tileObj.tileSize = TILE_SIZE * 2; // Optionally update the tile graphics if needed } } // Update all MapTile objects to use new TILE_SIZE, except (0,0) which is double for (var y = 0; y < MAP_HEIGHT; y++) { for (var x = 0; x < MAP_WIDTH; x++) { var t = gameMap.getTileAt(x, y); if (t) { if (!(x === 0 && y === 0)) { t.tileSize = TILE_SIZE; } } } } // Spawn player at a non-water, non-dark tile near 15,15 coordinates, centered in tile var playerStartX = 15; var playerStartY = 15; // Find a valid starting tile (not water, not dark) function findValidPlayerStart(gameMap, startX, startY) { var maxRadius = 10; for (var r = 0; r <= maxRadius; r++) { for (var dy = -r; dy <= r; dy++) { for (var dx = -r; dx <= r; dx++) { var x = startX + dx; var y = startY + dy; if (x >= 0 && x < MAP_WIDTH && y >= 0 && y < MAP_HEIGHT) { var biome = gameMap.tiles && gameMap.tiles[y] && gameMap.tiles[y][x]; // Only allow spawn if not water and not dark if (biome && biome !== "water" && biome !== "dark") { return { x: x, y: y }; } } } } } // fallback to 0,0 if nothing found return { x: 0, y: 0 }; } var validStart = findValidPlayerStart(gameMap, playerStartX, playerStartY); playerStartX = validStart.x; playerStartY = validStart.y; // Ensure start tile is not water for consistency if (gameMap.tiles && gameMap.tiles[playerStartY] && gameMap.tiles[playerStartY][playerStartX]) { // If the found tile is water or dark, forcibly set to grass if (gameMap.tiles[playerStartY][playerStartX] === "water" || gameMap.tiles[playerStartY][playerStartX] === "dark") { gameMap.tiles[playerStartY][playerStartX] = "grass"; var tileObj = gameMap.getTileAt(playerStartX, playerStartY); if (tileObj) { tileObj.biomeType = "grass"; tileObj.tileSize = TILE_SIZE; } } else { // Otherwise, just update the tile object to match the biome var tileObj = gameMap.getTileAt(playerStartX, playerStartY); if (tileObj) { tileObj.biomeType = gameMap.tiles[playerStartY][playerStartX]; tileObj.tileSize = TILE_SIZE; } } } storage.playerPosition = { x: playerStartX, y: playerStartY, subTileX: 0, subTileY: 0 }; var player = new Player().init(); player.gridX = playerStartX; player.gridY = playerStartY; player.lastGridX = playerStartX; player.lastGridY = playerStartY; player.subTileX = 0; player.subTileY = 0; player.updatePosition(); // Always visually center player in the tile after spawn centerViewOnPlayer(); centerViewOnPlayer(); // player.x and player.y are set by updatePosition and movement now // Ensure player is always above all map tiles and scaled to match map player.scale.set(gameMap.scale.x, gameMap.scale.y); // Always add player after gameMap so it is above all tiles if (player.parent !== game) { game.addChild(player); } player.visible = true; // Explicitly set player visible in case it was hidden // Mark starting tile as explored and discovered var startTile2 = gameMap.getTileAt(playerStartX, playerStartY); if (startTile2 && !startTile2.explored) { startTile2.explore(); } var dpad = game.addChild(new DPad().init()); var minimap = game.addChild(new MiniMap().init()); var encounter = game.addChild(new Encounter().init()); var collection = game.addChild(new Collection().init()); // Collection button (top right corner) var collectionButton = game.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: 1900, y: 150 }); collectionButton.interactive = true; // Collection button icon var collectionText = new Text2('COL', { size: 30, fill: 0xFFFFFF }); collectionText.anchor.set(0.5, 0.5); collectionText.x = 1900; collectionText.y = 150; game.addChild(collectionText); // Game variables var encounterActive = false; var exploredCount = 0; var totalTiles = MAP_WIDTH * MAP_HEIGHT; // Pokeball inventory UI var pokeballInventoryText = new Text2('', { size: 40, fill: 0xffffff }); pokeballInventoryText.anchor.set(0.5, 0); pokeballInventoryText.x = 1024; pokeballInventoryText.y = 80; game.addChild(pokeballInventoryText); function updatePokeballInventoryText() { pokeballInventoryText.setText('Pokeballs: ' + 'Basic(' + (playerPokeballs.basic || 0) + ') ' + 'Uncommon(' + (playerPokeballs.uncommon || 0) + ') ' + 'Rare(' + (playerPokeballs.rare || 0) + ') ' + 'Legendary(' + (playerPokeballs.legendary || 0) + ')'); } updatePokeballInventoryText(); // Update stats display // (removed: no stats text shown) // Center the game view on player function centerViewOnPlayer() { // Always center the map so the player is visually centered on screen if (typeof gameMap !== "undefined" && typeof player !== "undefined") { // Calculate the target position for the map so player is at (1024, 1366) var offsetX = TILE_SIZE / 2; var offsetY = TILE_SIZE / 2; var targetMapX = player.gridX * TILE_SIZE + offsetX; var targetMapY = player.gridY * TILE_SIZE + offsetY; // Adjust for zoom (scale) var scaleX = gameMap.scale.x; var scaleY = gameMap.scale.y; gameMap.x = 1024 - targetMapX * scaleX; gameMap.y = 1366 - targetMapY * scaleY; // Place player at the center of the screen player.x = 1024; player.y = 1336; // Move player image 30px above tile center for better centering // Ensure player is always visually centered in the tile // (player.x, player.y are always the center of the tile in world coordinates) } // Ensure player is above tiles if (player.parent !== game) { game.addChild(player); } player.scale.set(gameMap.scale.x, gameMap.scale.y); player.visible = true; } // Handle player movement from DPad (move exactly 1 block per input) dpad.upButton.down = function () { if (!player.moving && !encounterActive && !collection.visible) { // Move exactly 0.5 tile up per press var newGridY = player.gridY - 0.5; if (newGridY >= 0) { player.moveToGrid(player.gridX, newGridY); } } }; dpad.downButton.down = function () { if (!player.moving && !encounterActive && !collection.visible) { // Move exactly 0.5 tile down per press var newGridY = player.gridY + 0.5; if (newGridY < MAP_HEIGHT) { player.moveToGrid(player.gridX, newGridY); } } }; dpad.leftButton.down = function () { if (!player.moving && !encounterActive && !collection.visible) { // Move exactly 0.5 tile left per press var newGridX = player.gridX - 0.5; if (newGridX >= 0) { player.moveToGrid(newGridX, player.gridY); } } }; dpad.rightButton.down = function () { if (!player.moving && !encounterActive && !collection.visible) { // Move exactly 0.5 tile right per press var newGridX = player.gridX + 0.5; if (newGridX < MAP_WIDTH) { player.moveToGrid(newGridX, player.gridY); } } }; // Always start the player centered in the vision area player.gridX = playerStartX; player.gridY = playerStartY; player.lastGridX = playerStartX; player.lastGridY = playerStartY; player.updatePosition(); centerViewOnPlayer(); // Handle collection button collectionButton.down = function () { if (!encounterActive) { // Always reload the bag menu for all creatures if (typeof collection.show === "function") { collection.show(); } } }; // Handle collection close button collection.closeButton.down = function () { collection.hide(); // Show player again when closing collection player.visible = true; }; // Track last player position to detect changes var lastPlayerGridX = player.gridX; var lastPlayerGridY = player.gridY; // Main game update loop // Throttle update to 30FPS (every ~33ms) for better performance var lastUpdateTime = 0; game.update = function () { var now = Date.now(); if (lastUpdateTime && now - lastUpdateTime < 33) { return; } lastUpdateTime = now; // --- Pokeball system --- // Pokeball rarities and their catch chances var pokeballTypes = [{ id: 'pokeball_basic', name: 'Basic Ball', color: 0xffffff, rarity: 'basic', catchBonus: 0, chance: 0.5 }, { id: 'pokeball_uncommon', name: 'Uncommon Ball', color: 0x00ff00, rarity: 'uncommon', catchBonus: 0.15, chance: 0.65 }, { id: 'pokeball_rare', name: 'Rare Ball', color: 0x0000ff, rarity: 'rare', catchBonus: 0.3, chance: 0.8 }, { id: 'pokeball_legendary', name: 'Legendary Ball', color: 0xffd700, rarity: 'legendary', catchBonus: 0.5, chance: 0.95 }]; // Player's pokeball inventory if (typeof playerPokeballs === "undefined") { playerPokeballs = { basic: 3, uncommon: 1, rare: 0, legendary: 0 }; } // Pokeball map objects if (typeof mapPokeballs === "undefined") { mapPokeballs = []; mapPokeballTiles = []; mapPokeballMax = 6; // Max pokeballs on map at once mapPokeballSpawnCooldown = 0; } // --- Creature spawn/despawn logic --- // We'll use a global array to track visible creatures on the map if (typeof mapCreatures === "undefined") { mapCreatures = []; mapCreatureTiles = []; mapCreatureLabels = []; mapCreatureMax = 4; // Max creatures on map at once (fewer for optimization) mapCreatureSpawnCooldown = 0; } // Despawn creatures sometimes (randomly, or if their tile is explored) for (var i = mapCreatures.length - 1; i >= 0; i--) { var tile = mapCreatureTiles[i]; // Despawn if tile is explored or random chance if (!tile || tile.explored || Math.random() < 0.01) { // Remove label if present if (mapCreatureLabels[i]) { tile && tile.removeChild(mapCreatureLabels[i]); mapCreatureLabels.splice(i, 1); } // Remove indicator if present if (tile && tile.hasCreature) { tile.hasCreature = false; // Remove encounterIndicator for (var j = 0; j < tile.children.length; j++) { if (tile.children[j].assetId === 'encounterIndicator') { tile.removeChild(tile.children[j]); break; } } } mapCreatures.splice(i, 1); mapCreatureTiles.splice(i, 1); continue; } } // Spawn new creatures sometimes, up to max if (mapCreatures.length < mapCreatureMax && mapCreatureSpawnCooldown <= 0) { // Try to spawn a new creature var tries = 0; while (tries < 10 && mapCreatures.length < mapCreatureMax) { var gx = Math.floor(Math.random() * MAP_WIDTH); var gy = Math.floor(Math.random() * MAP_HEIGHT); var tile = gameMap.getTileAt(gx, gy); // Only spawn on tiles that are not explored, not dark, not already with a creature, and are crossable by the player // Crossable: not water, not dark, not mountain (if you want to restrict more, add more biomeType checks) var crossable = tile && tile.biomeType !== "dark"; // Optionally, restrict further: e.g. not water, not mountain // crossable = crossable && tile.biomeType !== "water" && tile.biomeType !== "mountain"; if (tile && !tile.explored && !tile.hasCreature && crossable) { // Mark tile as having a creature tile.hasCreature = true; // Add indicator var indicator = tile.attachAsset('encounterIndicator', { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); indicator.alpha = 0.7; // Pick a random type for this biome var cType = new Creature().getRandomType(tile.biomeType); mapCreatures.push(cType); mapCreatureTiles.push(tile); // No type label writing for bug ground type mapCreatureLabels.push(null); } tries++; } // Add a much longer cooldown so we don't spawn every frame (increased for much less frequent spawning) mapCreatureSpawnCooldown = 180 + Math.floor(Math.random() * 120); // 3-5s } else { mapCreatureSpawnCooldown--; } // --- Pokeball spawn/despawn logic --- // Despawn pokeballs sometimes (randomly, or if their tile is explored) for (var i = mapPokeballs.length - 1; i >= 0; i--) { var tile = mapPokeballTiles[i]; if (!tile || tile.explored || Math.random() < 0.01) { // Remove pokeball visual if present if (tile && tile._pokeballVisual) { tile.removeChild(tile._pokeballVisual); tile._pokeballVisual = null; } mapPokeballs.splice(i, 1); mapPokeballTiles.splice(i, 1); continue; } } // Spawn new pokeballs sometimes, up to max if (mapPokeballs.length < mapPokeballMax && mapPokeballSpawnCooldown <= 0) { var tries = 0; while (tries < 10 && mapPokeballs.length < mapPokeballMax) { var gx = Math.floor(Math.random() * MAP_WIDTH); var gy = Math.floor(Math.random() * MAP_HEIGHT); var tile = gameMap.getTileAt(gx, gy); // Only spawn on tiles that are not explored, not dark, not already with a pokeball or creature var crossable = tile && tile.biomeType !== "dark" && !tile.hasCreature && !tile._pokeballVisual; if (tile && !tile.explored && crossable) { // Pick a random pokeball type (weighted: more basics, less legendary) var rand = Math.random(); var pType = pokeballTypes[0]; if (rand > 0.85) pType = pokeballTypes[3];else if (rand > 0.7) pType = pokeballTypes[2];else if (rand > 0.5) pType = pokeballTypes[1]; // Show pokeball on tile var pokeballVisual = LK.getAsset('captureRing', { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); pokeballVisual.tint = pType.color; pokeballVisual.scale.set(0.5, 0.5); tile.addChild(pokeballVisual); tile._pokeballVisual = pokeballVisual; pokeballVisual.assetId = 'pokeball_' + pType.rarity; mapPokeballs.push(pType.rarity); mapPokeballTiles.push(tile); } tries++; } mapPokeballSpawnCooldown = 120 + Math.floor(Math.random() * 120); // 2-4s } else { mapPokeballSpawnCooldown--; } // --- End creature spawn/despawn logic --- // --- Pokeball collection logic --- var playerTile = gameMap.getTileAt(player.gridX, player.gridY); if (playerTile && playerTile._pokeballVisual) { // Find which pokeball type is here var foundType = null; for (var i = 0; i < pokeballTypes.length; i++) { if (playerTile._pokeballVisual.assetId === 'pokeball_' + pokeballTypes[i].rarity) { foundType = pokeballTypes[i]; break; } } if (foundType) { // Add to inventory if (!playerPokeballs[foundType.rarity]) playerPokeballs[foundType.rarity] = 0; playerPokeballs[foundType.rarity]++; // Remove pokeball from map playerTile.removeChild(playerTile._pokeballVisual); playerTile._pokeballVisual = null; // Remove from arrays for (var i = mapPokeballTiles.length - 1; i >= 0; i--) { if (mapPokeballTiles[i] === playerTile) { mapPokeballs.splice(i, 1); mapPokeballTiles.splice(i, 1); break; } } // Show a message var msg = new Text2('Collected ' + foundType.name + '!', { size: 60, fill: foundType.color }); msg.anchor.set(0.5, 0.5); msg.x = 1024; msg.y = 400; game.addChild(msg); LK.setTimeout(function () { game.removeChild(msg); }, 1000); // Update inventory UI updatePokeballInventoryText(); } } // Skip updates if in an active encounter if (encounterActive) { encounter.update(); return; } // Check if collection is visible if (collection.visible) { // Hide player and move below collection player.visible = false; if (player.parent !== game) { game.addChild(player); } // Always reload the bag menu for all creatures if (typeof collection.show === "function") { collection.show(); } return; } // If collection is not visible, ensure player is visible player.visible = true; // Center view on player centerViewOnPlayer(); // Update minimap only every 3rd update for optimization if (typeof minimapUpdateCounter === "undefined") minimapUpdateCounter = 0; minimapUpdateCounter++; if (minimapUpdateCounter % 3 === 0) { minimap.update(player, gameMap); } // Vision update is now handled after player movement for optimization // Check if player has moved to a new tile if (player.gridX !== lastPlayerGridX || player.gridY !== lastPlayerGridY) { // Get the current tile var currentTile = gameMap.getTileAt(player.gridX, player.gridY); if (currentTile) { // Mark tile as explored if it's new if (!currentTile.explored) { currentTile.explore(); exploredCount++; } // Prevent encounters on dark (black) blocks if (currentTile.biomeType !== "dark" && currentTile.hasCreature && !currentTile._creatureCaught) { // Always trigger encounter if player and creature cross encounterActive = true; // Create creature based on biome var creatureType = new Creature().getRandomType(currentTile.biomeType); var creature = new Creature().init(creatureType, null, player.gridX, player.gridY); // Start encounter with this creature encounter.startEncounter(creature); // Remove creature from tile and mark as caught if caught, otherwise allow re-encounter // (actual caught logic handled in encounter/capture, so only mark as caught if caught) currentTile.hasCreature = false; currentTile._creatureCaught = false; // Not caught yet, only set to true if caught currentTile._creatureCaught = true; // Mark as caught so it doesn't respawn immediately // Remove indicator from tile for (var i = 0; i < currentTile.children.length; i++) { var child = currentTile.children[i]; if (child.assetId === 'encounterIndicator') { currentTile.removeChild(child); break; } } // Remove label if present (from mapCreatureLabels) if (typeof mapCreatureTiles !== "undefined") { for (var k = mapCreatureTiles.length - 1; k >= 0; k--) { if (mapCreatureTiles[k] === currentTile) { if (mapCreatureLabels[k]) { currentTile.removeChild(mapCreatureLabels[k]); mapCreatureLabels.splice(k, 1); } mapCreatures.splice(k, 1); mapCreatureTiles.splice(k, 1); break; } } } } } // Update last position lastPlayerGridX = player.gridX; lastPlayerGridY = player.gridY; } }; // End throttled update // Listen for encounter end var checkEncounterInterval = LK.setInterval(function () { if (encounterActive && !encounter.active) { encounterActive = false; } }, 100); // Initialize stats // Initial centering centerViewOnPlayer(); // Fix: Only reveal vision area at game start, not the whole map if (typeof player !== "undefined" && typeof gameMap !== "undefined" && typeof player.updateVision === "function") { player.updateVision(gameMap); } // Vision mask overlay removed to fix black screen issue
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1", {
discoveredCreatures: {},
playerPosition: {
x: 10,
y: 10
},
unlockedAreas: {
starter: true
}
});
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Collection class to manage discovered creatures
var Collection = Container.expand(function () {
var self = Container.call(this);
// Collection components
self.visible = false;
self.background = null;
self.scrollContainer = null;
self.closeButton = null;
// Initialize collection screen
self.init = function () {
// Create darkened background
self.background = self.attachAsset('grassTile', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732
});
self.background.alpha = 0.9;
self.background.tint = 0x000000;
// Create scroll container for creatures
self.scrollContainer = new Container();
self.addChild(self.scrollContainer);
self.scrollContainer.x = 50;
self.scrollContainer.y = 200;
// Add title
var titleText = new Text2('CREATURE COLLECTION', {
size: 80,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0);
titleText.x = 1024;
titleText.y = 50;
self.addChild(titleText);
// Create close button
self.closeButton = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1900,
y: 100
});
self.closeButton.interactive = true;
// Add X to close button
var closeText = new Text2('X', {
size: 60,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeText.x = 1900;
closeText.y = 100;
self.addChild(closeText);
// Hide initially
self.visible = false;
return self;
};
// Show collection screen and populate with discovered creatures
self.show = function () {
self.visible = true;
// Clear existing creatures
while (self.scrollContainer.children.length > 0) {
self.scrollContainer.removeChild(self.scrollContainer.children[0]);
}
// Get discovered creatures from storage
var discoveredCreatures = storage.discoveredCreatures || {};
var creatureCount = Object.keys(discoveredCreatures).length;
// Show count
var countText = new Text2('DISCOVERED: ' + creatureCount + ' / 90', {
size: 50,
fill: 0xFFFFFF
});
countText.anchor.set(0.5, 0);
countText.x = 1024;
countText.y = 150;
self.addChild(countText);
// Create grid of discovered creatures
var gridX = 0;
var gridY = 0;
var itemsPerRow = 5;
var cellSize = 380;
// First add "undiscovered" placeholders for all possible combinations
var allTypes = ['bug', 'dark', 'dragon', 'electric', 'fairy', 'fighting', 'fire', 'flying', 'ghost', 'grass', 'ground', 'ice', 'normal', 'poison', 'psychic', 'rock', 'steel', 'water'];
var allRarities = ['common', 'uncommon', 'rare', 'epic', 'legendary'];
for (var t = 0; t < allTypes.length; t++) {
for (var r = 0; r < allRarities.length; r++) {
var type = allTypes[t];
var rarity = allRarities[r];
var key = type + '-' + rarity;
// Calculate grid position
gridX = (allRarities.length * t + r) % itemsPerRow;
gridY = Math.floor((allRarities.length * t + r) / itemsPerRow);
// Create cell container
var cell = new Container();
cell.x = gridX * cellSize;
cell.y = gridY * cellSize;
self.scrollContainer.addChild(cell);
if (discoveredCreatures[key]) {
// Discovered creature
var creatureGraphic = LK.getAsset(type + 'Creature', {
anchorX: 0.5,
anchorY: 0.5,
x: cellSize / 2,
y: cellSize / 2 - 40
});
// Apply rarity styling
var rarityColor = 0xFFFFFF;
if (rarity === 'uncommon') rarityColor = 0x00FF00;
if (rarity === 'rare') rarityColor = 0x0000FF;
if (rarity === 'epic') rarityColor = 0xFF00FF;
if (rarity === 'legendary') rarityColor = 0xFFD700;
// Make rarer creatures slightly larger
var scale = 1.0;
if (rarity === 'uncommon') scale = 1.1;
if (rarity === 'rare') scale = 1.2;
if (rarity === 'epic') scale = 1.3;
if (rarity === 'legendary') scale = 1.5;
creatureGraphic.scale.set(scale, scale);
cell.addChild(creatureGraphic);
// Add info text
var infoText = new Text2(type.toUpperCase() + '\n' + rarity.toUpperCase(), {
size: 30,
fill: 0xFFFFFF
});
infoText.anchor.set(0.5, 0);
infoText.x = cellSize / 2;
infoText.y = cellSize / 2 + 40;
cell.addChild(infoText);
} else {
// Undiscovered creature (shadow)
var undiscoveredGraphic = LK.getAsset(type + 'Creature', {
anchorX: 0.5,
anchorY: 0.5,
x: cellSize / 2,
y: cellSize / 2
});
undiscoveredGraphic.alpha = 0.2;
undiscoveredGraphic.tint = 0x000000;
cell.addChild(undiscoveredGraphic);
// Add question mark
var questionText = new Text2('?', {
size: 60,
fill: 0x999999
});
questionText.anchor.set(0.5, 0.5);
questionText.x = cellSize / 2;
questionText.y = cellSize / 2;
cell.addChild(questionText);
}
}
}
};
// Hide collection screen
self.hide = function () {
self.visible = false;
};
return self;
});
// Creature class for encounters
var Creature = Container.expand(function () {
var self = Container.call(this);
// --- Creature Database: 100 unique creatures with names, images, and evolutions ---
self.creatureList = [
// Example: {id: 0, name: "Sproutle", type: "grass", image: "grassCreature", evolvesTo: 1}
// ... 100 creatures, each with unique name, type, image, and evolution chain
];
// Generate 100 unique creatures with types, images, and names
(function () {
var types = ['bug', 'dark', 'dragon', 'electric', 'fairy', 'fighting', 'fire', 'flying', 'ghost', 'grass', 'ground', 'ice', 'normal', 'poison', 'psychic', 'rock', 'steel', 'water'];
var baseNames = ["Sproutle", "Aquapup", "Pyrokit", "Stonox", "Zephyro", "Lumina", "Frosty", "Venoma", "Spectra", "Boulder", "Voltix", "Mystwing", "Shadeon", "Petalyn", "Scorcher", "Chillfin", "Ironclad", "Pebblit", "Gustlet", "Dewdrop", "Emberly", "Thornet", "Mossy", "Duskleaf", "Blazetail", "Crystowl", "Sableye", "Toxifin", "Glimmer", "Rubble", "Cinderpaw", "Leaflet", "Mudkip", "Glacier", "Fanglet", "Wispurr", "Bramble", "Sparky", "Tidelet", "Cobalite", "Garnet", "Quartz", "Onyx", "Topaz", "Opal", "Jade", "Amber", "Ruby", "Sapphire", "Emerald", "Coral", "Shellby", "Ripple", "Torrent", "Breeze", "Nimbus", "Tempest", "Cyclone", "Blizzard", "Icicle", "Flicker", "Glowbug", "Mothra", "Scarab", "Beetle", "Antler", "Staggle", "Hornet", "Buzzly", "Flutter", "Puddle", "Splashy", "Drizzle", "Rainy", "Stormy", "Thunder", "Bolt", "Sparkle", "Shocker", "Zaplet", "Mystic", "Rune", "Oracle", "Wraith", "Shade", "Phantom", "Polter", "Ghast", "Specter", "Spirit", "Bash", "Crush", "Smash", "Pummel", "Thump", "Rumble", "Gravel", "Dusty", "Rocky", "Pebble"];
// Shuffle baseNames for uniqueness
for (var i = baseNames.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = baseNames[i];
baseNames[i] = baseNames[j];
baseNames[j] = temp;
}
// Assign types and images in a round-robin fashion
for (var i = 0; i < 100; i++) {
var type = types[i % types.length];
var name = baseNames[i % baseNames.length] + (i >= baseNames.length ? String(i) : "");
var image = type + "Creature";
var evolvesTo = i + 1 < 100 && Math.random() < 0.7 ? i + 1 : null; // 70% chance to evolve to next
self.creatureList.push({
id: i,
name: name,
type: type,
image: image,
evolvesTo: evolvesTo
});
}
})();
// Creature properties
self.creatureId = 0; // Index in creatureList
self.type = 'normal'; // Default type
self.rarity = 'common'; // Default rarity
self.gridX = 0;
self.gridY = 0;
self.captured = false;
self.name = '';
self.image = '';
self.evolvesTo = null;
// Rarity colors
self.rarityColors = {
common: 0xFFFFFF,
uncommon: 0x00FF00,
rare: 0x0000FF,
epic: 0xFF00FF,
legendary: 0xFFD700
};
// Type to biome mapping
self.typeBiomeMap = {
bug: 'forest',
dark: 'mountain',
dragon: 'mountain',
electric: 'grass',
fairy: 'forest',
fighting: 'mountain',
fire: 'desert',
flying: 'grass',
ghost: 'mountain',
grass: 'grass',
ground: 'desert',
ice: 'mountain',
normal: 'grass',
poison: 'forest',
psychic: 'grass',
rock: 'mountain',
steel: 'urban',
water: 'water'
};
// Initialize creature with specific id, or type/rarity/position
self.init = function (type, rarity, gridX, gridY, creatureId) {
// If creatureId is provided, use it
if (typeof creatureId === "number" && self.creatureList[creatureId]) {
var c = self.creatureList[creatureId];
self.creatureId = c.id;
self.type = c.type;
self.name = c.name;
self.image = c.image;
self.evolvesTo = c.evolvesTo;
} else {
// Pick a random creature for the biome
var candidates = [];
for (var i = 0; i < self.creatureList.length; i++) {
if (!type || self.creatureList[i].type === type) {
candidates.push(self.creatureList[i]);
}
}
var c = candidates[Math.floor(Math.random() * candidates.length)];
self.creatureId = c.id;
self.type = c.type;
self.name = c.name;
self.image = c.image;
self.evolvesTo = c.evolvesTo;
}
self.rarity = rarity || self.getRandomRarity();
self.gridX = gridX;
self.gridY = gridY;
// Create creature graphic
var creatureGraphics = self.attachAsset(self.image, {
anchorX: 0.5,
anchorY: 0.5
});
// Apply rarity visual effect (glow or color)
var rarityColor = self.rarityColors[self.rarity] || 0xFFFFFF;
// Add glow effect for rare+ creatures
if (self.rarity !== 'common') {
// Make rarer creatures slightly larger
var scale = 1.0;
if (self.rarity === 'uncommon') scale = 1.1;
if (self.rarity === 'rare') scale = 1.2;
if (self.rarity === 'epic') scale = 1.3;
if (self.rarity === 'legendary') scale = 1.5;
creatureGraphics.scale.set(scale, scale);
}
// Position creature
self.x = self.gridX * TILE_SIZE + TILE_SIZE / 2; // Center of tile
self.y = self.gridY * TILE_SIZE + TILE_SIZE / 2; // Center of tile
// Add name label
var nameText = new Text2(self.name, {
size: 36,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.x = 0;
nameText.y = 60;
self.addChild(nameText);
// If this creature can evolve, show evolution arrow
if (self.evolvesTo !== null && typeof self.creatureList[self.evolvesTo] !== "undefined") {
var evoText = new Text2("Evolves to: " + self.creatureList[self.evolvesTo].name, {
size: 24,
fill: 0xFFD700
});
evoText.anchor.set(0.5, 0);
evoText.x = 0;
evoText.y = 100;
self.addChild(evoText);
}
return self;
};
// Generate a random creature type, weighted by biome
self.getRandomType = function (biome) {
// Default types for each biome
var biomeTypes = {
grass: ['normal', 'grass', 'bug', 'flying', 'electric', 'psychic'],
water: ['water', 'ice', 'flying'],
desert: ['ground', 'fire', 'rock'],
forest: ['bug', 'grass', 'poison', 'fairy'],
mountain: ['rock', 'fighting', 'dragon', 'dark', 'ghost', 'ice'],
street: ['steel', 'electric', 'poison', 'normal', 'fighting'],
urban: ['steel', 'electric', 'normal', 'psychic']
};
// Get types for the provided biome, or use all types
var types = biome ? biomeTypes[biome] : Object.keys(self.typeBiomeMap);
// Select random type from available options
return types[Math.floor(Math.random() * types.length)];
};
// Generate a random rarity based on configured probabilities
self.getRandomRarity = function () {
var rand = Math.random();
if (rand < 0.65) return 'common';
if (rand < 0.85) return 'uncommon';
if (rand < 0.95) return 'rare';
if (rand < 0.99) return 'epic';
return 'legendary';
};
// Animate creature during encounter
self.animate = function () {
// Bounce animation
var originalY = self.y;
// Use tween to create a bouncing effect
tween(self, {
y: originalY - 20
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: originalY
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Repeat animation
self.animate();
}
});
}
});
};
// Stop animations
self.stopAnimation = function () {
tween.stop(self, {
y: true
});
};
// Evolve this creature to its next form, if possible
self.evolve = function () {
if (self.evolvesTo !== null && typeof self.creatureList[self.evolvesTo] !== "undefined") {
var next = self.creatureList[self.evolvesTo];
// Remove all children (graphics, name, evo text)
while (self.children.length > 0) self.removeChild(self.children[0]);
self.creatureId = next.id;
self.type = next.type;
self.name = next.name;
self.image = next.image;
self.evolvesTo = next.evolvesTo;
// Re-add graphics and labels
var creatureGraphics = self.attachAsset(self.image, {
anchorX: 0.5,
anchorY: 0.5
});
creatureGraphics.scale.set(1.2, 1.2);
var nameText = new Text2(self.name, {
size: 36,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.x = 0;
nameText.y = 60;
self.addChild(nameText);
if (self.evolvesTo !== null && typeof self.creatureList[self.evolvesTo] !== "undefined") {
var evoText = new Text2("Evolves to: " + self.creatureList[self.evolvesTo].name, {
size: 24,
fill: 0xFFD700
});
evoText.anchor.set(0.5, 0);
evoText.x = 0;
evoText.y = 100;
self.addChild(evoText);
}
}
};
return self;
});
// Directional pad control for player movement
var DPad = Container.expand(function () {
var self = Container.call(this);
// DPad components
self.padBase = null;
self.upButton = null;
self.downButton = null;
self.leftButton = null;
self.rightButton = null;
// Initialize dpad with buttons
self.init = function () {
// Create base of DPad
self.padBase = self.attachAsset('dpad', {
anchorX: 0.5,
anchorY: 0.5
});
// Create directional buttons
self.upButton = self.createButton(0, -65);
self.downButton = self.createButton(0, 65);
self.leftButton = self.createButton(-65, 0);
self.rightButton = self.createButton(65, 0);
// Position the entire DPad in the bottom left of the screen
self.x = 200;
self.y = 2500;
return self;
};
// Create a directional button at relative x,y from center
self.createButton = function (relX, relY) {
var button = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: relX,
y: relY
});
button.interactive = true;
return button;
};
return self;
});
// Encounter class to handle creature encounters
var Encounter = Container.expand(function () {
var self = Container.call(this);
// Encounter state
self.active = false;
self.creature = null;
self.captureRing = null;
self.captureTarget = null;
self.targetSpeed = 5;
self.targetDirection = 1;
self.captureAttempts = 0;
self.maxAttempts = 3;
// Initialize encounter screen
self.init = function () {
// Darkened background
var bg = self.attachAsset('grassTile', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732
});
bg.alpha = 0.7;
bg.tint = 0x000000;
// Initially hidden
self.visible = false;
return self;
};
// Start encounter with a specific creature
self.startEncounter = function (creature) {
self.active = true;
self.visible = true;
self.creature = creature;
self.captureAttempts = 0;
// --- Pokeball selection UI ---
self.selectedPokeball = 'basic'; // Default
// Only allow selection of pokeballs the player has
function getAvailablePokeballs() {
var arr = [];
for (var i = 0; i < pokeballTypes.length; i++) {
if (playerPokeballs[pokeballTypes[i].rarity] > 0) arr.push(pokeballTypes[i]);
}
return arr;
}
var availableBalls = getAvailablePokeballs();
if (availableBalls.length === 0) {
// No pokeballs, cannot attempt capture
var noBallText = new Text2('No Pokeballs!\nFind more to catch creatures.', {
size: 70,
fill: 0xff0000
});
noBallText.anchor.set(0.5, 0.5);
noBallText.x = 1024;
noBallText.y = 1500;
self.addChild(noBallText);
LK.setTimeout(function () {
self.endEncounter();
}, 1800);
return;
}
// Show pokeball selection buttons
self.pokeballButtons = [];
for (var i = 0; i < pokeballTypes.length; i++) {
var pType = pokeballTypes[i];
var btn = self.attachAsset('captureRing', {
anchorX: 0.5,
anchorY: 0.5,
x: 700 + i * 200,
y: 1600
});
btn.scale.set(0.3, 0.3);
btn.tint = pType.color;
btn.interactive = true;
(function (type) {
btn.down = function () {
self.selectedPokeball = type.rarity;
// Highlight selected
for (var j = 0; j < self.pokeballButtons.length; j++) {
self.pokeballButtons[j].alpha = self.pokeballButtons[j].rarity === type.rarity ? 1.0 : 0.5;
}
};
})(pType);
btn.rarity = pType.rarity;
if (playerPokeballs[pType.rarity] <= 0) btn.alpha = 0.2;
self.addChild(btn);
self.pokeballButtons.push(btn);
// Add label
var label = new Text2(pType.name + ' (' + (playerPokeballs[pType.rarity] || 0) + ')', {
size: 30,
fill: pType.color
});
label.anchor.set(0.5, 0);
label.x = 700 + i * 200;
label.y = 1670;
self.addChild(label);
if (i === 0) btn.alpha = 1.0;else btn.alpha = 0.5;
}
// Add creature to encounter screen
self.addChild(creature);
creature.x = 1024; // Center horizontally
creature.y = 1000; // Position in upper portion of screen
// Make creature interactive to allow clicking directly on it
creature.interactive = true;
creature.down = function () {
self.attemptCapture();
};
// Start creature animation
creature.animate();
// Create capture interface
self.createCaptureInterface();
// Play encounter sound
LK.getSound('encounter').play();
};
// Create the capture ring and target interface
self.createCaptureInterface = function () {
// Capture ring
self.captureRing = self.attachAsset('captureRing', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
// Center horizontally
y: 1800 // Lower portion of screen
});
// Moving target
self.captureTarget = self.attachAsset('captureTarget', {
anchorX: 0.5,
anchorY: 0.5,
x: 964,
// Start position (left side of ring)
y: 1800 // Same Y as ring
});
// Capture button
var captureButton = self.attachAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2200,
width: 300,
height: 100
});
captureButton.tint = 0xFF0000;
// Capture button text
var captureText = new Text2('CAPTURE', {
size: 60,
fill: 0xFFFFFF
});
captureText.anchor.set(0.5, 0.5);
captureText.x = 1024;
captureText.y = 2200;
self.addChild(captureText);
// Capture button event
captureButton.interactive = true;
captureButton.down = function () {
self.attemptCapture();
};
// Leave button
var leaveButton = self.attachAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
x: 1700,
y: 2200,
width: 300,
height: 100
});
leaveButton.tint = 0x888888;
var leaveText = new Text2('LEAVE', {
size: 60,
fill: 0xFFFFFF
});
leaveText.anchor.set(0.5, 0.5);
leaveText.x = 1700;
leaveText.y = 2200;
self.addChild(leaveText);
leaveButton.interactive = true;
leaveButton.down = function () {
// End encounter immediately, treat as fail/escape
self.endEncounter();
};
// Info text about the creature
var infoText = new Text2('TYPE: ' + self.creature.type.toUpperCase() + '\n' + 'RARITY: ' + self.creature.rarity.toUpperCase() + '\n' + 'ATTEMPTS: ' + (self.maxAttempts - self.captureAttempts) + ' REMAINING', {
size: 50,
fill: 0xFFFFFF
});
infoText.anchor.set(0.5, 0);
infoText.x = 1024;
infoText.y = 2300;
self.addChild(infoText);
};
// Update function for moving the target
self.update = function () {
if (!self.active || !self.captureTarget) return;
// Move target back and forth across the ring
self.captureTarget.x += self.targetSpeed * self.targetDirection;
// Reverse direction at edges
if (self.captureTarget.x > 1084) {
// Right edge of ring
self.targetDirection = -1;
} else if (self.captureTarget.x < 964) {
// Left edge of ring
self.targetDirection = 1;
}
// Make target move faster based on creature rarity
var speedMultiplier = 1;
if (self.creature) {
if (self.creature.rarity === 'uncommon') speedMultiplier = 1.2;
if (self.creature.rarity === 'rare') speedMultiplier = 1.5;
if (self.creature.rarity === 'epic') speedMultiplier = 1.8;
if (self.creature.rarity === 'legendary') speedMultiplier = 2.2;
}
self.targetSpeed = 5 * speedMultiplier;
};
// Attempt to capture the creature
self.attemptCapture = function () {
// Play capture sound
LK.getSound('capture').play();
self.captureAttempts++;
// Use selected pokeball, reduce inventory
var selectedType = pokeballTypes[0];
for (var i = 0; i < pokeballTypes.length; i++) {
if (pokeballTypes[i].rarity === self.selectedPokeball) {
selectedType = pokeballTypes[i];
break;
}
}
if (playerPokeballs[selectedType.rarity] > 0) {
playerPokeballs[selectedType.rarity]--;
updatePokeballInventoryText();
// Update pokeball button labels
if (self.pokeballButtons) {
for (var i = 0; i < self.pokeballButtons.length; i++) {
var labelY = 1670;
var label = null;
for (var j = 0; j < self.children.length; j++) {
if (self.children[j] instanceof Text2 && self.children[j].y === labelY && self.children[j].text.indexOf(pokeballTypes[i].name) === 0) {
label = self.children[j];
break;
}
}
if (label) label.setText(pokeballTypes[i].name + ' (' + (playerPokeballs[pokeballTypes[i].rarity] || 0) + ')');
if (playerPokeballs[pokeballTypes[i].rarity] <= 0) self.pokeballButtons[i].alpha = 0.2;
}
}
}
// Check if target is in center zone (successful capture)
var distanceFromCenter = Math.abs(self.captureTarget.x - 1024);
var successZone = 15; // Size of "perfect" zone
// Adjust success zone based on rarity (harder for rarer creatures)
if (self.creature.rarity === 'uncommon') successZone = 13;
if (self.creature.rarity === 'rare') successZone = 10;
if (self.creature.rarity === 'epic') successZone = 8;
if (self.creature.rarity === 'legendary') successZone = 5;
var captured = false;
if (distanceFromCenter <= successZone) {
// Use pokeball catch bonus
var baseChance = 0.5;
if (self.creature.rarity === 'common') baseChance = 0.5;
if (self.creature.rarity === 'uncommon') baseChance = 0.35;
if (self.creature.rarity === 'rare') baseChance = 0.2;
if (self.creature.rarity === 'epic') baseChance = 0.1;
if (self.creature.rarity === 'legendary') baseChance = 0.05;
var finalChance = baseChance + (selectedType.catchBonus || 0);
if (Math.random() < finalChance) captured = true;
}
if (captured) {
// Success!
self.captureSuccess();
} else if (self.captureAttempts >= self.maxAttempts) {
// Failed after max attempts
self.captureFail();
} else {
// Update attempts remaining text
var remainingText = self.children.filter(function (child) {
return child instanceof Text2 && child.y === 2300;
})[0];
if (remainingText) {
remainingText.setText('TYPE: ' + self.creature.type.toUpperCase() + '\n' + 'RARITY: ' + self.creature.rarity.toUpperCase() + '\n' + 'ATTEMPTS: ' + (self.maxAttempts - self.captureAttempts) + ' REMAINING');
}
}
};
// Handle successful capture
self.captureSuccess = function () {
// Play success sound
LK.getSound('success').play();
// Add to collection
if (!storage.discoveredCreatures) {
storage.discoveredCreatures = {};
}
var creatureKey = self.creature.type + '-' + self.creature.rarity;
storage.discoveredCreatures[creatureKey] = true;
// Mark the tile as caught so it cannot be encountered again until respawn
if (typeof gameMap !== "undefined" && typeof player !== "undefined") {
var tile = gameMap.getTileAt(player.gridX, player.gridY);
if (tile) {
tile._creatureCaught = true;
tile.hasCreature = false;
// Remove creature visual if present
if (tile._creatureVisual) {
tile.removeChild(tile._creatureVisual);
tile._creatureVisual = null;
}
// Remove encounterIndicator if present (yellow circle)
for (var i = tile.children.length - 1; i >= 0; i--) {
if (tile.children[i].assetId === 'encounterIndicator') {
tile.removeChild(tile.children[i]);
}
}
}
}
// Show success message
var successText = new Text2('CAPTURED!', {
size: 100,
fill: 0x00FF00
});
successText.anchor.set(0.5, 0.5);
successText.x = 1024;
successText.y = 1400;
self.addChild(successText);
// Flash screen
LK.effects.flashScreen(0x00FF00, 500);
// End encounter after delay
LK.setTimeout(function () {
self.endEncounter();
}, 1500);
};
// Handle failed capture
self.captureFail = function () {
// Show fail message
var failText = new Text2('ESCAPED!', {
size: 100,
fill: 0xFF0000
});
failText.anchor.set(0.5, 0.5);
failText.x = 1024;
failText.y = 1400;
self.addChild(failText);
// Flash screen
LK.effects.flashScreen(0xFF0000, 500);
// Despawn the creature from the map if present
if (typeof gameMap !== "undefined" && typeof player !== "undefined") {
var tile = gameMap.getTileAt(player.gridX, player.gridY);
if (tile) {
tile.hasCreature = false;
tile._creatureCaught = false; // Not caught, but despawned
// Remove indicator if present
for (var i = tile.children.length - 1; i >= 0; i--) {
if (tile.children[i].assetId === 'encounterIndicator') {
tile.removeChild(tile.children[i]);
}
}
// Remove from mapCreatures/mapCreatureTiles/mapCreatureLabels if present
if (typeof mapCreatureTiles !== "undefined") {
for (var k = mapCreatureTiles.length - 1; k >= 0; k--) {
if (mapCreatureTiles[k] === tile) {
if (mapCreatureLabels[k]) {
tile.removeChild(mapCreatureLabels[k]);
mapCreatureLabels.splice(k, 1);
}
mapCreatures.splice(k, 1);
mapCreatureTiles.splice(k, 1);
}
}
}
}
}
// End encounter after delay
LK.setTimeout(function () {
self.endEncounter();
}, 1500);
};
// End the current encounter
self.endEncounter = function () {
if (self.creature) {
self.creature.stopAnimation();
}
self.active = false;
self.visible = false;
// Remove all children
while (self.children.length > 0) {
self.removeChild(self.children[0]);
}
self.creature = null;
self.captureRing = null;
self.captureTarget = null;
// Reinitialize for next encounter
self.init();
// Also set encounterActive to false so game resumes
if (typeof encounterActive !== "undefined") {
encounterActive = false;
}
};
return self;
});
// GameMap represents the entire game map with different biomes
var GameMap = Container.expand(function () {
var self = Container.call(this);
self.mapWidth = 60; // Number of tiles horizontally (max size, increased)
self.mapHeight = 60; // Number of tiles vertically (max size, increased)
self.tileSize = TILE_SIZE; // Size of each tile in pixels (increased to match TILE_SIZE)
self.tiles = []; // 2D array to hold all map tiles
// Biome generation parameters
self.biomeTypes = ['grass', 'water', 'desert', 'forest', 'mountain', 'street', 'urban'];
self.biomeSeeds = []; // Points from which biomes spread
// Initialize map with biomes
self.initMap = function () {
// Create empty 2D array for tiles
for (var y = 0; y < self.mapHeight; y++) {
self.tiles[y] = [];
for (var x = 0; x < self.mapWidth; x++) {
self.tiles[y][x] = null;
}
}
// Place biome seeds
self.placeBiomeSeeds();
// Generate biomes from seeds
self.generateBiomes();
// --- Flood fill to ensure all tiles are connected (no black dots) ---
var visited = [];
for (var y = 0; y < self.mapHeight; y++) {
visited[y] = [];
for (var x = 0; x < self.mapWidth; x++) {
visited[y][x] = false;
}
}
// Start flood fill from center of map
var startX = Math.floor(self.mapWidth / 2);
var startY = Math.floor(self.mapHeight / 2);
var startBiome = self.tiles[startY][startX];
var queue = [{
x: startX,
y: startY
}];
visited[startY][startX] = true;
while (queue.length > 0) {
var pos = queue.shift();
var dirs = [{
dx: 1,
dy: 0
}, {
dx: -1,
dy: 0
}, {
dx: 0,
dy: 1
}, {
dx: 0,
dy: -1
}];
for (var d = 0; d < dirs.length; d++) {
var nx = pos.x + dirs[d].dx;
var ny = pos.y + dirs[d].dy;
if (nx >= 0 && nx < self.mapWidth && ny >= 0 && ny < self.mapHeight && !visited[ny][nx]) {
// Only connectable if not "dark"
if (self.tiles[ny][nx] !== "dark") {
visited[ny][nx] = true;
queue.push({
x: nx,
y: ny
});
}
}
}
}
// Any tile not visited is unreachable, so set it to the most common neighbor biome
for (var y = 0; y < self.mapHeight; y++) {
for (var x = 0; x < self.mapWidth; x++) {
if (!visited[y][x]) {
// Find most common neighbor biome (not dark)
var biomeCount = {};
for (var dy = -1; dy <= 1; dy++) {
for (var dx = -1; dx <= 1; dx++) {
var nx = x + dx;
var ny = y + dy;
if (nx >= 0 && nx < self.mapWidth && ny >= 0 && ny < self.mapHeight && (dx !== 0 || dy !== 0)) {
var b = self.tiles[ny][nx];
if (b && b !== "dark") {
if (!biomeCount[b]) biomeCount[b] = 0;
biomeCount[b]++;
}
}
}
}
// Pick the most common neighbor biome, fallback to "grass"
var maxCount = 0;
var bestBiome = "grass";
for (var b in biomeCount) {
if (biomeCount[b] > maxCount) {
maxCount = biomeCount[b];
bestBiome = b;
}
}
self.tiles[y][x] = bestBiome;
}
}
}
// Create tiles based on biome map
self.createTiles();
return self;
};
// Place seed points for biome generation
self.placeBiomeSeeds = function () {
// Place multiple seeds for each biome for more, smaller biomes
for (var i = 0; i < self.biomeTypes.length; i++) {
var seedsPerBiome = 3; // Default for most biomes
// Make mountain biomes much smaller by reducing their seed count
if (self.biomeTypes[i] === 'mountain') {
seedsPerBiome = 1; // Only 1 seed for mountain biome for much smaller area
}
for (var j = 0; j < seedsPerBiome; j++) {
var seed = {
x: Math.floor(Math.random() * self.mapWidth),
y: Math.floor(Math.random() * self.mapHeight),
biome: self.biomeTypes[i]
};
self.biomeSeeds.push(seed);
}
}
};
// Generate biomes from seed points using a simple distance-based algorithm
self.generateBiomes = function () {
for (var y = 0; y < self.mapHeight; y++) {
for (var x = 0; x < self.mapWidth; x++) {
// Find closest biome seed
var closestDist = Number.MAX_VALUE;
var closestBiome = 'grass'; // Default
for (var i = 0; i < self.biomeSeeds.length; i++) {
var seed = self.biomeSeeds[i];
// Reduce the effective radius for each seed, so biomes are smaller
var dist = Math.sqrt(Math.pow(x - seed.x, 2) + Math.pow(y - seed.y, 2)) * 0.35; // Reduce multiplier for smaller biomes
// Make mountain biomes even smaller by increasing the distance multiplier
if (seed.biome === 'mountain') {
dist *= 3.5; // Make mountain biomes much smaller
}
// Add more randomness to make borders less regular and biomes patchier
dist += Math.random() * 6 - 3;
if (dist < closestDist) {
closestDist = dist;
closestBiome = seed.biome;
}
}
self.tiles[y][x] = closestBiome;
}
}
// Add some randomness and blending between biomes
self.refineBiomes();
};
// Add more natural transitions between biomes with street patterns
self.refineBiomes = function () {
// Create a deep copy of tiles array
var tempTiles = [];
for (var y = 0; y < self.mapHeight; y++) {
tempTiles[y] = [];
for (var x = 0; x < self.mapWidth; x++) {
tempTiles[y][x] = self.tiles[y][x];
}
}
// Create street grid patterns
// Find street biome seed
var streetSeedX = -1;
var streetSeedY = -1;
for (var i = 0; i < self.biomeSeeds.length; i++) {
if (self.biomeSeeds[i].biome === 'street') {
streetSeedX = self.biomeSeeds[i].x;
streetSeedY = self.biomeSeeds[i].y;
break;
}
}
// If we have a street seed, create grid pattern around it
if (streetSeedX >= 0 && streetSeedY >= 0) {
// Create street grid - horizontal and vertical streets
var streetRadius = 8; // Size of urban area
var streetGridSpacing = 3; // Distance between streets
for (var y = Math.max(0, streetSeedY - streetRadius); y < Math.min(self.mapHeight, streetSeedY + streetRadius); y++) {
for (var x = Math.max(0, streetSeedX - streetRadius); x < Math.min(self.mapWidth, streetSeedX + streetRadius); x++) {
// Check if within street biome core radius
var distToSeed = Math.sqrt(Math.pow(x - streetSeedX, 2) + Math.pow(y - streetSeedY, 2));
if (distToSeed <= streetRadius) {
// Street grid pattern: create roads at regular intervals
if (x % streetGridSpacing === 0 || y % streetGridSpacing === 0) {
self.tiles[y][x] = 'street';
}
// Building blocks between streets
else if (self.tiles[y][x] === 'street') {
// Determine building type based on position in grid - some variety within urban areas
if ((x + y) % 2 === 0) {
self.tiles[y][x] = 'street'; // Keep as urban (tall buildings)
} else {
// Mix with nearby biomes occasionally for parks, etc.
if (Math.random() < 0.3) {
var nearbyNonStreetBiome = '';
// Look for any non-street biome nearby
for (var ny = y - 1; ny <= y + 1; ny++) {
for (var nx = x - 1; nx <= x + 1; nx++) {
if (ny >= 0 && ny < self.mapHeight && nx >= 0 && nx < self.mapWidth) {
if (self.tiles[ny][nx] !== 'street' && self.tiles[ny][nx] !== undefined) {
nearbyNonStreetBiome = self.tiles[ny][nx];
break;
}
}
}
if (nearbyNonStreetBiome) break;
}
// If found nearby non-street biome, use it, otherwise keep as street
if (nearbyNonStreetBiome) {
self.tiles[y][x] = nearbyNonStreetBiome;
}
}
}
}
}
}
}
}
// Now apply regular biome smoothing
for (var y = 1; y < self.mapHeight - 1; y++) {
for (var x = 1; x < self.mapWidth - 1; x++) {
// Skip street grid areas
var distToStreetSeed = streetSeedX >= 0 ? Math.sqrt(Math.pow(x - streetSeedX, 2) + Math.pow(y - streetSeedY, 2)) : Number.MAX_VALUE;
if (distToStreetSeed <= streetRadius && (x % streetGridSpacing === 0 || y % streetGridSpacing === 0)) {
continue; // Skip street grid lines
}
// Count neighboring biomes
var biomeCount = {};
for (var ny = y - 1; ny <= y + 1; ny++) {
for (var nx = x - 1; nx <= x + 1; nx++) {
// Check that neighbor coordinates are valid
if (ny >= 0 && ny < self.mapHeight && nx >= 0 && nx < self.mapWidth) {
var neighborBiome = tempTiles[ny][nx];
if (neighborBiome) {
if (!biomeCount[neighborBiome]) {
biomeCount[neighborBiome] = 0;
}
biomeCount[neighborBiome]++;
}
}
}
}
// Random chance to convert to most common neighbor
if (Math.random() < 0.4) {
var mostCommon = tempTiles[y][x];
var maxCount = 0;
for (var biome in biomeCount) {
if (biomeCount.hasOwnProperty(biome) && biomeCount[biome] > maxCount) {
maxCount = biomeCount[biome];
mostCommon = biome;
}
}
self.tiles[y][x] = mostCommon;
}
}
}
};
// Create actual tile objects based on the biome map
self.createTiles = function () {
for (var y = 0; y < self.mapHeight; y++) {
for (var x = 0; x < self.mapWidth; x++) {
var biomeType = self.tiles[y][x];
var tile = new MapTile().initTile(biomeType, x, y);
tile.tileSize = TILE_SIZE;
self.addChild(tile);
}
}
};
// Get tile at grid coordinates
self.getTileAt = function (gridX, gridY) {
// Ensure coordinates are within bounds
if (gridX < 0 || gridX >= self.mapWidth || gridY < 0 || gridY >= self.mapHeight) {
return null;
}
// Find the tile in the children
for (var i = 0; i < self.children.length; i++) {
var tile = self.children[i];
if (tile.gridX === gridX && tile.gridY === gridY) {
return tile;
}
}
return null;
};
return self;
});
// MapTile represents a single tile in the game grid
var MapTile = Container.expand(function () {
var self = Container.call(this);
self.biomeType = 'grass'; // Default biome
self.tileSize = TILE_SIZE; // Set to match TILE_SIZE for correct movement alignment
self.hasCreature = false;
self.explored = false;
self.x = 0;
self.y = 0;
self.gridX = 0;
self.gridY = 0;
// Initialize the tile with a specific biome
self.initTile = function (biomeType, gridX, gridY) {
self.biomeType = biomeType;
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * self.tileSize;
self.y = gridY * self.tileSize;
// Get the appropriate tile asset based on biome
var assetId = self.biomeType + 'Tile';
// Use unique image for urban biome
if (self.biomeType === 'urban') assetId = 'urbanTile';
// Create a container to hold 4 quadrant images
self.quadrantTiles = [];
for (var qy = 0; qy < 2; qy++) {
for (var qx = 0; qx < 2; qx++) {
var quad = self.attachAsset(assetId, {
anchorX: 0,
anchorY: 0,
width: TILE_SIZE / 2 + 4,
// Add 4px overlap to eliminate black dots
height: TILE_SIZE / 2 + 4,
// Add 4px overlap to eliminate black dots
x: qx * TILE_SIZE / 2 - (qx > 0 ? 2 : 0),
y: qy * TILE_SIZE / 2 - (qy > 0 ? 2 : 0)
});
quad.alpha = 0.9;
self.quadrantTiles.push(quad);
}
}
// No fog overlay for undiscovered tiles (fog removed)
self.fogOverlay = null;
// Random chance to spawn a creature based on biome (further reduced for less frequent spawning)
if (Math.random() < 0.02) {
self.hasCreature = true;
var indicator = self.attachAsset('encounterIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
indicator.alpha = 0.7;
}
return self;
};
// Mark tile as explored
self.explore = function () {
if (!self.explored) {
self.explored = true;
// Hide fog overlay if present
if (self.fogOverlay) {
self.fogOverlay.visible = false;
}
// Add to explored areas in storage
var exploredKey = self.gridX + "," + self.gridY;
if (!storage.exploredTiles) storage.exploredTiles = {};
storage.exploredTiles[exploredKey] = true;
}
};
return self;
});
// MiniMap class to show explored areas
var MiniMap = Container.expand(function () {
var self = Container.call(this);
// MiniMap components
self.mapBackground = null;
self.playerMarker = null;
self.tileMarkers = [];
self.mapSize = 300;
self.scale = 0.1; // How much of the full map to show
// Initialize minimap
self.init = function () {
// Create background
self.mapBackground = self.attachAsset('minimap', {
anchorX: 0,
anchorY: 0,
width: self.mapSize,
height: self.mapSize
});
// Create player marker
self.playerMarker = self.attachAsset('minimapMarker', {
anchorX: 0.5,
anchorY: 0.5,
x: self.mapSize / 2,
y: self.mapSize / 2
});
// Position minimap in top right corner
self.x = 2048 - self.mapSize - 20;
self.y = 20;
return self;
};
// Update minimap with current player position and explored tiles
self.update = function (player, gameMap) {
// Check if player and gameMap are defined
if (!player || !gameMap) return;
// Update player marker position
var centerX = self.mapSize / 2;
var centerY = self.mapSize / 2;
// Position marker relative to center
self.playerMarker.x = centerX;
self.playerMarker.y = centerY;
// Clear existing tile markers
for (var i = 0; i < self.tileMarkers.length; i++) {
self.removeChild(self.tileMarkers[i]);
}
self.tileMarkers = [];
// Add markers for visible tiles around player
var visionRadius = 2; // Match the reduced player vision radius
for (var y = Math.floor(player.gridY - visionRadius); y <= Math.ceil(player.gridY + visionRadius); y++) {
for (var x = Math.floor(player.gridX - visionRadius); x <= Math.ceil(player.gridX + visionRadius); x++) {
var dx = x - player.gridX;
var dy = y - player.gridY;
if (Math.sqrt(dx * dx + dy * dy) <= visionRadius + 0.2) {
var tile = gameMap.getTileAt(x, y);
if (tile && tile.explored) {
// Create marker with biome color
var color = 0x33cc33; // Default grass
if (tile.biomeType === 'water') color = 0x3399ff;
if (tile.biomeType === 'desert') color = 0xe6cc99;
if (tile.biomeType === 'forest') color = 0x006600;
if (tile.biomeType === 'mountain') color = 0x999999;
if (tile.biomeType === 'street') color = 0x666666;
if (tile.biomeType === 'urban') color = 0x4444aa;
var marker = new Container();
var markerGraphics = LK.getAsset('grassTile', {
anchorX: 0,
anchorY: 0,
width: 10,
height: 10
});
markerGraphics.tint = color;
marker.addChild(markerGraphics);
// Position marker relative to player
marker.x = centerX + (x - player.gridX) * 10;
marker.y = centerY + (y - player.gridY) * 10;
self.addChild(marker);
self.tileMarkers.push(marker);
}
}
}
}
};
return self;
});
// Player character with integrated vision logic
var Player = Container.expand(function () {
var self = Container.call(this);
// Player properties
self.gridX = 10; // Starting X position on grid (max map)
self.gridY = 10; // Starting Y position on grid (max map)
self.moveSpeed = 0.2; // Movement animation speed
self.moving = false;
self.lastGridX = 10;
self.lastGridY = 10;
self.visionRadius = 2; // Reduced vision radius in blocks
// Remove sub-tile logic: always center player in tile
self.subTileX = 0;
self.subTileY = 0;
// New: quadrant label
self.quadrantLabel = null;
// Initialize player with graphics
self.init = function () {
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
y: -44 // Move player image 44px higher for better visual centering
});
// Make player a bit bigger for better visibility
playerGraphics.scale.set(0.34, 0.34);
// Add quadrant label
self.quadrantLabel = new Text2('', {
size: 40,
fill: 0xffff00
});
self.quadrantLabel.anchor.set(0.5, 1);
self.quadrantLabel.x = 0;
self.quadrantLabel.y = -TILE_SIZE / 4;
self.addChild(self.quadrantLabel);
// Load position from storage if available
if (storage.playerPosition) {
self.gridX = storage.playerPosition.x;
self.gridY = storage.playerPosition.y;
self.lastGridX = self.gridX;
self.lastGridY = self.gridY;
// Always force subTileX and subTileY to 0 (center)
self.subTileX = 0;
self.subTileY = 0;
} else {
// If not in storage, spawn at center of map
self.gridX = 15;
self.gridY = 15;
self.lastGridX = 15;
self.lastGridY = 15;
self.subTileX = 0;
self.subTileY = 0;
}
// Update position based on grid coordinates
self.updatePosition();
// Always visually center player on screen after spawn
self.x = self.gridX * TILE_SIZE + TILE_SIZE / 2;
self.y = self.gridY * TILE_SIZE + TILE_SIZE / 2;
return self;
};
// Move player to specific grid coordinates
self.moveToGrid = function (newGridX, newGridY) {
if (self.moving) return; // Don't interrupt current movement
// Store last position for comparison
self.lastGridX = self.gridX;
self.lastGridY = self.gridY;
// Prevent movement onto dark (black) blocks
if (typeof gameMap !== "undefined") {
var targetTile = gameMap.getTileAt(newGridX, newGridY);
if (targetTile && targetTile.biomeType === "dark") {
// Block movement, do not update grid position
return;
}
// Water entry animation: if moving into water, play animation
if (targetTile && targetTile.biomeType === "water") {
// Animate player "sinking" and "bobbing" in water
var originalY = self.y;
tween(self, {
y: originalY + 30
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
var bobCount = 0;
function bob() {
if (bobCount < 3) {
tween(self, {
y: originalY + 20
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
y: originalY + 30
}, {
duration: 120,
easing: tween.easeInOut,
onFinish: function onFinish() {
bobCount++;
bob();
}
});
}
});
} else {
// After bobbing, ensure player is at correct Y for water tile
var finalY = self.gridY * TILE_SIZE + TILE_SIZE / 2;
self.y = finalY;
}
}
bob();
}
});
}
}
// Update grid position (no sub-tile logic)
self.gridX = newGridX;
self.gridY = newGridY;
self.subTileX = 0;
self.subTileY = 0;
// Save position to storage
storage.playerPosition = {
x: self.gridX,
y: self.gridY,
subTileX: self.subTileX,
subTileY: self.subTileY
};
// Animate movement and update vision after movement finishes
self.moving = true;
// Move to the center of the tile
var offsetX = TILE_SIZE / 2;
var offsetY = TILE_SIZE / 2;
var targetMapX = self.gridX * TILE_SIZE + offsetX;
var targetMapY = self.gridY * TILE_SIZE + offsetY;
// Animate the map and player so the player appears to move smoothly and always stays centered on screen
var startPlayerX = self.x;
var startPlayerY = self.y;
var startMapX = typeof gameMap !== "undefined" ? gameMap.x : 0;
var startMapY = typeof gameMap !== "undefined" ? gameMap.y : 0;
var endPlayerX = 1024;
var endPlayerY = 1366;
var endMapX = 1024 - targetMapX;
var endMapY = 1366 - targetMapY;
var playerTweenDuration = 350;
var visionTweenDuration = 200;
// Animate vision update to be much faster than player movement
if (typeof gameMap !== "undefined" && typeof self.updateVision === "function") {
var visionObj = {
progress: 0
};
tween(visionObj, {
progress: 1
}, {
duration: visionTweenDuration,
easing: tween.easeOut,
onUpdate: function onUpdate() {
self.updateVision(gameMap);
}
});
}
// Animate both player and map for smooth movement
tween({
t: 0
}, {
t: 1
}, {
duration: playerTweenDuration,
easing: tween.easeOut,
onUpdate: function onUpdate(obj) {
// Interpolate player position (moves on map)
self.x = startPlayerX + (targetMapX - startPlayerX) * obj.t;
self.y = startPlayerY + (targetMapY - startPlayerY) * obj.t;
// Animate map so player is always centered
if (typeof gameMap !== "undefined") {
gameMap.x = startMapX + (endMapX - startMapX) * obj.t;
gameMap.y = startMapY + (endMapY - startMapY) * obj.t;
}
},
onFinish: function onFinish() {
self.moving = false;
// Snap to final positions
self.x = targetMapX;
// If current tile is water, keep the "bobbing" offset, else restore to normal
var currentTile = typeof gameMap !== "undefined" ? gameMap.getTileAt(self.gridX, self.gridY) : null;
if (currentTile && currentTile.biomeType === "water") {
self.y = targetMapY; // Appear lower in water
} else {
self.y = targetMapY - 30; // Move player image 30px above tile center for better centering
// If coming from water, stop any water bobbing animation and reset Y
if (typeof tween !== "undefined") {
tween.stop(self, {
y: true
});
}
}
if (typeof gameMap !== "undefined") {
gameMap.x = endMapX;
gameMap.y = endMapY;
}
// Only update vision after movement is complete for optimized vision
if (typeof gameMap !== "undefined") {
self.updateVision(gameMap);
}
}
});
// Always clear quadrant label since player is always at center
if (self.quadrantLabel) self.quadrantLabel.setText('');
// Call onQuadrantChange with 'CENTER' label for compatibility
if (typeof self.onQuadrantChange === "function") {
self.onQuadrantChange(self.gridX, self.gridY, self.subTileX, self.subTileY, 'CENTER');
}
};
// Update position based on current grid coordinates (no animation)
self.updatePosition = function () {
// Position player at the center of the tile (no sub-tile logic)
var offsetX = TILE_SIZE / 2;
var offsetY = TILE_SIZE / 2;
self.x = self.gridX * TILE_SIZE + offsetX;
// If current tile is water, show player lower (no -30 offset)
var currentTile = typeof gameMap !== "undefined" ? gameMap.getTileAt(self.gridX, self.gridY) : null;
if (currentTile && currentTile.biomeType === "water") {
self.y = self.gridY * TILE_SIZE + offsetY;
} else {
self.y = self.gridY * TILE_SIZE + offsetY - 30; // Move player image 30px above tile center for better centering
}
// Hide player sprite if on dark (black) block
if (typeof gameMap !== "undefined") {
var tile = gameMap.getTileAt(self.gridX, self.gridY);
if (tile && tile.biomeType === "dark") {
self.visible = false;
} else {
self.visible = true;
// Mark this tile as discovered if not already
if (tile && !tile.explored) {
tile.explore();
}
}
} else {
self.visible = true;
}
// No longer force sub-tile to 0 (center) when updating position
// Only update vision if not moving, to optimize vision updates
if (!self.moving && typeof gameMap !== "undefined" && typeof self.updateVision === "function") {
self.updateVision(gameMap);
}
// Always clear quadrant label since player is always at center
if (self.quadrantLabel) self.quadrantLabel.setText('');
// Call onQuadrantChange with 'CENTER' label for compatibility
if (typeof self.onQuadrantChange === "function") {
self.onQuadrantChange(self.gridX, self.gridY, self.subTileX, self.subTileY, 'CENTER');
}
// Ensure player is always above the tile and centered after update
if (typeof game !== "undefined" && self.parent !== game) {
game.addChild(self);
}
self.scale.set(gameMap.scale.x, gameMap.scale.y);
self.visible = true;
// Always visually center player on screen after updatePosition
// Now: player moves on map, so set to map position
self.x = self.gridX * TILE_SIZE + TILE_SIZE / 2;
self.y = self.gridY * TILE_SIZE + TILE_SIZE / 2;
// Always bring player to top of display list
if (typeof game !== "undefined" && typeof game.children !== "undefined") {
if (game.children.indexOf(self) !== -1) {
game.removeChild(self);
game.addChild(self);
}
}
};
// Update vision: show/hide tiles based on a circular vision area, and save seen tiles as memory
self.updateVision = function (gameMap) {
// Vision area: always center player in the middle of the vision area for optimized movement
var visionRadius = 2; // Reduced from 3 to 2 for even smaller vision
var centerX = self.gridX;
var centerY = self.gridY;
var minX = Math.max(0, Math.floor(centerX - visionRadius));
var maxX = Math.min(MAP_WIDTH - 1, Math.ceil(centerX + visionRadius));
var minY = Math.max(0, Math.floor(centerY - visionRadius));
var maxY = Math.min(MAP_HEIGHT - 1, Math.ceil(centerY + visionRadius));
for (var y = 0; y < MAP_HEIGHT; y++) {
for (var x = 0; x < MAP_WIDTH; x++) {
var tile = gameMap.getTileAt(x, y);
if (tile) {
// Calculate distance from player - properly centered now
var dx = x - centerX;
var dy = y - centerY;
// Only show tiles within a circular vision area (Euclidean distance <= visionRadius + 0.2 for smoothness)
if (Math.sqrt(dx * dx + dy * dy) <= visionRadius + 0.2) {
tile.visible = true;
// Mark as explored and save to memory
if (!tile.explored) {
tile.explored = true;
var exploredKey = x + "," + y;
if (!storage.exploredTiles) storage.exploredTiles = {};
storage.exploredTiles[exploredKey] = true;
}
// No fog overlay to hide (fog removed)
// --- Show both spawned and despawned creatures in vision ---
// In vision area: show both spawned and despawned creatures
// Remove all faded indicators first to avoid duplicates
for (var c = tile.children.length - 1; c >= 0; c--) {
if (tile.children[c].assetId === 'encounterIndicator' && tile.children[c].alpha === 0.25) {
tile.removeChild(tile.children[c]);
}
}
// If a creature is currently spawned, mark that this tile has ever had a creature
if (tile.hasCreature) {
tile._everHadCreature = true;
}
// Show indicator for spawned creature
if (tile.hasCreature) {
// Remove encounterIndicator if present, since we will show the creature image instead
for (var c = tile.children.length - 1; c >= 0; c--) {
if (tile.children[c].assetId === 'encounterIndicator' && tile.children[c].alpha === 0.7) {
tile.removeChild(tile.children[c]);
}
}
// --- Show creature visually on top of tile ---
if (!tile._creatureVisual) {
// Find the type of the creature for this tile from mapCreatures/mapCreatureTiles
var creatureType = null;
if (typeof mapCreatures !== "undefined" && typeof mapCreatureTiles !== "undefined") {
for (var mci = 0; mci < mapCreatureTiles.length; mci++) {
if (mapCreatureTiles[mci] === tile) {
creatureType = mapCreatures[mci];
break;
}
}
}
// If not found, fallback to biomeType
if (!creatureType) {
// Use a default type for biome
if (typeof Creature !== "undefined") {
creatureType = new Creature().getRandomType(tile.biomeType);
} else {
creatureType = "normal";
}
}
// If biome is urban, use urbanCreature
if (tile.biomeType === "urban") {
creatureType = "urban";
}
// Show the creature visually using the correct image for the type
var creatureVisual = LK.getAsset(creatureType + "Creature", {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
creatureVisual.scale.set(1.2, 1.2);
creatureVisual.alpha = 1.0;
tile.addChild(creatureVisual);
tile._creatureVisual = creatureVisual;
}
// Always bring creature visual to top
if (tile._creatureVisual && tile.children.indexOf(tile._creatureVisual) !== -1) {
tile.removeChild(tile._creatureVisual);
tile.addChild(tile._creatureVisual);
}
}
// Remove creature visual if not hasCreature
if (!tile.hasCreature && tile._creatureVisual) {
tile.removeChild(tile._creatureVisual);
tile._creatureVisual = null;
}
// Show faded indicator for despawned creature (if ever had creature, but not currently spawned)
if (tile._everHadCreature && !tile.hasCreature) {
var alreadyFaded = false;
for (var c = 0; c < tile.children.length; c++) {
if (tile.children[c].assetId === 'encounterIndicator' && tile.children[c].alpha === 0.25) {
alreadyFaded = true;
break;
}
}
if (!alreadyFaded) {
var fadedIndicator = tile.attachAsset('encounterIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
fadedIndicator.alpha = 0.25;
}
// Remove creature visual if not hasCreature
if (tile._creatureVisual) {
tile.removeChild(tile._creatureVisual);
tile._creatureVisual = null;
}
}
// --- End show both spawned and despawned creatures in vision ---
} else {
// Out of vision: hide all encounter indicators (spawned and despawned) in foggy/unvision area
for (var c = tile.children.length - 1; c >= 0; c--) {
if (tile.children[c].assetId === 'encounterIndicator') {
tile.removeChild(tile.children[c]);
}
}
// Hide all creatures in foggy area: do not show any indicators or creatures
tile.visible = true;
// No fog overlay to show (fog removed)
}
}
}
// Called whenever the player moves to a new quadrant of a tile
self.onQuadrantChange = function (gridX, gridY, subTileX, subTileY, label) {
// Example: you can add your own logic here to check which quadrant the player is in
// For example, trigger something if in DOWN LEFT
// gridX, gridY: tile coordinates
// subTileX, subTileY: 0 or 1 (0=left/up, 1=right/down)
// label: string label of the quadrant
};
// Example: log which quadrant the player is in
// console.log("Player is at tile (" + gridX + "," + gridY + ") in quadrant: " + label);
// You can add more logic here, e.g.:
// if (subTileX === 0 && subTileY === 1) { /* DOWN LEFT */ }
// if (subTileX === 1 && subTileY === 0) { /* UP RIGHT */ }
// etc.
}
;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Unique image for urban biome
// Sounds
// Encounter indicator
// UI elements
// Creatures by elemental type
// Map tiles
// Player character
// Storage for saving game progress
// Tween for animations
// Game dimensions and settings
// JSON helper functions for parsing and stringifying
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);
}
function parseJSON(str) {
var result = {};
if (typeof str === 'string') {
try {
var pairs = str.slice(1, -1).split(',');
for (var i = 0; i < pairs.length; i++) {
var pair = pairs[i].trim().split(':');
var key = pair[0].replace(/["']/g, '').trim();
var value = pair[1].trim();
if (value.startsWith('[') && value.endsWith(']')) {
result[key] = parseJSONArray(value);
} else if (value.startsWith('{') && value.endsWith('}')) {
result[key] = parseJSON(value);
} else if (value === 'true') {
result[key] = true;
} else if (value === 'false') {
result[key] = false;
} else if (!isNaN(value)) {
result[key] = Number(value);
} else {
result[key] = value.replace(/["']/g, '');
}
}
} catch (e) {
console.log('Error parsing JSON: ', e);
}
}
return result;
}
function parseJSONArray(str) {
var result = [];
if (typeof str === 'string') {
try {
var items = str.slice(1, -1).split(',');
for (var i = 0; i < items.length; i++) {
var item = items[i].trim();
if (item.startsWith('[') && item.endsWith(']')) {
result.push(parseJSONArray(item));
} else if (item.startsWith('{') && item.endsWith('}')) {
result.push(parseJSON(item));
} else if (item === 'true') {
result.push(true);
} else if (item === 'false') {
result.push(false);
} else if (!isNaN(item)) {
result.push(Number(item));
} else {
result.push(item.replace(/["']/g, ''));
}
}
} catch (e) {
console.log('Error parsing JSON array: ', e);
}
}
return result;
}
function stringifyJSON(obj) {
if (Array.isArray(obj)) {
var items = [];
for (var i = 0; i < obj.length; i++) {
items.push(stringifyJSON(obj[i]));
}
return '[' + items.join(',') + ']';
} else if (_typeof(obj) === 'object' && obj !== null) {
var pairs = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
pairs.push('"' + key + '":' + stringifyJSON(obj[key]));
}
}
return '{' + pairs.join(',') + '}';
} else if (typeof obj === 'string') {
return '"' + obj.replace(/"/g, '\\"') + '"';
} else {
return String(obj);
}
}
var TILE_SIZE = 400; // Double the default tile size
var MAP_WIDTH = 80; // Make the map much larger
var MAP_HEIGHT = 80; // Make the map much larger
// Vision radius is now set in Player.updateVision for optimized vision area
// Initialize game objects
var gameMap = game.addChild(new GameMap().initMap());
gameMap.scale.set(1.5, 1.5); // Zoom in the map for a closer look
// Double the size of the tile at (0,0)
if (gameMap.tiles && gameMap.tiles[0] && gameMap.tiles[0][0]) {
gameMap.tiles[0][0] = "grass";
var tileObj = gameMap.getTileAt(0, 0);
if (tileObj) {
tileObj.biomeType = "grass";
tileObj.tileSize = TILE_SIZE * 2;
// Optionally update the tile graphics if needed
}
}
// Update all MapTile objects to use new TILE_SIZE, except (0,0) which is double
for (var y = 0; y < MAP_HEIGHT; y++) {
for (var x = 0; x < MAP_WIDTH; x++) {
var t = gameMap.getTileAt(x, y);
if (t) {
if (!(x === 0 && y === 0)) {
t.tileSize = TILE_SIZE;
}
}
}
}
// Spawn player at a non-water, non-dark tile near 15,15 coordinates, centered in tile
var playerStartX = 15;
var playerStartY = 15;
// Find a valid starting tile (not water, not dark)
function findValidPlayerStart(gameMap, startX, startY) {
var maxRadius = 10;
for (var r = 0; r <= maxRadius; r++) {
for (var dy = -r; dy <= r; dy++) {
for (var dx = -r; dx <= r; dx++) {
var x = startX + dx;
var y = startY + dy;
if (x >= 0 && x < MAP_WIDTH && y >= 0 && y < MAP_HEIGHT) {
var biome = gameMap.tiles && gameMap.tiles[y] && gameMap.tiles[y][x];
// Only allow spawn if not water and not dark
if (biome && biome !== "water" && biome !== "dark") {
return {
x: x,
y: y
};
}
}
}
}
}
// fallback to 0,0 if nothing found
return {
x: 0,
y: 0
};
}
var validStart = findValidPlayerStart(gameMap, playerStartX, playerStartY);
playerStartX = validStart.x;
playerStartY = validStart.y;
// Ensure start tile is not water for consistency
if (gameMap.tiles && gameMap.tiles[playerStartY] && gameMap.tiles[playerStartY][playerStartX]) {
// If the found tile is water or dark, forcibly set to grass
if (gameMap.tiles[playerStartY][playerStartX] === "water" || gameMap.tiles[playerStartY][playerStartX] === "dark") {
gameMap.tiles[playerStartY][playerStartX] = "grass";
var tileObj = gameMap.getTileAt(playerStartX, playerStartY);
if (tileObj) {
tileObj.biomeType = "grass";
tileObj.tileSize = TILE_SIZE;
}
} else {
// Otherwise, just update the tile object to match the biome
var tileObj = gameMap.getTileAt(playerStartX, playerStartY);
if (tileObj) {
tileObj.biomeType = gameMap.tiles[playerStartY][playerStartX];
tileObj.tileSize = TILE_SIZE;
}
}
}
storage.playerPosition = {
x: playerStartX,
y: playerStartY,
subTileX: 0,
subTileY: 0
};
var player = new Player().init();
player.gridX = playerStartX;
player.gridY = playerStartY;
player.lastGridX = playerStartX;
player.lastGridY = playerStartY;
player.subTileX = 0;
player.subTileY = 0;
player.updatePosition();
// Always visually center player in the tile after spawn
centerViewOnPlayer();
centerViewOnPlayer();
// player.x and player.y are set by updatePosition and movement now
// Ensure player is always above all map tiles and scaled to match map
player.scale.set(gameMap.scale.x, gameMap.scale.y);
// Always add player after gameMap so it is above all tiles
if (player.parent !== game) {
game.addChild(player);
}
player.visible = true; // Explicitly set player visible in case it was hidden
// Mark starting tile as explored and discovered
var startTile2 = gameMap.getTileAt(playerStartX, playerStartY);
if (startTile2 && !startTile2.explored) {
startTile2.explore();
}
var dpad = game.addChild(new DPad().init());
var minimap = game.addChild(new MiniMap().init());
var encounter = game.addChild(new Encounter().init());
var collection = game.addChild(new Collection().init());
// Collection button (top right corner)
var collectionButton = game.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1900,
y: 150
});
collectionButton.interactive = true;
// Collection button icon
var collectionText = new Text2('COL', {
size: 30,
fill: 0xFFFFFF
});
collectionText.anchor.set(0.5, 0.5);
collectionText.x = 1900;
collectionText.y = 150;
game.addChild(collectionText);
// Game variables
var encounterActive = false;
var exploredCount = 0;
var totalTiles = MAP_WIDTH * MAP_HEIGHT;
// Pokeball inventory UI
var pokeballInventoryText = new Text2('', {
size: 40,
fill: 0xffffff
});
pokeballInventoryText.anchor.set(0.5, 0);
pokeballInventoryText.x = 1024;
pokeballInventoryText.y = 80;
game.addChild(pokeballInventoryText);
function updatePokeballInventoryText() {
pokeballInventoryText.setText('Pokeballs: ' + 'Basic(' + (playerPokeballs.basic || 0) + ') ' + 'Uncommon(' + (playerPokeballs.uncommon || 0) + ') ' + 'Rare(' + (playerPokeballs.rare || 0) + ') ' + 'Legendary(' + (playerPokeballs.legendary || 0) + ')');
}
updatePokeballInventoryText();
// Update stats display
// (removed: no stats text shown)
// Center the game view on player
function centerViewOnPlayer() {
// Always center the map so the player is visually centered on screen
if (typeof gameMap !== "undefined" && typeof player !== "undefined") {
// Calculate the target position for the map so player is at (1024, 1366)
var offsetX = TILE_SIZE / 2;
var offsetY = TILE_SIZE / 2;
var targetMapX = player.gridX * TILE_SIZE + offsetX;
var targetMapY = player.gridY * TILE_SIZE + offsetY;
// Adjust for zoom (scale)
var scaleX = gameMap.scale.x;
var scaleY = gameMap.scale.y;
gameMap.x = 1024 - targetMapX * scaleX;
gameMap.y = 1366 - targetMapY * scaleY;
// Place player at the center of the screen
player.x = 1024;
player.y = 1336; // Move player image 30px above tile center for better centering
// Ensure player is always visually centered in the tile
// (player.x, player.y are always the center of the tile in world coordinates)
}
// Ensure player is above tiles
if (player.parent !== game) {
game.addChild(player);
}
player.scale.set(gameMap.scale.x, gameMap.scale.y);
player.visible = true;
}
// Handle player movement from DPad (move exactly 1 block per input)
dpad.upButton.down = function () {
if (!player.moving && !encounterActive && !collection.visible) {
// Move exactly 0.5 tile up per press
var newGridY = player.gridY - 0.5;
if (newGridY >= 0) {
player.moveToGrid(player.gridX, newGridY);
}
}
};
dpad.downButton.down = function () {
if (!player.moving && !encounterActive && !collection.visible) {
// Move exactly 0.5 tile down per press
var newGridY = player.gridY + 0.5;
if (newGridY < MAP_HEIGHT) {
player.moveToGrid(player.gridX, newGridY);
}
}
};
dpad.leftButton.down = function () {
if (!player.moving && !encounterActive && !collection.visible) {
// Move exactly 0.5 tile left per press
var newGridX = player.gridX - 0.5;
if (newGridX >= 0) {
player.moveToGrid(newGridX, player.gridY);
}
}
};
dpad.rightButton.down = function () {
if (!player.moving && !encounterActive && !collection.visible) {
// Move exactly 0.5 tile right per press
var newGridX = player.gridX + 0.5;
if (newGridX < MAP_WIDTH) {
player.moveToGrid(newGridX, player.gridY);
}
}
};
// Always start the player centered in the vision area
player.gridX = playerStartX;
player.gridY = playerStartY;
player.lastGridX = playerStartX;
player.lastGridY = playerStartY;
player.updatePosition();
centerViewOnPlayer();
// Handle collection button
collectionButton.down = function () {
if (!encounterActive) {
// Always reload the bag menu for all creatures
if (typeof collection.show === "function") {
collection.show();
}
}
};
// Handle collection close button
collection.closeButton.down = function () {
collection.hide();
// Show player again when closing collection
player.visible = true;
};
// Track last player position to detect changes
var lastPlayerGridX = player.gridX;
var lastPlayerGridY = player.gridY;
// Main game update loop
// Throttle update to 30FPS (every ~33ms) for better performance
var lastUpdateTime = 0;
game.update = function () {
var now = Date.now();
if (lastUpdateTime && now - lastUpdateTime < 33) {
return;
}
lastUpdateTime = now;
// --- Pokeball system ---
// Pokeball rarities and their catch chances
var pokeballTypes = [{
id: 'pokeball_basic',
name: 'Basic Ball',
color: 0xffffff,
rarity: 'basic',
catchBonus: 0,
chance: 0.5
}, {
id: 'pokeball_uncommon',
name: 'Uncommon Ball',
color: 0x00ff00,
rarity: 'uncommon',
catchBonus: 0.15,
chance: 0.65
}, {
id: 'pokeball_rare',
name: 'Rare Ball',
color: 0x0000ff,
rarity: 'rare',
catchBonus: 0.3,
chance: 0.8
}, {
id: 'pokeball_legendary',
name: 'Legendary Ball',
color: 0xffd700,
rarity: 'legendary',
catchBonus: 0.5,
chance: 0.95
}];
// Player's pokeball inventory
if (typeof playerPokeballs === "undefined") {
playerPokeballs = {
basic: 3,
uncommon: 1,
rare: 0,
legendary: 0
};
}
// Pokeball map objects
if (typeof mapPokeballs === "undefined") {
mapPokeballs = [];
mapPokeballTiles = [];
mapPokeballMax = 6; // Max pokeballs on map at once
mapPokeballSpawnCooldown = 0;
}
// --- Creature spawn/despawn logic ---
// We'll use a global array to track visible creatures on the map
if (typeof mapCreatures === "undefined") {
mapCreatures = [];
mapCreatureTiles = [];
mapCreatureLabels = [];
mapCreatureMax = 4; // Max creatures on map at once (fewer for optimization)
mapCreatureSpawnCooldown = 0;
}
// Despawn creatures sometimes (randomly, or if their tile is explored)
for (var i = mapCreatures.length - 1; i >= 0; i--) {
var tile = mapCreatureTiles[i];
// Despawn if tile is explored or random chance
if (!tile || tile.explored || Math.random() < 0.01) {
// Remove label if present
if (mapCreatureLabels[i]) {
tile && tile.removeChild(mapCreatureLabels[i]);
mapCreatureLabels.splice(i, 1);
}
// Remove indicator if present
if (tile && tile.hasCreature) {
tile.hasCreature = false;
// Remove encounterIndicator
for (var j = 0; j < tile.children.length; j++) {
if (tile.children[j].assetId === 'encounterIndicator') {
tile.removeChild(tile.children[j]);
break;
}
}
}
mapCreatures.splice(i, 1);
mapCreatureTiles.splice(i, 1);
continue;
}
}
// Spawn new creatures sometimes, up to max
if (mapCreatures.length < mapCreatureMax && mapCreatureSpawnCooldown <= 0) {
// Try to spawn a new creature
var tries = 0;
while (tries < 10 && mapCreatures.length < mapCreatureMax) {
var gx = Math.floor(Math.random() * MAP_WIDTH);
var gy = Math.floor(Math.random() * MAP_HEIGHT);
var tile = gameMap.getTileAt(gx, gy);
// Only spawn on tiles that are not explored, not dark, not already with a creature, and are crossable by the player
// Crossable: not water, not dark, not mountain (if you want to restrict more, add more biomeType checks)
var crossable = tile && tile.biomeType !== "dark";
// Optionally, restrict further: e.g. not water, not mountain
// crossable = crossable && tile.biomeType !== "water" && tile.biomeType !== "mountain";
if (tile && !tile.explored && !tile.hasCreature && crossable) {
// Mark tile as having a creature
tile.hasCreature = true;
// Add indicator
var indicator = tile.attachAsset('encounterIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
indicator.alpha = 0.7;
// Pick a random type for this biome
var cType = new Creature().getRandomType(tile.biomeType);
mapCreatures.push(cType);
mapCreatureTiles.push(tile);
// No type label writing for bug ground type
mapCreatureLabels.push(null);
}
tries++;
}
// Add a much longer cooldown so we don't spawn every frame (increased for much less frequent spawning)
mapCreatureSpawnCooldown = 180 + Math.floor(Math.random() * 120); // 3-5s
} else {
mapCreatureSpawnCooldown--;
}
// --- Pokeball spawn/despawn logic ---
// Despawn pokeballs sometimes (randomly, or if their tile is explored)
for (var i = mapPokeballs.length - 1; i >= 0; i--) {
var tile = mapPokeballTiles[i];
if (!tile || tile.explored || Math.random() < 0.01) {
// Remove pokeball visual if present
if (tile && tile._pokeballVisual) {
tile.removeChild(tile._pokeballVisual);
tile._pokeballVisual = null;
}
mapPokeballs.splice(i, 1);
mapPokeballTiles.splice(i, 1);
continue;
}
}
// Spawn new pokeballs sometimes, up to max
if (mapPokeballs.length < mapPokeballMax && mapPokeballSpawnCooldown <= 0) {
var tries = 0;
while (tries < 10 && mapPokeballs.length < mapPokeballMax) {
var gx = Math.floor(Math.random() * MAP_WIDTH);
var gy = Math.floor(Math.random() * MAP_HEIGHT);
var tile = gameMap.getTileAt(gx, gy);
// Only spawn on tiles that are not explored, not dark, not already with a pokeball or creature
var crossable = tile && tile.biomeType !== "dark" && !tile.hasCreature && !tile._pokeballVisual;
if (tile && !tile.explored && crossable) {
// Pick a random pokeball type (weighted: more basics, less legendary)
var rand = Math.random();
var pType = pokeballTypes[0];
if (rand > 0.85) pType = pokeballTypes[3];else if (rand > 0.7) pType = pokeballTypes[2];else if (rand > 0.5) pType = pokeballTypes[1];
// Show pokeball on tile
var pokeballVisual = LK.getAsset('captureRing', {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
pokeballVisual.tint = pType.color;
pokeballVisual.scale.set(0.5, 0.5);
tile.addChild(pokeballVisual);
tile._pokeballVisual = pokeballVisual;
pokeballVisual.assetId = 'pokeball_' + pType.rarity;
mapPokeballs.push(pType.rarity);
mapPokeballTiles.push(tile);
}
tries++;
}
mapPokeballSpawnCooldown = 120 + Math.floor(Math.random() * 120); // 2-4s
} else {
mapPokeballSpawnCooldown--;
}
// --- End creature spawn/despawn logic ---
// --- Pokeball collection logic ---
var playerTile = gameMap.getTileAt(player.gridX, player.gridY);
if (playerTile && playerTile._pokeballVisual) {
// Find which pokeball type is here
var foundType = null;
for (var i = 0; i < pokeballTypes.length; i++) {
if (playerTile._pokeballVisual.assetId === 'pokeball_' + pokeballTypes[i].rarity) {
foundType = pokeballTypes[i];
break;
}
}
if (foundType) {
// Add to inventory
if (!playerPokeballs[foundType.rarity]) playerPokeballs[foundType.rarity] = 0;
playerPokeballs[foundType.rarity]++;
// Remove pokeball from map
playerTile.removeChild(playerTile._pokeballVisual);
playerTile._pokeballVisual = null;
// Remove from arrays
for (var i = mapPokeballTiles.length - 1; i >= 0; i--) {
if (mapPokeballTiles[i] === playerTile) {
mapPokeballs.splice(i, 1);
mapPokeballTiles.splice(i, 1);
break;
}
}
// Show a message
var msg = new Text2('Collected ' + foundType.name + '!', {
size: 60,
fill: foundType.color
});
msg.anchor.set(0.5, 0.5);
msg.x = 1024;
msg.y = 400;
game.addChild(msg);
LK.setTimeout(function () {
game.removeChild(msg);
}, 1000);
// Update inventory UI
updatePokeballInventoryText();
}
}
// Skip updates if in an active encounter
if (encounterActive) {
encounter.update();
return;
}
// Check if collection is visible
if (collection.visible) {
// Hide player and move below collection
player.visible = false;
if (player.parent !== game) {
game.addChild(player);
}
// Always reload the bag menu for all creatures
if (typeof collection.show === "function") {
collection.show();
}
return;
}
// If collection is not visible, ensure player is visible
player.visible = true;
// Center view on player
centerViewOnPlayer();
// Update minimap only every 3rd update for optimization
if (typeof minimapUpdateCounter === "undefined") minimapUpdateCounter = 0;
minimapUpdateCounter++;
if (minimapUpdateCounter % 3 === 0) {
minimap.update(player, gameMap);
}
// Vision update is now handled after player movement for optimization
// Check if player has moved to a new tile
if (player.gridX !== lastPlayerGridX || player.gridY !== lastPlayerGridY) {
// Get the current tile
var currentTile = gameMap.getTileAt(player.gridX, player.gridY);
if (currentTile) {
// Mark tile as explored if it's new
if (!currentTile.explored) {
currentTile.explore();
exploredCount++;
}
// Prevent encounters on dark (black) blocks
if (currentTile.biomeType !== "dark" && currentTile.hasCreature && !currentTile._creatureCaught) {
// Always trigger encounter if player and creature cross
encounterActive = true;
// Create creature based on biome
var creatureType = new Creature().getRandomType(currentTile.biomeType);
var creature = new Creature().init(creatureType, null, player.gridX, player.gridY);
// Start encounter with this creature
encounter.startEncounter(creature);
// Remove creature from tile and mark as caught if caught, otherwise allow re-encounter
// (actual caught logic handled in encounter/capture, so only mark as caught if caught)
currentTile.hasCreature = false;
currentTile._creatureCaught = false; // Not caught yet, only set to true if caught
currentTile._creatureCaught = true; // Mark as caught so it doesn't respawn immediately
// Remove indicator from tile
for (var i = 0; i < currentTile.children.length; i++) {
var child = currentTile.children[i];
if (child.assetId === 'encounterIndicator') {
currentTile.removeChild(child);
break;
}
}
// Remove label if present (from mapCreatureLabels)
if (typeof mapCreatureTiles !== "undefined") {
for (var k = mapCreatureTiles.length - 1; k >= 0; k--) {
if (mapCreatureTiles[k] === currentTile) {
if (mapCreatureLabels[k]) {
currentTile.removeChild(mapCreatureLabels[k]);
mapCreatureLabels.splice(k, 1);
}
mapCreatures.splice(k, 1);
mapCreatureTiles.splice(k, 1);
break;
}
}
}
}
}
// Update last position
lastPlayerGridX = player.gridX;
lastPlayerGridY = player.gridY;
}
}; // End throttled update
// Listen for encounter end
var checkEncounterInterval = LK.setInterval(function () {
if (encounterActive && !encounter.active) {
encounterActive = false;
}
}, 100);
// Initialize stats
// Initial centering
centerViewOnPlayer();
// Fix: Only reveal vision area at game start, not the whole map
if (typeof player !== "undefined" && typeof gameMap !== "undefined" && typeof player.updateVision === "function") {
player.updateVision(gameMap);
}
// Vision mask overlay removed to fix black screen issue
water tile just water and top viewing. In-Game asset. 2d
dpad. In-Game asset. 2d. High contrast. No shadows
red button square. In-Game asset. 2d. High contrast. No shadows
grass tile on top viewing. In-Game asset. 2d. High contrast. No shadows
Mountain tile on top viewing. In-Game asset. 2d
sand tile on top viewing. In-Game asset. 2d. High contrast. No shadows
forest tile on top viewing. In-Game asset. 2d. No shadows
square grass tile on top viewing. In-Game asset. 2d. High contrast. No shadows
urban tile on viewing. In-Game asset. 2d
street tile on top viewing. In-Game asset. 2d. High contrast
street tile on top viewing with street way. In-Game asset. 2d
street tile on top viewing with street way horizontal. In-Game asset. 2d. High contrast. No shadows
make creature image for grass elemental name is sproutle. In-Game asset. 2d. High contrast. No shadows
make creature image for rock elemental name is boulder but dont write name on image. In-Game asset. 2d. High contrast. No shadows
make creature image for electrical elemental name is voltix but dont write name on image. In-Game asset. 2d. High contrast. No shadows
player human viewing on behind of him with all of body. In-Game asset. 2d
make a cube but like a pokeball and change the colour of original and make it the basic one make with purple and yellow but do diffrently. In-Game asset. 2d. High contrast. No shadows
make a cube but like a pokeball and make it the legendary one make with red and golden and blue but do diffrently. In-Game asset. 2d. High contrast. No shadows
make a cube but like a pokeball and make it for rare one make with grey and silver and white but do diffrently. In-Game asset. 2d. High contrast. No shadows
make a cube but like a pokeball and make it for uncommon one make with green and white and bronze but do diffrently. In-Game asset. 2d. High contrast. No shadows