/**** * 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 and bag self.scrollContainer = new Container(); self.addChild(self.scrollContainer); self.scrollContainer.x = 50; self.scrollContainer.y = 320; // Move down to make space for bag section // Create scrollable Bag section container with mask self.bagScrollContainer = new Container(); self.bagMaskContainer = new Container(); self.addChild(self.bagMaskContainer); self.bagMaskContainer.x = 50; self.bagMaskContainer.y = 200; self.bagMaskContainer.addChild(self.bagScrollContainer); // Create a mask for the scrollable area var bagMask = LK.getAsset('grassTile', { anchorX: 0, anchorY: 0, width: 2048 - 100, height: 1000 }); bagMask.alpha = 0.0; // Invisible mask self.bagMaskContainer.addChild(bagMask); self.bagScrollContainer.mask = bagMask; // Create a background for the bag area var bagBackground = LK.getAsset('grassTile', { anchorX: 0, anchorY: 0, width: 2048 - 100, height: 1000 }); bagBackground.alpha = 0.3; bagBackground.tint = 0x000088; self.bagMaskContainer.addChild(bagBackground); self.bagMaskContainer.setChildIndex(bagBackground, 0); // Add Bag button to open Bag section self.bagButton = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, // Move to right bottom x: 2048 - 200, y: 2732 - 200, scaleX: 1.5, scaleY: 1.5 }); self.bagButton.interactive = true; self.bagBtnText = new Text2('BAG', { size: 60, fill: 0xFFD700 }); self.bagBtnText.anchor.set(0.5, 0.5); // Move to right bottom self.bagBtnText.x = 2048 - 200; self.bagBtnText.y = 2732 - 200; self.addChild(self.bagButton); self.addChild(self.bagBtnText); // Add bag section title self.bagTitleText = new Text2('MY BAG', { size: 70, fill: 0xFFD700 }); self.bagTitleText.anchor.set(0.5, 0); self.bagTitleText.x = 1024; self.bagTitleText.y = 220; self.addChild(self.bagTitleText); self.bagTitleText.visible = true; // Scrolling variables self.bagScrollingActive = false; self.bagScrollStartY = 0; self.bagScrollStartPos = 0; self.bagMaxScroll = 0; // Add scroll indicators self.scrollUpIndicator = new Text2('▲', { size: 60, fill: 0xFFFFFF }); self.scrollUpIndicator.anchor.set(0.5, 0); self.scrollUpIndicator.x = 1024; self.scrollUpIndicator.y = 260; self.scrollUpIndicator.alpha = 0.7; self.addChild(self.scrollUpIndicator); self.scrollDownIndicator = new Text2('▼', { size: 60, fill: 0xFFFFFF }); self.scrollDownIndicator.anchor.set(0.5, 1); self.scrollDownIndicator.x = 1024; self.scrollDownIndicator.y = 1180; self.scrollDownIndicator.alpha = 0.7; self.addChild(self.scrollDownIndicator); // Bag section is visible by default, but you can toggle it with the button if desired self.bagButton.down = function () { // Toggle bagMaskContainer visibility self.bagMaskContainer.visible = !self.bagMaskContainer.visible; self.bagTitleText.visible = self.bagMaskContainer.visible; self.scrollUpIndicator.visible = self.bagMaskContainer.visible; self.scrollDownIndicator.visible = self.bagMaskContainer.visible; // Change title depending on bagContainer visibility if (self.bagMaskContainer.visible && self.titleText) { self.titleText.setText('THE BAG'); // Hide bag button when bag is visible self.bagButton.visible = false; if (self.bagBtnText) { self.bagBtnText.visible = false; } // Create/show Cubixion menu button when in bag view if (!self.cubixionMenuButton) { self.cubixionMenuButton = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 200, y: 2732 - 200, scaleX: 1.5, scaleY: 1.5 }); self.cubixionMenuButton.interactive = true; self.cubixionMenuBtnText = new Text2('CUBIXION MENU', { size: 60, fill: 0xFFD700 }); self.cubixionMenuBtnText.anchor.set(0.5, 0.5); self.cubixionMenuBtnText.x = 2048 - 200; self.cubixionMenuBtnText.y = 2732 - 200; self.addChild(self.cubixionMenuButton); self.addChild(self.cubixionMenuBtnText); self.cubixionMenuButton.down = function () { // Hide bag view self.bagMaskContainer.visible = false; self.bagTitleText.visible = false; self.scrollUpIndicator.visible = false; self.scrollDownIndicator.visible = false; // Hide Cubixion menu button self.cubixionMenuButton.visible = false; self.cubixionMenuBtnText.visible = false; // Show bag button again self.bagButton.visible = true; if (self.bagBtnText) { self.bagBtnText.visible = true; } // Change title back to collection menu if (self.titleText) { self.titleText.setText('CUBIXION MENU'); } // Hide back button and collection button if they exist if (self.backToCollectionButton) { self.backToCollectionButton.visible = false; if (self.backToCollectionButton.backBtnText) { self.backToCollectionButton.backBtnText.visible = false; } } if (self.collectionButton) { self.collectionButton.visible = false; if (self.collectionButton.colBtnText) { self.collectionButton.colBtnText.visible = false; } } }; } self.cubixionMenuButton.visible = true; self.cubixionMenuBtnText.visible = true; } else if (self.titleText) { self.titleText.setText('CUBIXION MENU'); // Show bag button when bag is not visible self.bagButton.visible = true; if (self.bagBtnText) { self.bagBtnText.visible = true; } // Hide Cubixion menu button if it exists if (self.cubixionMenuButton) { self.cubixionMenuButton.visible = false; self.cubixionMenuBtnText.visible = false; } } // If showing bag, populate it with items if (self.bagMaskContainer.visible) { self.populateBagItems(); // Make sure back button is visible when bag is visible if (self.backToCollectionButton) { self.backToCollectionButton.visible = true; if (self.backToCollectionButton.backBtnText) { self.backToCollectionButton.backBtnText.visible = true; } } } }; // Function to populate bag with items self.populateBagItems = function () { // Remove previous cubixion images from bagScrollContainer for (var i = self.bagScrollContainer.children.length - 1; i >= 0; i--) { var child = self.bagScrollContainer.children[i]; if (child && child.isCubixionBagImage) { self.bagScrollContainer.removeChild(child); } } // Reset scroll position self.bagScrollContainer.y = 0; // Show a summary of how many cubix of each rarity the player has in the bag section, with their image on the left var cubixRarities = ['basic', 'uncommon', 'rare', 'legendary']; var cubixNames = { basic: 'Basic Cubix', uncommon: 'Uncommon Cubix', rare: 'Rare Cubix', legendary: 'Legendary Cubix' }; var cubixColors = { basic: 0xffffff, uncommon: 0x00ff00, rare: 0x0000ff, legendary: 0xffd700 }; var itemHeight = 200; // Increased height for each item var padding = 40; // Padding between items var totalHeight = 0; var containerWidth = 1900; // Width of the container // Add title for each section var headerText = new Text2('CUBIX BAG', { size: 70, fill: 0xFFFFFF }); headerText.anchor.set(0.5, 0); headerText.x = containerWidth / 2; headerText.y = 20; headerText.isCubixionBagImage = true; self.bagScrollContainer.addChild(headerText); totalHeight = 120; // Starting height after header // Store references to cubix count Text2 for live updating if (!self._cubixCountTexts) self._cubixCountTexts = {}; for (var i = 0; i < cubixRarities.length; i++) { var rarity = cubixRarities[i]; // Always access the latest playerCubix from global or storage for UI accuracy if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") { playerCubix = window.playerCubix; } if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") { playerCubix = storage.playerCubix; } if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } // Defensive: always show all rarities, even if 0 var count = playerCubix && typeof playerCubix[rarity] === "number" ? playerCubix[rarity] : 0; // Create a container for this item row for better organization var itemContainer = new Container(); itemContainer.y = totalHeight; itemContainer.isCubixionBagImage = true; self.bagScrollContainer.addChild(itemContainer); // Add background for this item var itemBg = LK.getAsset('grassTile', { anchorX: 0, anchorY: 0, width: containerWidth, height: itemHeight }); itemBg.alpha = 0.2; itemBg.tint = cubixColors[rarity]; itemContainer.addChild(itemBg); // Always show all rarities, even if 0 var cubixImg = LK.getAsset('cubix_' + rarity, { anchorX: 0.5, anchorY: 0.5, x: 150, y: itemHeight / 2, scaleX: 2.5, // Bigger image (increased from 2.0 to 2.5) scaleY: 2.5 // Bigger image (increased from 2.0 to 2.5) }); itemContainer.addChild(cubixImg); var cubixName = new Text2(cubixNames[rarity], { size: 80, // Bigger text (increased from 70 to 80) fill: cubixColors[rarity] }); cubixName.anchor.set(0, 0.5); cubixName.x = 300; cubixName.y = itemHeight / 2 - 30; itemContainer.addChild(cubixName); var cubixDescription = new Text2('Used for capturing creatures', { size: 55, // Bigger text (increased from 45 to 55) fill: 0xCCCCCC }); cubixDescription.anchor.set(0, 0.5); cubixDescription.x = 300; cubixDescription.y = itemHeight / 2 + 30; itemContainer.addChild(cubixDescription); // Make sure to display the correct count from playerCubix var countText = new Text2('x' + count, { size: 100, // Bigger text (increased from 90 to 100) fill: 0xFFFFFF }); countText.anchor.set(1, 0.5); countText.x = containerWidth - 80; countText.y = itemHeight / 2; itemContainer.addChild(countText); // Store reference for live updating self._cubixCountTexts[rarity] = countText; // Add a select button for this cubix type (for catching) var selectBtn = LK.getAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: containerWidth - 300, y: itemHeight / 2, scaleX: 1.2, scaleY: 1.2 }); selectBtn.interactive = true; itemContainer.addChild(selectBtn); var selectText = new Text2('SELECT', { size: 40, fill: 0xFFD700 }); selectText.anchor.set(0.5, 0.5); selectText.x = containerWidth - 300; selectText.y = itemHeight / 2; itemContainer.addChild(selectText); (function (rarity) { selectBtn.down = function () { // Set selected cubix type for catching if (typeof encounter !== "undefined" && encounter.active) { encounter.selectedCubix = rarity; // Optionally, show a message or highlight var msg = new Text2('Selected ' + cubixNames[rarity] + ' for catching!', { size: 40, fill: cubixColors[rarity] }); msg.anchor.set(0.5, 0.5); msg.x = 1024; msg.y = 400; game.addChild(msg); LK.setTimeout(function () { game.removeChild(msg); }, 800); } }; })(rarity); totalHeight += itemHeight + padding; } // Add a live update function for cubix counts if (!self._cubixBagLiveUpdateAdded) { self._cubixBagLiveUpdateAdded = true; // Add to game.update for live updating var oldGameUpdate = typeof game.update === "function" ? game.update : null; game.update = function () { // Live update cubix counts in bag if bag is visible if (self.bagMaskContainer && self.bagMaskContainer.visible && self._cubixCountTexts) { // Always get latest playerCubix from global or storage if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") { playerCubix = window.playerCubix; } if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") { playerCubix = storage.playerCubix; } if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } for (var i = 0; i < cubixRarities.length; i++) { var rarity = cubixRarities[i]; if (self._cubixCountTexts[rarity]) { var newCount = playerCubix[rarity] || 0; var txt = self._cubixCountTexts[rarity]; if (txt.text !== 'x' + newCount) { txt.setText('x' + newCount); } } } } if (oldGameUpdate) oldGameUpdate(); }; } // Add a section for other potential items (empty for now, but prepared for future items) if (totalHeight > 0) { var otherItemsHeader = new Text2('OTHER ITEMS', { size: 50, fill: 0xFFFFFF }); otherItemsHeader.anchor.set(0.5, 0); otherItemsHeader.x = containerWidth / 2; otherItemsHeader.y = totalHeight; otherItemsHeader.isCubixionBagImage = true; self.bagScrollContainer.addChild(otherItemsHeader); totalHeight += 80; // Add placeholder message for future items var placeholderText = new Text2('No other items in your bag yet', { size: 36, fill: 0xAAAAAA }); placeholderText.anchor.set(0.5, 0); placeholderText.x = containerWidth / 2; placeholderText.y = totalHeight; placeholderText.isCubixionBagImage = true; self.bagScrollContainer.addChild(placeholderText); totalHeight += 100; } // Set the max scroll value based on content height self.bagMaxScroll = Math.max(0, totalHeight - 900); // 900 is approximate visible area height // Update scroll indicators visibility self.updateScrollIndicators(); // Add scroll functionality self.bagMaskContainer.interactive = true; // Remove any existing listeners to prevent duplicates if (self.bagMaskContainer.down) { self.bagMaskContainer.down = null; } if (self.bagMaskContainer.up) { self.bagMaskContainer.up = null; } if (self.bagMaskContainer.move) { self.bagMaskContainer.move = null; } self.bagMaskContainer.down = function (x, y, obj) { self.bagScrollingActive = true; self.bagScrollStartY = y; self.bagScrollStartPos = self.bagScrollContainer.y; }; self.bagMaskContainer.move = function (x, y, obj) { if (self.bagScrollingActive) { var delta = y - self.bagScrollStartY; var newY = self.bagScrollStartPos + delta; // Clamp scrolling if (newY > 0) newY = 0; if (newY < -self.bagMaxScroll) newY = -self.bagMaxScroll; self.bagScrollContainer.y = newY; self.updateScrollIndicators(); } }; self.bagMaskContainer.up = function () { self.bagScrollingActive = false; // Only one spawn near player per update if (Math.abs(self.bagScrollContainer.y - self.bagScrollStartPos) > 5) { var velocity = (self.bagScrollContainer.y - self.bagScrollStartPos) * 0.3; var targetY = self.bagScrollContainer.y + velocity; // Clamp target if (targetY > 0) targetY = 0; if (targetY < -self.bagMaxScroll) targetY = -self.bagMaxScroll; // Animate to target position tween(self.bagScrollContainer, { y: targetY }, { duration: 300, easing: tween.easeOut, onUpdate: function onUpdate() { self.updateScrollIndicators(); } }); } // Add back button if not already added if (!self.backToCollectionButton) { self.backToCollectionButton = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1180, scaleX: 1.5, scaleY: 1.5 }); self.backToCollectionButton.interactive = true; var backBtnText = new Text2('BACK TO COLLECTION', { size: 50, fill: 0xFFD700 }); backBtnText.anchor.set(0.5, 0.5); backBtnText.x = 1024; backBtnText.y = 1180; self.addChild(self.backToCollectionButton); self.addChild(backBtnText); self.backToCollectionButton.backBtnText = backBtnText; self.backToCollectionButton.down = function () { // Toggle bag visibility off and show collection menu self.bagMaskContainer.visible = false; self.bagTitleText.visible = false; self.scrollUpIndicator.visible = false; self.scrollDownIndicator.visible = false; self.backToCollectionButton.visible = false; self.backToCollectionButton.backBtnText.visible = false; // Change title back to collection menu if (self.titleText) { self.titleText.setText('CUBIXION MENU'); } // Show bag button again self.bagButton.visible = true; if (self.bagBtnText) { self.bagBtnText.visible = true; } // Hide Cubixion menu button if it exists if (self.cubixionMenuButton) { self.cubixionMenuButton.visible = false; self.cubixionMenuBtnText.visible = false; } // Also hide collection button if it exists if (self.collectionButton) { self.collectionButton.visible = false; if (self.collectionButton.colBtnText) { self.collectionButton.colBtnText.visible = false; } } }; } // Make sure the back button is visible when the bag is visible if (self.backToCollectionButton) { self.backToCollectionButton.visible = self.bagMaskContainer.visible; if (self.backToCollectionButton.backBtnText) { self.backToCollectionButton.backBtnText.visible = self.bagMaskContainer.visible; } } // Make sure the back button is visible when the bag is visible if (self.backToCollectionButton) { self.backToCollectionButton.visible = self.bagMaskContainer.visible; if (self.backToCollectionButton.backBtnText) { self.backToCollectionButton.backBtnText.visible = self.bagMaskContainer.visible; } } // Add Collection button to left bottom corner in bag menu if (!self.collectionButton) { self.collectionButton = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 1180, scaleX: 1.5, scaleY: 1.5 }); self.collectionButton.interactive = true; var colBtnText = new Text2('COLLECTION', { size: 50, fill: 0xFFD700 }); colBtnText.anchor.set(0.5, 0.5); colBtnText.x = 200; colBtnText.y = 1180; self.addChild(self.collectionButton); self.addChild(colBtnText); self.collectionButton.colBtnText = colBtnText; self.collectionButton.down = function () { // Same function as backToCollectionButton - toggle bag visibility off and show collection menu self.bagMaskContainer.visible = false; self.bagTitleText.visible = false; self.scrollUpIndicator.visible = false; self.scrollDownIndicator.visible = false; self.backToCollectionButton.visible = false; self.backToCollectionButton.backBtnText.visible = false; self.collectionButton.visible = false; self.collectionButton.colBtnText.visible = false; // Change title back to collection menu if (self.titleText) { self.titleText.setText('CUBIX MENU'); } }; } // Make collection button visible when bag is visible if (self.collectionButton) { self.collectionButton.visible = self.bagMaskContainer.visible; if (self.collectionButton.colBtnText) { self.collectionButton.colBtnText.visible = self.bagMaskContainer.visible; } } // Add Collection button to left bottom corner in bag menu if (!self.collectionButton) { self.collectionButton = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 1180, scaleX: 1.5, scaleY: 1.5 }); self.collectionButton.interactive = true; var colBtnText = new Text2('COLLECTION', { size: 50, fill: 0xFFD700 }); colBtnText.anchor.set(0.5, 0.5); colBtnText.x = 200; colBtnText.y = 1180; self.addChild(self.collectionButton); self.addChild(colBtnText); self.collectionButton.colBtnText = colBtnText; self.collectionButton.down = function () { // Same function as backToCollectionButton - toggle bag visibility off and show collection menu self.bagMaskContainer.visible = false; self.bagTitleText.visible = false; self.scrollUpIndicator.visible = false; self.scrollDownIndicator.visible = false; self.backToCollectionButton.visible = false; self.backToCollectionButton.backBtnText.visible = false; self.collectionButton.visible = false; self.collectionButton.colBtnText.visible = false; // Change title back to collection menu if (self.titleText) { self.titleText.setText('CUBIXION MENU'); } // Show bag button again self.bagButton.visible = true; if (self.bagBtnText) { self.bagBtnText.visible = true; } // Hide Cubixion menu button if it exists if (self.cubixionMenuButton) { self.cubixionMenuButton.visible = false; self.cubixionMenuBtnText.visible = false; } }; } // Make collection button visible when bag is visible if (self.collectionButton) { self.collectionButton.visible = self.bagMaskContainer.visible; if (self.collectionButton.colBtnText) { self.collectionButton.colBtnText.visible = self.bagMaskContainer.visible; } } }; }; // Update scroll indicators based on current scroll position self.updateScrollIndicators = function () { if (self.bagMaxScroll <= 0) { // No need for indicators if content fits self.scrollUpIndicator.visible = false; self.scrollDownIndicator.visible = false; return; } // Show up indicator only if scrolled down self.scrollUpIndicator.visible = self.bagMaskContainer.visible && self.bagScrollContainer.y < 0; // Show down indicator only if can scroll further down self.scrollDownIndicator.visible = self.bagMaskContainer.visible && self.bagScrollContainer.y > -self.bagMaxScroll; // Fade indicators based on scroll position self.scrollUpIndicator.alpha = Math.min(1, Math.abs(self.bagScrollContainer.y) / 100); self.scrollDownIndicator.alpha = Math.min(1, (self.bagMaxScroll + self.bagScrollContainer.y) / 100); }; // Example: Add a placeholder for "other items" in the bag // You can add more items here as needed var bagItemY = 70; // Remove potion and revive from the bag playerBagItems = []; // No items to display in the bag // Add title self.titleText = new Text2('CUBIXION MENU', { size: 80, fill: 0xFFFFFF }); self.titleText.anchor.set(0.5, 0); self.titleText.x = 1024; self.titleText.y = 50; self.addChild(self.titleText); // Show owned Cubixion with image, name, type, and count in collection menu // Remove previous Cubixion display if present if (self.cubixionDisplay && self.cubixionDisplay.length) { for (var i = 0; i < self.cubixionDisplay.length; i++) { self.removeChild(self.cubixionDisplay[i]); } } self.cubixionDisplay = []; // Defensive: get playerCubixionCollection from storage or window if not present if (typeof playerCubixionCollection === "undefined" && typeof storage !== "undefined" && typeof storage.playerCubixionCollection !== "undefined") { playerCubixionCollection = storage.playerCubixionCollection; } if (typeof playerCubixionCollection === "undefined" && typeof window !== "undefined" && typeof window.playerCubixionCollection !== "undefined") { playerCubixionCollection = window.playerCubixionCollection; } if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {}; // Build ownedCubixion array from playerCubixionCollection var ownedCubixion = []; for (var key in playerCubixionCollection) { if (playerCubixionCollection.hasOwnProperty(key) && playerCubixionCollection[key].count > 0) { // Try to find matching cubixType for bag cubix var cType = null; for (var j = 0; j < cubixTypes.length; j++) { if (cubixTypes[j].rarity === key) { cType = cubixTypes[j]; break; } } // Try to find matching creature for creature cubixion if (!cType && typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) { for (var j = 0; j < Creature.prototype.creatureList.length; j++) { if (String(Creature.prototype.creatureList[j].id) === String(key)) { cType = { id: Creature.prototype.creatureList[j].id, name: Creature.prototype.creatureList[j].name, type: Creature.prototype.creatureList[j].type, image: Creature.prototype.creatureList[j].image, rarity: 'common', color: 0xffffff }; break; } } } if (cType) { ownedCubixion.push({ type: cType, count: playerCubixionCollection[key].count }); } } } // Sort by element (type) alphabetically ownedCubixion.sort(function (a, b) { if (!a.type.type) return -1; if (!b.type.type) return 1; if (a.type.type < b.type.type) return -1; if (a.type.type > b.type.type) return 1; return 0; }); var cubixionStartX = 400; var cubixionStartY = 140; var cubixionSpacing = 320; for (var i = 0; i < ownedCubixion.length; i++) { var cType = ownedCubixion[i].type; var count = ownedCubixion[i].count; // Cubixion image var cubixionImg = LK.getAsset(cType.image ? cType.image : 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: cubixionStartX + i * cubixionSpacing, y: cubixionStartY + 80, scaleX: 0.7, scaleY: 0.7 }); self.addChild(cubixionImg); self.cubixionDisplay.push(cubixionImg); // Cubixion name var cubixionName = new Text2(cType.name ? cType.name.replace(' Cubix', 'ion') : cType.rarity ? cType.rarity.charAt(0).toUpperCase() + cType.rarity.slice(1) + " Cubixion" : "Cubixion", { size: 38, fill: cType.color || 0xffffff }); cubixionName.anchor.set(0.5, 0); cubixionName.x = cubixionStartX + i * cubixionSpacing; cubixionName.y = cubixionStartY + 160; self.addChild(cubixionName); self.cubixionDisplay.push(cubixionName); // Cubixion type (element) var elementText = new Text2('Type: ' + (cType.type ? cType.type.toUpperCase() : '-'), { size: 32, fill: 0xFFFFFF }); elementText.anchor.set(0.5, 0); elementText.x = cubixionStartX + i * cubixionSpacing; elementText.y = cubixionStartY + 200; self.addChild(elementText); self.cubixionDisplay.push(elementText); // Cubixion count var countText = new Text2('x' + count, { size: 36, fill: 0xFFFFFF }); countText.anchor.set(0.5, 0); countText.x = cubixionStartX + i * cubixionSpacing; countText.y = cubixionStartY + 240; self.addChild(countText); self.cubixionDisplay.push(countText); } // 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; // Make sure the bag container is initialized but hidden initially if (self.bagMaskContainer) { self.bagMaskContainer.visible = false; self.bagTitleText.visible = false; self.scrollUpIndicator.visible = false; self.scrollDownIndicator.visible = false; // Initialize bag items on first show self.populateBagItems(); // Make sure back button is properly hidden if (self.backToCollectionButton) { self.backToCollectionButton.visible = false; if (self.backToCollectionButton.backBtnText) { self.backToCollectionButton.backBtnText.visible = false; } } } // Remove any collection menu under the cross (close) button if present for (var i = self.children.length - 1; i >= 0; i--) { var child = self.children[i]; // Remove any Text2 or Container that is at or near the close button position (1900, 100) if ((child instanceof Text2 || typeof child.x === "number" && typeof child.y === "number") && Math.abs(child.x - 1900) < 80 && Math.abs(child.y - 100) < 80 && child !== self.closeButton) { self.removeChild(child); } } // 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; // Calculate total Cubixion count var totalCubixion = 0; var ownedCubixion = []; if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") { playerCubix = window.playerCubix; } if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") { playerCubix = storage.playerCubix; } if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } // --- Fix: Show caught Cubixion in collection menu with image --- // Use playerCubixionCollection to show all caught Cubixion if (typeof playerCubixionCollection === "undefined" && typeof storage !== "undefined" && typeof storage.playerCubixionCollection !== "undefined") { playerCubixionCollection = storage.playerCubixionCollection; } if (typeof playerCubixionCollection === "undefined" && typeof window !== "undefined" && typeof window.playerCubixionCollection !== "undefined") { playerCubixionCollection = window.playerCubixionCollection; } if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {}; // Defensive: always sync to storage and window if (typeof storage !== "undefined" && typeof storage.set === "function") { storage.set("playerCubixionCollection", playerCubixionCollection); } else if (typeof storage !== "undefined") { try { storage.playerCubixionCollection = playerCubixionCollection; } catch (e) { // Fallback: do nothing if assignment fails } } if (typeof window !== "undefined") window.playerCubixionCollection = playerCubixionCollection; // Build ownedCubixion from playerCubixionCollection ownedCubixion = []; totalCubixion = 0; // Add Cubixion from creatures if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) { for (var i = 0; i < Creature.prototype.creatureList.length; i++) { var c = Creature.prototype.creatureList[i]; if (playerCubixionCollection[c.id] && playerCubixionCollection[c.id].count > 0) { ownedCubixion.push({ type: { id: c.id, name: c.name, type: c.type, image: c.image, rarity: 'common', color: 0xffffff // Optionally set color, or use a color map by type }, count: playerCubixionCollection[c.id].count }); totalCubixion += playerCubixionCollection[c.id].count; } } } // Do NOT add Cubix (pokeball) types to Cubixion menu; only show Cubixion (creatures) in collection menu // (Intentionally left blank: skip adding cubixTypes to ownedCubixion here) // Show Cubixion count at the top of collection menu var cubixionCountText = new Text2('Cubixion: ' + totalCubixion, { size: 50, fill: 0xFFFFFF }); cubixionCountText.anchor.set(0.5, 0); cubixionCountText.x = 1024; cubixionCountText.y = 150; self.addChild(cubixionCountText); // Remove previous Cubixion image display if present if (self.cubixionImageDisplay && Array.isArray(self.cubixionImageDisplay) && self.cubixionImageDisplay.length) { for (var i = 0; i < self.cubixionImageDisplay.length; i++) { self.removeChild(self.cubixionImageDisplay[i]); } } self.cubixionImageDisplay = []; // Show Cubixion as images in a row var imgStartX = 400; var imgY = 220; var imgSpacing = 320; for (var i = 0; i < ownedCubixion.length; i++) { var cType = ownedCubixion[i].type; var count = ownedCubixion[i].count; var cubixionImg = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: imgStartX + i * imgSpacing, y: imgY + 80, scaleX: 0.7, scaleY: 0.7 }); self.addChild(cubixionImg); self.cubixionImageDisplay.push(cubixionImg); // Cubixion name var cubixionName = new Text2(cType.name.replace(' Cubix', 'ion'), { size: 38, fill: cType.color }); cubixionName.anchor.set(0.5, 0); cubixionName.x = imgStartX + i * imgSpacing; cubixionName.y = imgY + 160; self.addChild(cubixionName); self.cubixionImageDisplay.push(cubixionName); // Cubixion count var countText = new Text2('x' + count, { size: 36, fill: 0xFFFFFF }); countText.anchor.set(0.5, 0); countText.x = imgStartX + i * imgSpacing; countText.y = imgY + 210; self.addChild(countText); self.cubixionImageDisplay.push(countText); // Show gems for this cubixion var cid = cType.id !== undefined ? cType.id : cType.creatureId; var creatureIdx = typeof cType.creatureId !== "undefined" ? cType.creatureId : typeof cType.id !== "undefined" ? cType.id : null; var gems = 0; if (creatureIdx !== null && _typeof2(playerCubixionCollection) === "object" && playerCubixionCollection[creatureIdx]) { gems = playerCubixionCollection[creatureIdx].gems || 0; } var gemText = new Text2('Gems: ' + gems, { size: 28, fill: 0xFFD700 }); gemText.anchor.set(0.5, 0); gemText.x = imgStartX + i * imgSpacing; gemText.y = imgY + 250; self.addChild(gemText); self.cubixionImageDisplay.push(gemText); // Add evolve button if possible if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) { var evoId = null; // Find the creature in the database for (var ci = 0; ci < Creature.prototype.creatureList.length; ci++) { var c = Creature.prototype.creatureList[ci]; if (c.name === cType.name.replace(' Cubix', '') && c.type === cType.type) { evoId = c.evolvesTo; break; } } if (evoId !== null && typeof Creature.prototype.creatureList[evoId] !== "undefined" && gems > 0) { // Show evolve button var evolveBtn = self.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: imgStartX + i * imgSpacing, y: imgY + 320 }); evolveBtn.interactive = true; var evolveText = new Text2('EVOLVE', { size: 28, fill: 0x00FF00 }); evolveText.anchor.set(0.5, 0.5); evolveText.x = imgStartX + i * imgSpacing; evolveText.y = imgY + 320; self.addChild(evolveBtn); self.addChild(evolveText); self.cubixionImageDisplay.push(evolveBtn); self.cubixionImageDisplay.push(evolveText); // Evolve logic: spend 1 gem, unlock evolved form (function (creatureIdx, evoId) { evolveBtn.down = function () { if (playerCubixionCollection[creatureIdx] && playerCubixionCollection[creatureIdx].gems > 0) { playerCubixionCollection[creatureIdx].gems--; // Add evolved form to collection if (!playerCubixionCollection[evoId]) playerCubixionCollection[evoId] = { count: 0, gems: 0 }; playerCubixionCollection[evoId].count++; playerCubixionCollection[evoId].gems++; // Save updated collection to storage if (typeof storage !== "undefined" && typeof storage.set === "function") { storage.set("playerCubixionCollection", playerCubixionCollection); } else { if (typeof storage !== "undefined" && typeof storage.set === "function") { storage.set("playerCubixionCollection", playerCubixionCollection); } else { storage.playerCubixionCollection = playerCubixionCollection; } } // Show message var msg = new Text2('Evolved to ' + Creature.prototype.creatureList[evoId].name + '!', { size: 60, fill: 0x00FF00 }); msg.anchor.set(0.5, 0.5); msg.x = 1024; msg.y = 400; self.addChild(msg); LK.setTimeout(function () { self.removeChild(msg); // Refresh collection menu to show new evolution if (typeof self.show === "function") self.show(); }, 1000); } }; })(creatureIdx, evoId); } } } // Create grid of discovered creatures var gridX = 0; var gridY = 0; // Make collection menu much bigger var itemsPerRow = 7; var cellSize = 480; // Build a list of all possible creatures (by name, not by element type) var allCreatures = []; if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) { for (var i = 0; i < Creature.prototype.creatureList.length; i++) { var c = Creature.prototype.creatureList[i]; // For each rarity, add a possible entry var allRarities = ['common', 'uncommon', 'rare', 'epic', 'legendary']; for (var r = 0; r < allRarities.length; r++) { allCreatures.push({ name: c.name, type: c.type, image: c.image, rarity: allRarities[r] }); } } } // Sort allCreatures by name (alphabetically) allCreatures.sort(function (a, b) { if (a.name < b.name) return -1; if (a.name > b.name) return 1; return 0; }); var itemsPerRow = 7; var cellSize = 480; for (var idx = 0; idx < allCreatures.length; idx++) { var creature = allCreatures[idx]; var key = creature.type + '-' + creature.rarity; var gridX = idx % itemsPerRow; var gridY = Math.floor(idx / itemsPerRow); 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(creature.image, { anchorX: 0.5, anchorY: 0.5, x: cellSize / 2, y: cellSize / 2 - 40 }); // Apply rarity styling var rarityColor = 0xFFFFFF; if (creature.rarity === 'uncommon') rarityColor = 0x00FF00; if (creature.rarity === 'rare') rarityColor = 0x0000FF; if (creature.rarity === 'epic') rarityColor = 0xFF00FF; if (creature.rarity === 'legendary') rarityColor = 0xFFD700; // Make rarer creatures slightly larger var scale = 1.0; if (creature.rarity === 'uncommon') scale = 1.1; if (creature.rarity === 'rare') scale = 1.2; if (creature.rarity === 'epic') scale = 1.3; if (creature.rarity === 'legendary') scale = 1.5; creatureGraphic.scale.set(scale, scale); cell.addChild(creatureGraphic); // Add info text with name, element type, and rarity under each discovered Cubixion var infoText = new Text2(creature.name + '\nElement: ' + creature.type.toUpperCase() + '\nRarity: ' + creature.rarity.toUpperCase(), { size: 40, fill: 0xFFFFFF }); infoText.anchor.set(0.5, 0); infoText.x = cellSize / 2; infoText.y = cellSize / 2 + 60; cell.addChild(infoText); } else { // Undiscovered creature (shadow) var undiscoveredGraphic = LK.getAsset(creature.type + 'Cubixion', { 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: 80, fill: 0x999999 }); questionText.anchor.set(0.5, 0.5); questionText.x = cellSize / 2; questionText.y = cellSize / 2; cell.addChild(questionText); } } // --- Fluent drag-to-scroll logic for scrollContainer --- self.scrollContainer.interactive = true; self.scrollContainer.hitArea = new Rectangle(0, 0, itemsPerRow * cellSize, (gridY + 1) * cellSize); var isDragging = false; var dragStartY = 0; var dragStartScrollY = 0; var minY = 0; var maxY = Math.max(0, (gridY + 1) * cellSize - 1800); // 1800px is approx. visible area // For momentum/fluent scroll var lastMoveTime = 0; var lastMoveY = 0; var velocityY = 0; var momentumTimer = null; self.scrollContainer.down = function (x, y, obj) { isDragging = true; dragStartY = y; dragStartScrollY = self.scrollContainer.y; lastMoveTime = Date.now(); lastMoveY = y; velocityY = 0; if (momentumTimer) { LK.clearInterval(momentumTimer); momentumTimer = null; } // Prevent accidental selection of creatures while dragging if (obj && obj.event && obj.event.stopPropagation) obj.event.stopPropagation(); }; self.scrollContainer.move = function (x, y, obj) { if (isDragging) { var now = Date.now(); var newY = dragStartScrollY + (y - dragStartY); // Clamp scroll if (newY > 200) newY = 200; if (newY < -maxY) newY = -maxY; self.scrollContainer.y = newY; // Calculate velocity for momentum var dt = now - lastMoveTime; if (dt > 0) { velocityY = (y - lastMoveY) / dt; lastMoveTime = now; lastMoveY = y; } } }; self.scrollContainer.up = function (x, y, obj) { isDragging = false; // Start momentum scroll if velocity is significant if (Math.abs(velocityY) > 0.1) { var decay = 0.95; momentumTimer = LK.setInterval(function () { self.scrollContainer.y += velocityY * 30; // Clamp scroll if (self.scrollContainer.y > 200) self.scrollContainer.y = 200; if (self.scrollContainer.y < -maxY) self.scrollContainer.y = -maxY; velocityY *= decay; if (Math.abs(velocityY) < 0.05) { LK.clearInterval(momentumTimer); momentumTimer = null; } }, 16); } }; }; // Hide collection screen self.hide = function () { self.visible = false; // Make sure to hide bag containers too if (self.bagMaskContainer) { self.bagMaskContainer.visible = false; } if (self.bagTitleText) { self.bagTitleText.visible = false; } if (self.scrollUpIndicator) { self.scrollUpIndicator.visible = false; } if (self.scrollDownIndicator) { self.scrollDownIndicator.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 + "Cubixion"; 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; // --- Cubix UI at top middle for catching action (single UI, no Cubixion UI) --- // Remove any previous Cubixion info UI in the middle (if any) and only show the top middle UI // (Erase any Cubixion info UI in the middle of the screen, so only the top middle Cubix info UI is shown) if (self.cubixionInfoUI) { for (var key in self.cubixionInfoUI) { if (self.cubixionInfoUI[key]) { if (Array.isArray(self.cubixionInfoUI[key])) { for (var j = 0; j < self.cubixionInfoUI[key].length; j++) { if (self.cubixionInfoUI[key][j] && self.cubixionInfoUI[key][j].parent === self) { // Only remove if it is not at the top middle (y < 300) if (typeof self.cubixionInfoUI[key][j].y === "number" && self.cubixionInfoUI[key][j].y > 300) { self.removeChild(self.cubixionInfoUI[key][j]); } } } } else if (self.cubixionInfoUI[key].parent === self) { if (typeof self.cubixionInfoUI[key].y === "number" && self.cubixionInfoUI[key].y > 300) { self.removeChild(self.cubixionInfoUI[key]); } } } } } self.cubixionInfoUI = {}; // Defensive: always ensure playerCubix is defined and up to date, and always use the global reference if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") { playerCubix = window.playerCubix; } if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") { playerCubix = storage.playerCubix; } if (_typeof5(playerCubix) !== "object" || !playerCubix) { playerCubix = { basic: 0, uncommon: 0, rare: 0, legendary: 0 }; } if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } // Show only Cubix (pokeball) UI at top middle with selection and catch chance var cubixRarities = ['basic', 'uncommon', 'rare', 'legendary']; var cubixNames = { basic: 'Basic Cubix', uncommon: 'Uncommon Cubix', rare: 'Rare Cubix', legendary: 'Legendary Cubix' }; var cubixColors = { basic: 0xffffff, uncommon: 0x00ff00, rare: 0x0000ff, legendary: 0xffd700 }; var cubixStartX = 1024 - 1.5 * 220; var cubixSpacing = 220; var cubixY = 60; self.cubixionInfoUI.cubixImgArr = []; self.cubixionInfoUI.cubixNameArr = []; self.cubixionInfoUI.cubixTypeArr = []; self.cubixionInfoUI.cubixCountArr = []; self.cubixionInfoUI.cubixSelectArr = []; self.cubixionInfoUI.cubixChanceArr = []; self.selectedCubix = null; // Only one UI: show all cubix types in a row at top middle, with image, name, type, count, and selection, and catch chance for (var i = 0; i < cubixRarities.length; i++) { var rarity = cubixRarities[i]; var count = playerCubix && typeof playerCubix[rarity] === "number" ? playerCubix[rarity] : 0; // Cubix image var cubixImg = LK.getAsset('cubix_' + rarity, { anchorX: 0.5, anchorY: 0, x: cubixStartX + i * cubixSpacing, y: cubixY, scaleX: 0.5, scaleY: 0.5 }); self.addChild(cubixImg); // Cubix name var cubixName = new Text2(cubixNames[rarity], { size: 36, fill: cubixColors[rarity] }); cubixName.anchor.set(0.5, 0); cubixName.x = cubixStartX + i * cubixSpacing; cubixName.y = cubixY + 110; self.addChild(cubixName); // Cubix type (rarity) var cubixType = new Text2(rarity.charAt(0).toUpperCase() + rarity.slice(1), { size: 28, fill: cubixColors[rarity] }); cubixType.anchor.set(0.5, 0); cubixType.x = cubixStartX + i * cubixSpacing; cubixType.y = cubixY + 150; self.addChild(cubixType); // Cubix count var cubixCount = new Text2('x' + count, { size: 36, fill: 0xffffff }); cubixCount.anchor.set(0.5, 0); cubixCount.x = cubixStartX + i * cubixSpacing; cubixCount.y = cubixY + 190; self.addChild(cubixCount); // Show catch chance for this cubix var baseChance = 0.5; if (self.creature && self.creature.rarity) { 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 bonus = 0; for (var j = 0; j < cubixTypes.length; j++) { if (cubixTypes[j].rarity === rarity && typeof cubixTypes[j].catchBonus === "number") { bonus = cubixTypes[j].catchBonus; break; } } var finalChance = baseChance + bonus; if (finalChance > 1) finalChance = 1; var percent = Math.round(finalChance * 100); var cubixChance = new Text2('Chance: ' + percent + '%', { size: 28, fill: 0xFFD700 }); cubixChance.anchor.set(0.5, 0); cubixChance.x = cubixStartX + i * cubixSpacing; cubixChance.y = cubixY + 220; self.addChild(cubixChance); // Selection highlight (invisible by default) var selectRect = LK.getAsset('grassTile', { anchorX: 0.5, anchorY: 0, x: cubixStartX + i * cubixSpacing, y: cubixY - 10, width: 120, height: 240 }); selectRect.alpha = 0.0; selectRect.tint = 0xFFD700; self.addChild(selectRect); // Make cubix image interactive for selection cubixImg.interactive = true; (function (rarity, idx) { cubixImg.down = function () { if (playerCubix[rarity] > 0) { // Remove highlight from all for (var j = 0; j < self.cubixionInfoUI.cubixSelectArr.length; j++) { self.cubixionInfoUI.cubixSelectArr[j].alpha = 0.0; } // Highlight this one self.cubixionInfoUI.cubixSelectArr[idx].alpha = 0.5; self.selectedCubix = rarity; } }; })(rarity, i); // If player has at least one, select the first available by default if (self.selectedCubix === null && count > 0) { self.selectedCubix = rarity; selectRect.alpha = 0.5; } self.cubixionInfoUI.cubixImgArr.push(cubixImg); self.cubixionInfoUI.cubixNameArr.push(cubixName); self.cubixionInfoUI.cubixTypeArr.push(cubixType); self.cubixionInfoUI.cubixCountArr.push(cubixCount); self.cubixionInfoUI.cubixSelectArr.push(selectRect); self.cubixionInfoUI.cubixChanceArr.push(cubixChance); } // --- Cubix selection UI --- self.selectedCubix = 'basic'; // Default // Defensive: always ensure playerCubix is defined and up to date, and always use the global reference if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") { playerCubix = window.playerCubix; } if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") { playerCubix = storage.playerCubix; } if (_typeof3(playerCubix) !== "object" || !playerCubix) { playerCubix = { basic: 0, uncommon: 0, rare: 0, legendary: 0 }; } if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } // Defensive: ensure all rarities are present and are numbers, and always update the global reference for (var i = 0; i < cubixTypes.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; var rarity = cubixTypes[i].rarity; if (typeof playerCubix[rarity] !== "number" || isNaN(playerCubix[rarity])) { playerCubix[rarity] = 0; } } // Defensive: always sync playerCubix to storage and global scope if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } else { if (typeof storage !== "undefined") { if (typeof storage.set === "function") { storage.set("playerCubix", playerCubix); } else { storage.playerCubix = { basic: playerCubix.basic, uncommon: playerCubix.uncommon, rare: playerCubix.rare, legendary: playerCubix.legendary }; } } if (typeof window !== "undefined") { window.playerCubix = playerCubix; } } // Only allow selection of cubix the player has (at least one of any type) function getTotalCubixCount() { var total = 0; for (var i = 0; i < cubixTypes.length; i++) { total += playerCubix[cubixTypes[i].rarity] || 0; } return total; } // Defensive: always get the latest value from global and storage before checking var totalCubixCount = getTotalCubixCount(); var hasAnyCubix = false; for (var i = 0; i < cubixTypes.length; i++) { if ((playerCubix[cubixTypes[i].rarity] || 0) > 0) { hasAnyCubix = true; break; } } // Defensive: also check for negative or non-integer values (should never happen, but just in case) // --- FIX: Check for all possible causes of 'No Cubix' and resync inventory if needed --- var cubixError = false; var cubixErrorMsg = ''; // Defensive: check for undefined, null, negative, or non-integer values for (var i = 0; i < cubixTypes.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; var rarity = cubixTypes[i].rarity; if (typeof playerCubix[rarity] !== "number" || isNaN(playerCubix[rarity]) || playerCubix[rarity] < 0) { playerCubix[rarity] = 0; cubixError = true; cubixErrorMsg = 'Cubix inventory error detected. Inventory was reset.'; } } // Defensive: check for missing playerCubix object if (!playerCubix || _typeof4(playerCubix) !== "object") { playerCubix = { basic: 0, uncommon: 0, rare: 0, legendary: 0 }; cubixError = true; cubixErrorMsg = 'Cubix inventory missing. Inventory was reset.'; } // Defensive: check for out-of-sync inventory (storage vs global) if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") { for (var i = 0; i < cubixTypes.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; var rarity = cubixTypes[i].rarity; if (typeof storage.playerCubix[rarity] !== "number" || isNaN(storage.playerCubix[rarity]) || storage.playerCubix[rarity] < 0) { storage.playerCubix[rarity] = 0; cubixError = true; cubixErrorMsg = 'Cubix inventory in storage was invalid. Inventory was reset.'; } } } // Defensive: check for out-of-sync between window and storage if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") { for (var i = 0; i < cubixTypes.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; var rarity = cubixTypes[i].rarity; if (typeof window.playerCubix[rarity] !== "number" || isNaN(window.playerCubix[rarity]) || window.playerCubix[rarity] < 0) { window.playerCubix[rarity] = 0; cubixError = true; cubixErrorMsg = 'Cubix inventory in window was invalid. Inventory was reset.'; } } } // Defensive: always sync after any fix if (cubixError && typeof syncPlayerCubix === "function") { syncPlayerCubix(); } // Defensive: check for totalCubixCount again after all fixes totalCubixCount = getTotalCubixCount(); hasAnyCubix = false; for (var i = 0; i < cubixTypes.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; if ((playerCubix[cubixTypes[i].rarity] || 0) > 0) { hasAnyCubix = true; break; } } // If player has no Cubix, allow normal catching animation, but player must leave manually. // Do not show any warning or block the encounter. Just proceed as normal. // Show cubix selection UI with image and name, require player to choose for catching // Redesigned Cubix selection UI for better readability and info self.cubixButtons = []; self.cubixButtonLabels = []; self.cubixButtonNameLabels = []; var firstAvailableIdx = -1; var cubixButtonStartX = 400; var cubixButtonSpacing = 320; var cubixButtonY = 1580; var cubixPanelHeight = 200; var cubixPanelWidth = 300; for (var i = 0; i < cubixTypes.length; i++) { var cType = cubixTypes[i]; if (!cType || typeof cType.rarity === "undefined") continue; // Panel background for each cubix type var panelBg = LK.getAsset('grassTile', { anchorX: 0.5, anchorY: 0.5, x: cubixButtonStartX + i * cubixButtonSpacing, y: cubixButtonY + cubixPanelHeight / 2, width: cubixPanelWidth, height: cubixPanelHeight }); panelBg.alpha = 0.18; panelBg.tint = cType.color; self.addChild(panelBg); // Cubix image button var btn = self.attachAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: cubixButtonStartX + i * cubixButtonSpacing, y: cubixButtonY + 60 }); btn.scale.set(0.38, 0.38); btn.interactive = true; (function (type, idx) { btn.down = function () { if ((playerCubix[type.rarity] || 0) > 0) { self.selectedCubix = type.rarity; // Highlight selected for (var j = 0; j < self.cubixButtons.length; j++) { self.cubixButtons[j].alpha = self.cubixButtons[j].rarity === type.rarity ? 1.0 : 0.5; if (self.cubixButtonNameLabels[j] && self.cubixButtonNameLabels[j].style) { self.cubixButtonNameLabels[j].style.fill = self.cubixButtons[j].rarity === type.rarity ? 0xFFD700 : type.color; } } // Show info about catch chance if (self.cubixCatchInfoText) { self.removeChild(self.cubixCatchInfoText); } var bonus = typeof type.catchBonus === "number" ? type.catchBonus : 0; var baseChance = 0.5; if (self.creature && self.creature.rarity) { 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 + bonus; if (finalChance > 1) finalChance = 1; var percent = Math.round(finalChance * 100); self.cubixCatchInfoText = new Text2('Catch chance: ' + percent + '%', { size: 38, fill: 0xFFD700 }); self.cubixCatchInfoText.anchor.set(0.5, 0); self.cubixCatchInfoText.x = 1024; self.cubixCatchInfoText.y = 1750; self.addChild(self.cubixCatchInfoText); } }; })(cType, i); btn.rarity = cType.rarity; if ((playerCubix[cType.rarity] || 0) <= 0) { btn.alpha = 0.2; btn.interactive = false; } else { if (firstAvailableIdx === -1) firstAvailableIdx = i; } self.addChild(btn); self.cubixButtons.push(btn); // Cubix name label (bigger, bold, above count) var nameLabel = new Text2(cType.name, { size: 40, fill: cType.color }); nameLabel.anchor.set(0.5, 0); nameLabel.x = cubixButtonStartX + i * cubixButtonSpacing; nameLabel.y = cubixButtonY + 110; self.addChild(nameLabel); self.cubixButtonNameLabels.push(nameLabel); // Cubix count label (large, white, below name) var label = new Text2('x' + (playerCubix[cType.rarity] || 0), { size: 36, fill: 0xFFFFFF }); label.anchor.set(0.5, 0); label.x = cubixButtonStartX + i * cubixButtonSpacing; label.y = cubixButtonY + 160; self.addChild(label); self.cubixButtonLabels.push(label); // Cubix bonus info (smaller, below count) var bonus = typeof cType.catchBonus === "number" ? cType.catchBonus : 0; var bonusText = new Text2(bonus > 0 ? "+" + Math.round(bonus * 100) + "% catch" : "No bonus", { size: 24, fill: 0xFFD700 }); bonusText.anchor.set(0.5, 0); bonusText.x = cubixButtonStartX + i * cubixButtonSpacing; bonusText.y = cubixButtonY + 200; self.addChild(bonusText); // Rarity label (smaller, below bonus) var rarityText = new Text2(cType.rarity.charAt(0).toUpperCase() + cType.rarity.slice(1), { size: 22, fill: cType.color }); rarityText.anchor.set(0.5, 0); rarityText.x = cubixButtonStartX + i * cubixButtonSpacing; rarityText.y = cubixButtonY + 230; self.addChild(rarityText); btn.alpha = i === firstAvailableIdx ? 1.0 : 0.5; if (self.cubixButtonNameLabels[i] && self.cubixButtonNameLabels[i].style) { self.cubixButtonNameLabels[i].style.fill = i === firstAvailableIdx ? 0xFFD700 : cType.color; } } // Set default selectedCubix to first available type if (firstAvailableIdx !== -1) { self.selectedCubix = cubixTypes[firstAvailableIdx].rarity; // Show info about catch chance for default if (self.cubixCatchInfoText) { self.removeChild(self.cubixCatchInfoText); } var type = cubixTypes[firstAvailableIdx]; var bonus = typeof type.catchBonus === "number" ? type.catchBonus : 0; var baseChance = 0.5; if (self.creature && self.creature.rarity) { 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 + bonus; if (finalChance > 1) finalChance = 1; var percent = Math.round(finalChance * 100); self.cubixCatchInfoText = new Text2('Catch chance: ' + percent + '%', { size: 38, fill: 0xFFD700 }); self.cubixCatchInfoText.anchor.set(0.5, 0); self.cubixCatchInfoText.x = 1024; self.cubixCatchInfoText.y = 1750; self.addChild(self.cubixCatchInfoText); // Highlight default selected for (var j = 0; j < self.cubixButtons.length; j++) { self.cubixButtons[j].alpha = j === firstAvailableIdx ? 1.0 : 0.5; if (self.cubixButtonNameLabels[j] && self.cubixButtonNameLabels[j].style) { self.cubixButtonNameLabels[j].style.fill = j === firstAvailableIdx ? 0xFFD700 : cubixTypes[j].color; } } } // 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 }); // Defensive: check if player has any cubix before showing capture button/text var totalCubixCount = 0; if (typeof playerCubix !== "undefined" && typeof cubixTypes !== "undefined") { for (var i = 0; i < cubixTypes.length; i++) { totalCubixCount += playerCubix[cubixTypes[i].rarity] || 0; } } // --- Cubix counter UI: Show cubix type-by-type counter below cubix selection buttons in encounter --- // Remove previous cubixCountTexts if present if (self.cubixCountTexts) { for (var i = 0; i < self.cubixCountTexts.length; i++) { if (self.cubixCountTexts[i] && self.cubixCountTexts[i].parent === self) { self.removeChild(self.cubixCountTexts[i]); } } } self.cubixCountTexts = []; for (var i = 0; i < cubixTypes.length; i++) { var cType = cubixTypes[i]; if (!cType || typeof cType.rarity === "undefined") continue; // Show cubix image and count for each type var img = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: 700 + i * 200, y: 1720, scaleX: 0.18, scaleY: 0.18 }); self.addChild(img); // Show count text var count = playerCubix && typeof playerCubix[cType.rarity] === "number" ? playerCubix[cType.rarity] : 0; var countText = new Text2('x' + count, { size: 32, fill: cType.color }); countText.anchor.set(0.5, 0); countText.x = 700 + i * 200; countText.y = 1745; self.addChild(countText); self.cubixCountTexts.push(countText); } // Fix: Always show capture button if player has at least 1 of any cubix (including 10 basic cubix) if (playerCubix && (playerCubix.basic > 0 || playerCubix.uncommon > 0 || playerCubix.rare > 0 || playerCubix.legendary > 0)) { // 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 (removed attempts display) var infoText = new Text2('TYPE: ' + self.creature.type.toUpperCase() + '\n' + 'RARITY: ' + self.creature.rarity.toUpperCase(), { 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 cubix, reduce inventory var selectedType = cubixTypes[0]; for (var i = 0; i < cubixTypes.length; i++) { if (cubixTypes[i].rarity === self.selectedCubix) { selectedType = cubixTypes[i]; break; } } if (selectedType && typeof selectedType.rarity === "string" && playerCubix && typeof playerCubix[selectedType.rarity] === "number" && playerCubix[selectedType.rarity] > 0) { playerCubix[selectedType.rarity]--; // Defensive: always sync playerCubix to storage and global scope after change if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } else { if (typeof storage !== "undefined") { if (typeof storage.set === "function") { storage.set("playerCubix", playerCubix); } else { storage.playerCubix = { basic: playerCubix.basic, uncommon: playerCubix.uncommon, rare: playerCubix.rare, legendary: playerCubix.legendary }; } } if (typeof window !== "undefined") { window.playerCubix = playerCubix; } // Also decrement from Cubixion bag collection if present if (typeof playerCubixionCollection !== "undefined") { var cubixionId = selectedType.rarity; if (playerCubixionCollection[cubixionId] && typeof playerCubixionCollection[cubixionId].count === "number" && playerCubixionCollection[cubixionId].count > 0) { playerCubixionCollection[cubixionId].count--; // Defensive: always sync to storage and window if (typeof storage !== "undefined" && typeof storage.set === "function") { storage.set("playerCubixionCollection", playerCubixionCollection); } else if (typeof storage !== "undefined") { try { storage.playerCubixionCollection = playerCubixionCollection; } catch (e) {} } if (typeof window !== "undefined") window.playerCubixionCollection = playerCubixionCollection; } } } // Update cubix button labels and cubix count display if (self.cubixButtons) { for (var i = 0; i < self.cubixButtons.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; // Update label if (self.cubixButtonLabels && self.cubixButtonLabels[i]) { self.cubixButtonLabels[i].setText(cubixTypes[i].name + ' (' + (playerCubix[cubixTypes[i].rarity] || 0) + ')'); } if (playerCubix[cubixTypes[i].rarity] <= 0) self.cubixButtons[i].alpha = 0.2; } } // Update cubix count text below (by type with image) if (self.cubixCountTexts) { for (var i = 0; i < self.cubixCountTexts.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; var rarity = cubixTypes[i].rarity; if (self.cubixCountTexts[i]) { self.cubixCountTexts[i].setText('x' + (playerCubix[rarity] || 0)); } } } } // 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 selected cubix type for 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 catchBonus = 0; var selectedType = cubixTypes[0]; for (var i = 0; i < cubixTypes.length; i++) { if (cubixTypes[i].rarity === self.selectedCubix) { selectedType = cubixTypes[i]; break; } } if (selectedType && typeof selectedType.catchBonus === "number") { catchBonus = selectedType.catchBonus; } var finalChance = baseChance + catchBonus; if (finalChance > 1) finalChance = 1; if (Math.random() < finalChance) captured = true; } if (captured) { // Success! self.captureSuccess(); } else if (self.captureAttempts >= self.maxAttempts) { // Failed after max attempts self.captureFail(); } else { // No attempts text to update (removed attempts UI) // Decrease player's cubix of used type by 1 on every failed catch attempt var selectedType = cubixTypes[0]; for (var i = 0; i < cubixTypes.length; i++) { if (cubixTypes[i].rarity === self.selectedCubix) { selectedType = cubixTypes[i]; break; } } if (selectedType && typeof selectedType.rarity === "string" && playerCubix && typeof playerCubix[selectedType.rarity] === "number" && playerCubix[selectedType.rarity] > 0) { playerCubix[selectedType.rarity]--; // Defensive: always sync playerCubix to storage and global scope after change if (typeof syncPlayerCubix === "function") { syncPlayerCubix(); } else { if (typeof storage !== "undefined") { if (typeof storage.set === "function") { storage.set("playerCubix", playerCubix); } else { storage.playerCubix = { basic: playerCubix.basic, uncommon: playerCubix.uncommon, rare: playerCubix.rare, legendary: playerCubix.legendary }; } } if (typeof window !== "undefined") { window.playerCubix = playerCubix; } } } // Update cubix button labels and cubix count display if (self.cubixButtons) { for (var i = 0; i < self.cubixButtons.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; // Update label if (self.cubixButtonLabels && self.cubixButtonLabels[i]) { self.cubixButtonLabels[i].setText(cubixTypes[i].name + " (" + (playerCubix[cubixTypes[i].rarity] || 0) + ")"); } if (playerCubix[cubixTypes[i].rarity] <= 0) self.cubixButtons[i].alpha = 0.2; } } // Update cubix count text below (by type with image) if (self.cubixCountTexts) { for (var i = 0; i < self.cubixCountTexts.length; i++) { if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue; var rarity = cubixTypes[i].rarity; if (self.cubixCountTexts[i]) { self.cubixCountTexts[i].setText("x" + (playerCubix[rarity] || 0)); } } } } }; // 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; // --- Cubixion collection and gem system --- if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {}; var cid = self.creature.creatureId; if (!playerCubixionCollection[cid]) { playerCubixionCollection[cid] = { count: 0, gems: 0 }; } playerCubixionCollection[cid].count++; playerCubixionCollection[cid].gems++; // 1 gem per catch // 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 from mapCreatures/mapCreatureTiles/mapCreatureLabels if present if (typeof mapCreatureTiles !== "undefined" && typeof mapCreatures !== "undefined" && typeof mapCreatureLabels !== "undefined") { for (var i = mapCreatureTiles.length - 1; i >= 0; i--) { if (mapCreatureTiles[i] === tile) { // Defensive: Only remove label if it exists and index is valid if (Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && mapCreatureLabels[i]) { tile.removeChild(mapCreatureLabels[i]); mapCreatureLabels.splice(i, 1); } // Defensive: Only remove from mapCreatures and mapCreatureTiles if index is valid if (Array.isArray(mapCreatures) && i < mapCreatures.length) { mapCreatures.splice(i, 1); } if (Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length) { mapCreatureTiles.splice(i, 1); } break; } } } // Remove encounterIndicator if present (yellow circle) } } // 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 () { // --- Refresh collection menu if open --- if (typeof collection !== "undefined" && collection.visible && typeof collection.show === "function") { collection.show(); } 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 // 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, using unique tile for each biome var biomeTileAssetMap = { grass: ['grassTile', 'grassTile2'], water: ['waterTile', 'waterTile2'], desert: ['desertTile', 'desertTile2'], forest: ['forestTile', 'forestTile2'], mountain: ['mountainTile', 'mountainTile2'], street: ['streetTile', 'streetTile2'], urban: ['urbanTile', 'urbanTile2'] }; // Special handling for out-of-world tiles removed. All tiles use biome tile assets. var assetList = biomeTileAssetMap[self.biomeType] || [self.biomeType + 'Tile']; // Randomly pick one of the two tile assets for this biome var assetId = assetList[Math.floor(Math.random() * assetList.length)]; // Use a single tile for the biome, with the new TILE_SIZE (already 3x bigger) self.tileImage = self.attachAsset(assetId, { anchorX: 0, anchorY: 0, width: TILE_SIZE, height: TILE_SIZE, x: 0, y: 0 }); self.tileImage.alpha = 0.9; // Set the logical tile size to TILE_SIZE for correct movement and centering self.tileSize = TILE_SIZE; // 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; } 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 = 1; // Match the reduced player vision radius for less lag 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); // Store reference for orientation logic self.playerGraphics = playerGraphics; // Always reset rotation and scale.x on init self.playerGraphics.rotation = 0; self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); // 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; // --- Determine facing direction and update sprite orientation --- // We'll use self.facing: 'left', 'right', 'up', 'down' if (typeof self.facing === "undefined") self.facing = "down"; if (typeof self.playerGraphics === "undefined") self.playerGraphics = self.children[0]; // Assume first child is player sprite var dx = newGridX - self.gridX; var dy = newGridY - self.gridY; if (Math.abs(dx) > Math.abs(dy)) { // Horizontal move if (dx > 0) { self.facing = "right"; // Swap to 'player' asset if not already if (self.playerGraphics && self.playerGraphics.assetId !== 'player') { self.removeChild(self.playerGraphics); self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } if (self.playerGraphics) { self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); // Face right (default) } } else if (dx < 0) { self.facing = "left"; // Swap to 'player' asset if not already if (self.playerGraphics && self.playerGraphics.assetId !== 'player') { self.removeChild(self.playerGraphics); self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } if (self.playerGraphics) { self.playerGraphics.scale.x = -Math.abs(self.playerGraphics.scale.x); // Flip horizontally to face left } } } else if (Math.abs(dy) > 0) { // Vertical move if (dy < 0) { self.facing = "up"; // Swap to 'player_up' asset if not already if (self.playerGraphics && self.playerGraphics.assetId !== 'player_up') { // Remove old graphic self.removeChild(self.playerGraphics); // Attach new up-facing asset self.playerGraphics = self.attachAsset('player_up', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } if (self.playerGraphics) { self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); // Face right (default) self.playerGraphics.rotation = Math.PI; // Show back (rotate 180deg) } } else if (dy > 0) { self.facing = "down"; // Swap to 'player' asset if not already if (self.playerGraphics && self.playerGraphics.assetId !== 'player') { self.removeChild(self.playerGraphics); self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } if (self.playerGraphics) { self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); // Face right (default) self.playerGraphics.rotation = 0; // Face forward } } } // If not moving vertically, always reset rotation if (Math.abs(dx) > 0 && Math.abs(dy) === 0 && self.playerGraphics) { self.playerGraphics.rotation = 0; } // 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 // Only animate player if actually on a water tile (not on edge) if (targetTile && targetTile.biomeType === "water") { // No shaking or swimming animation when entering water self._inWaterSwim = false; } else { // If not in water, stop swimming animation if it was running if (self._inWaterSwim) { self._inWaterSwim = false; if (typeof tween !== "undefined") { tween.stop(self, { y: true }); } } } } // 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 } // Always reset player sprite orientation if not moving if (self.facing === "up") { if (!self.playerGraphics || self.playerGraphics.assetId !== 'player_up') { if (self.playerGraphics) self.removeChild(self.playerGraphics); self.playerGraphics = self.attachAsset('player_up', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); self.playerGraphics.rotation = Math.PI; } else if (self.facing === "left") { if (!self.playerGraphics || self.playerGraphics.assetId !== 'player') { if (self.playerGraphics) self.removeChild(self.playerGraphics); self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } self.playerGraphics.scale.x = -Math.abs(self.playerGraphics.scale.x); self.playerGraphics.rotation = 0; } else if (self.facing === "right") { if (!self.playerGraphics || self.playerGraphics.assetId !== 'player') { if (self.playerGraphics) self.removeChild(self.playerGraphics); self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); self.playerGraphics.rotation = 0; } else { if (!self.playerGraphics || self.playerGraphics.assetId !== 'player') { if (self.playerGraphics) self.removeChild(self.playerGraphics); self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5, y: -44 }); self.playerGraphics.scale.set(0.34, 0.34); } self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); self.playerGraphics.rotation = 0; } // Hide player sprite if on dark (black) block if (typeof gameMap !== "undefined") { var tile = gameMap.getTileAt(self.gridX, self.gridY); // Do not hide player, but do not show dark tiles 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'); } // --- Rotate player if next to world border --- if (typeof self.rotation !== "undefined") { self.rotation = 0; } var nearBorder = false; var borderDir = 0; // 0=none, 1=left, 2=right, 3=top, 4=bottom if (self.gridX <= 0.5) { nearBorder = true; borderDir = 1; } else if (self.gridX >= MAP_WIDTH - 1) { nearBorder = true; borderDir = 2; } else if (self.gridY <= 0.5) { nearBorder = true; borderDir = 3; } else if (self.gridY >= MAP_HEIGHT - 1) { nearBorder = true; borderDir = 4; } if (nearBorder) { // Rotate player to indicate border direction if (borderDir === 1) self.rotation = Math.PI / 2; // Left: face down else if (borderDir === 2) self.rotation = -Math.PI / 2; // Right: face up // Remove upside-down rotation at top border (no Math.PI) else if (borderDir === 3) self.rotation = 0; // Top: normal (no upside down) else if (borderDir === 4) self.rotation = 0; // Bottom: normal } else { self.rotation = 0; } // 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 = 1; // Reduced from 2 to 1 for even fewer visible tiles and less lag 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) { // Hide dark (out-of-world) tiles so black area is never shown if (tile.biomeType === "dark") { tile.visible = false; continue; } 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 // 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) { // --- 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"; } // Try to find the correct image asset for the cubixion (creature) type var creatureImageAsset = null; if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) { // Find a matching creature in the database for this type for (var ci = 0; ci < Creature.prototype.creatureList.length; ci++) { var c = Creature.prototype.creatureList[ci]; if (c.type === creatureType) { // Prefer image asset if available if (c.image && LK.assets && LK.assets[c.image]) { creatureImageAsset = c.image; } // If there is a specific image asset for this type+name, use it if (LK.assets && LK.assets["creature_" + c.id + "_" + c.name + "_" + c.type]) { creatureImageAsset = "creature_" + c.id + "_" + c.name + "_" + c.type; } break; } } } // Fallback to typeCreature if no image found if (!creatureImageAsset) { // Try to use a generic image for this type if available if (LK.assets && LK.assets[creatureType + "Cubixion"]) { creatureImageAsset = creatureType + "Cubixion"; } else { // As a last fallback, use a generic ellipse creatureImageAsset = "grassCubixion"; } } // Show the cubixion visually using the correct image asset var creatureVisual = LK.getAsset(creatureImageAsset, { 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) { // 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 // 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 ****/ // Example: use desertTile as legendary cubix // Example: use Voltix as rare cubix // Example: use Boulder as uncommon cubix // Example: use Sproutle as basic cubix // Cubix (pokeball) image assets // Out-of-world tile asset (default color, can be changed in MapTile) // JSON helper functions for parsing and stringifying // Game dimensions and settings // Tween for animations // Storage for saving game progress // Player character // Map tiles // Creatures by elemental type // UI elements // Encounter indicator // Sounds // Unique image for urban biome // Unique biome tile assets for each biome (example: add one more for each) // Creature type base shapes (for fallback and type-based visuals) // --- Creature image assets for all 100 unique creatures --- // Example: LK.init.image('creature_0', {width:120, height:120, id:'<image_id>'}) // For demonstration, we use placeholder image ids. Replace with real ids as needed. // The following assets are initialized with the creature's name and element in the asset id for clarity function _typeof5(o) { "@babel/helpers - typeof"; return _typeof5 = "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; }, _typeof5(o); } function _typeof4(o) { "@babel/helpers - typeof"; return _typeof4 = "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; }, _typeof4(o); } function _typeof3(o) { "@babel/helpers - typeof"; return _typeof3 = "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; }, _typeof3(o); } function _typeof2(o) { "@babel/helpers - typeof"; return _typeof2 = "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; }, _typeof2(o); } 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 * 3; // Make each tile 3 times bigger than before 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 // Remove special case for (0,0) tile, all tiles are now the same size (TILE_SIZE * 3) for (var y = 0; y < MAP_HEIGHT; y++) { var _loop = function _loop() { t = gameMap.getTileAt(x, y); if (t) { t.tileSize = TILE_SIZE; } }, t, originalY; for (var x = 0; x < MAP_WIDTH; x++) { _loop(); } } // Spawn player at a random non-water, non-dark tile, and ensure nothing else spawns at the same place at game start function findRandomValidPlayerStart(gameMap) { var validTiles = []; for (var y = 0; y < MAP_HEIGHT; y++) { for (var x = 0; x < MAP_WIDTH; x++) { var biome = gameMap.tiles && gameMap.tiles[y] && gameMap.tiles[y][x]; if (biome && biome !== "water" && biome !== "dark") { var tileObj = gameMap.getTileAt(x, y); // Defensive: skip if tileObj is missing or already has a creature/cubix // Prevent spawning on player's current tile if (tileObj && !tileObj.hasCreature && !tileObj._cubixVisual && !tileObj._creatureVisual && !(tileObj.gridX === Math.round(playerStartX) && tileObj.gridY === Math.round(playerStartY))) { validTiles.push({ x: x, y: y }); } } } } // Pick a random valid tile, fallback to 0,0 if none found if (validTiles.length > 0) { return validTiles[Math.floor(Math.random() * validTiles.length)]; } return { x: 0, y: 0 }; } var validStart = findRandomValidPlayerStart(gameMap); var playerStartX = validStart.x; var playerStartY = validStart.y; // Ensure start tile is not water or dark for consistency if (gameMap.tiles && gameMap.tiles[playerStartY] && gameMap.tiles[playerStartY][playerStartX]) { 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 { 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(); centerViewOnPlayer(); centerViewOnPlayer(); // Prevent spawning cubix/cubixion on the same tile as the player at game start var playerStartTile = gameMap.getTileAt(playerStartX, playerStartY); if (playerStartTile) { // Remove cubix visual if present if (playerStartTile._cubixVisual) { playerStartTile.removeChild(playerStartTile._cubixVisual); playerStartTile._cubixVisual = null; if (typeof mapCubixTiles !== "undefined" && typeof mapCubix !== "undefined") { for (var i = mapCubixTiles.length - 1; i >= 0; i--) { if (mapCubixTiles[i] === playerStartTile) { mapCubix.splice(i, 1); mapCubixTiles.splice(i, 1); break; } } } } // Remove cubixion (creature) visual if present if (playerStartTile._creatureVisual) { playerStartTile.removeChild(playerStartTile._creatureVisual); playerStartTile._creatureVisual = null; playerStartTile.hasCreature = false; if (typeof mapCreatureTiles !== "undefined" && typeof mapCreatures !== "undefined" && typeof mapCreatureLabels !== "undefined") { for (var i = mapCreatureTiles.length - 1; i >= 0; i--) { if (mapCreatureTiles[i] === playerStartTile) { // Defensive: Only remove label if it exists and index is valid if (Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && mapCreatureLabels[i]) { playerStartTile.removeChild(mapCreatureLabels[i]); mapCreatureLabels.splice(i, 1); } // Defensive: Only remove from mapCreatures and mapCreatureTiles if index is valid if (Array.isArray(mapCreatures) && i < mapCreatures.length) { mapCreatures.splice(i, 1); } if (Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length) { mapCreatureTiles.splice(i, 1); } break; } } } } } // 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, scaleX: 1.5, scaleY: 1.5 }); collectionButton.interactive = true; // Collection button icon var collectionText = new Text2('COL', { size: 48, 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; // Cubix inventory UI // Removed cubixInventoryText from main UI (Cubix inventory is shown in collection menu) // Define playerCubix in global scope if not already defined // Reset playerCubix and Cubixion collection at game start var playerCubix = { basic: 10, uncommon: 0, rare: 0, legendary: 0 }; // Remove all Cubix except 10 basic Cubix from storage and window at game start if (typeof storage !== "undefined") { if (typeof storage.set === "function") { storage.set("playerCubix", { basic: 10, uncommon: 0, rare: 0, legendary: 0 }); storage.set("playerCubixionCollection", {}); // Reset Cubixion collection } else { storage.playerCubix = { basic: 10, uncommon: 0, rare: 0, legendary: 0 }; storage.playerCubixionCollection = {}; } } if (typeof window !== "undefined") { window.playerCubix = { basic: 10, uncommon: 0, rare: 0, legendary: 0 }; window.playerCubixionCollection = {}; } // Defensive: always sync playerCubix to storage and global scope after every update function syncPlayerCubix() { if (typeof storage !== "undefined") { if (typeof storage.set === "function") { storage.set("playerCubix", playerCubix); } else { // fallback for legacy: assign a shallow copy to avoid circular reference storage.playerCubix = { basic: playerCubix.basic, uncommon: playerCubix.uncommon, rare: playerCubix.rare, legendary: playerCubix.legendary }; } } if (typeof window !== "undefined") { window.playerCubix = playerCubix; } } // Cubixion collection and gem system var playerCubixionCollection = {}; // {creatureId: {count: 0, gems: 0}} // Reset Cubixion collection at game start if (typeof storage !== "undefined") { if (typeof storage.set === "function") { storage.set("playerCubixionCollection", {}); } else { storage.playerCubixionCollection = {}; } } if (typeof window !== "undefined") { window.playerCubixionCollection = {}; } // Removed updateCubixInventoryText and its calls (Cubix inventory now shown in collection menu) // 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.25 tile up per press var newGridY = player.gridY - 0.25; if (newGridY < 0) newGridY = 0; // Prevent moving into black area (dark biome) var targetTile = gameMap.getTileAt(Math.round(player.gridX), Math.round(newGridY)); if (targetTile && targetTile.biomeType !== "dark") { player.moveToGrid(player.gridX, newGridY); } } }; dpad.downButton.down = function () { if (!player.moving && !encounterActive && !collection.visible) { // Move exactly 0.25 tile down per press var newGridY = player.gridY + 0.25; if (newGridY > MAP_HEIGHT - 1) newGridY = MAP_HEIGHT - 1; // Prevent moving into black area (dark biome) var targetTile = gameMap.getTileAt(Math.round(player.gridX), Math.round(newGridY)); if (targetTile && targetTile.biomeType !== "dark") { player.moveToGrid(player.gridX, newGridY); } } }; dpad.leftButton.down = function () { if (!player.moving && !encounterActive && !collection.visible) { // Move exactly 0.25 tile left per press var newGridX = player.gridX - 0.25; if (newGridX < 0) newGridX = 0; // Prevent moving into black area (dark biome) var targetTile = gameMap.getTileAt(Math.round(newGridX), Math.round(player.gridY)); if (targetTile && targetTile.biomeType !== "dark") { player.moveToGrid(newGridX, player.gridY); } } }; dpad.rightButton.down = function () { if (!player.moving && !encounterActive && !collection.visible) { // Move exactly 0.25 tile right per press var newGridX = player.gridX + 0.25; if (newGridX > MAP_WIDTH - 1) newGridX = MAP_WIDTH - 1; // Prevent moving into black area (dark biome) var targetTile = gameMap.getTileAt(Math.round(newGridX), Math.round(player.gridY)); if (targetTile && targetTile.biomeType !== "dark") { 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) { // Hide collection button and text when menu is open collectionButton.visible = false; collectionText.visible = false; // Always reload the bag menu for all creatures if (typeof collection.show === "function") { collection.show(); } // Show exit button in collection menu if (!collection.exitButton) { collection.exitButton = collection.attachAsset('dpadButton', { anchorX: 0.5, anchorY: 0.5, x: 1900, y: 150 }); collection.exitButton.interactive = true; var exitText = new Text2('EXIT', { size: 48, fill: 0xFFFFFF }); exitText.anchor.set(0.5, 0.5); exitText.x = 1900; exitText.y = 150; collection.addChild(exitText); collection.exitButton.exitText = exitText; collection.exitButton.down = function () { collection.hide(); // Show player and collection button again player.visible = true; collectionButton.visible = true; collectionText.visible = true; // Hide exit button and text if (collection.exitButton) { collection.exitButton.visible = false; if (collection.exitButton.exitText) { collection.exitButton.exitText.visible = false; } } }; } // Show exit button and text if (collection.exitButton) { collection.exitButton.visible = true; if (collection.exitButton.exitText) { collection.exitButton.exitText.visible = true; } } } }; // Handle collection close button collection.closeButton.down = function () { collection.hide(); // Show player and collection button again player.visible = true; collectionButton.visible = true; collectionText.visible = true; // Hide exit button and text if (collection.exitButton) { collection.exitButton.visible = false; if (collection.exitButton.exitText) { collection.exitButton.exitText.visible = false; } } }; // Track last player position to detect changes var lastPlayerGridX = player.gridX; var lastPlayerGridY = player.gridY; // --- Cubix system --- // Cubix rarities and their catch chances if (typeof cubixTypes === "undefined") { var cubixTypes = [{ id: 'cubix_basic', name: 'Basic Cubix', color: 0xffffff, rarity: 'basic', catchBonus: 0, chance: 0.5 }, { id: 'cubix_uncommon', name: 'Uncommon Cubix', color: 0x00ff00, rarity: 'uncommon', catchBonus: 0.15, chance: 0.65 }, { id: 'cubix_rare', name: 'Rare Cubix', color: 0x0000ff, rarity: 'rare', catchBonus: 0.3, chance: 0.8 }, { id: 'cubix_legendary', name: 'Legendary Cubix', color: 0xffd700, rarity: 'legendary', catchBonus: 0.5, chance: 0.95 }]; } // 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) { // Defensive: check for undefined or not arrays before accessing index 0 if (typeof mapCubixTiles === "undefined" || typeof mapCubix === "undefined" || !Array.isArray(mapCubixTiles) || !Array.isArray(mapCubix) || mapCubixTiles.length === 0 || mapCubix.length === 0 || typeof mapCubixTiles[0] === "undefined" || typeof mapCubix[0] === "undefined") { return; } // Defensive: do not access index 0 if arrays are empty or undefined return; } lastUpdateTime = now; // Player's cubix inventory (legacy pokeball variable replaced) if (typeof playerCubix === "undefined") { playerCubix = { basic: 3, uncommon: 1, rare: 0, legendary: 0 }; } // Defensive: always sync playerCubix to storage and global scope at start of update syncPlayerCubix(); // Cubix map objects (legacy pokeball variable replaced) if (typeof mapCubix === "undefined") { mapCubix = []; mapCubixTiles = []; mapCubixMax = 12; // Max cubix on map at once (reduced for performance) mapCubixSpawnCooldown = 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 = 12; // Max creatures on map at once (reduced for performance) mapCreatureSpawnCooldown = 0; } // --- Add despawn timer for cubixion (creature) spawns: despawn after 5 minutes (300 seconds) --- var nowTime = Date.now(); for (var i = mapCreatures.length - 1; i >= 0; i--) { // Defensive: skip if arrays are out of sync or index is out of bounds if (!mapCreatureTiles || !mapCreatures || i >= mapCreatureTiles.length || i >= mapCreatures.length) { continue; } var tile = mapCreatureTiles[i]; // Defensive: skip if tile is undefined if (typeof tile === "undefined" || tile === null) { continue; } // Prevent despawn if player is currently on this tile if (player && Math.round(player.gridX) === tile.gridX && Math.round(player.gridY) === tile.gridY) { // Do not run despawn logic for this tile while player is on it continue; } // Initialize spawn timestamp if not set if (tile && typeof tile._creatureSpawnedAt === "undefined") { tile._creatureSpawnedAt = nowTime; } // Prevent despawn until it has spawned for 5 minutes (300000 ms) // (No despawn here, so skip this block entirely) // --- Keep original explored despawn logic --- // Initialize despawn timer if not set if (tile && typeof tile._creatureDespawnTimer === "undefined") { tile._creatureDespawnTimer = 0; } // If tile is explored, start despawn timer if (tile && tile.explored) { tile._creatureDespawnTimer++; // Only despawn if timer exceeds threshold (e.g. 90 frames ~1.5s) if (tile._creatureDespawnTimer > 90) { if (typeof mapCreatureLabels !== "undefined" && Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && typeof mapCreatureLabels[i] !== "undefined" && mapCreatureLabels[i]) { if (tile) tile.removeChild(mapCreatureLabels[i]); mapCreatureLabels.splice(i, 1); } if (tile && tile.hasCreature) { tile.hasCreature = false; } // Defensive: Only remove from mapCreatures and mapCreatureTiles if index is valid if (Array.isArray(mapCreatures) && i < mapCreatures.length && typeof mapCreatures[i] !== "undefined") { mapCreatures.splice(i, 1); } if (Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length && typeof mapCreatureTiles[i] !== "undefined") { mapCreatureTiles.splice(i, 1); } continue; } } else if (!tile || Math.random() < 0.002) { // Lower random despawn chance // Defensive: Only remove label if arrays and index are valid if (typeof mapCreatureLabels !== "undefined" && Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && mapCreatureLabels[i] !== undefined) { if (tile) tile.removeChild(mapCreatureLabels[i]); mapCreatureLabels.splice(i, 1); } if (tile && tile.hasCreature) { tile.hasCreature = false; } // Defensive: Only remove from mapCreatures and mapCreatureTiles if arrays and index are valid if (typeof mapCreatures !== "undefined" && Array.isArray(mapCreatures) && i < mapCreatures.length && mapCreatures[i] !== undefined) { mapCreatures.splice(i, 1); } if (typeof mapCreatureTiles !== "undefined" && Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length && mapCreatureTiles[i] !== undefined) { mapCreatureTiles.splice(i, 1); } continue; } else if (tile) { // Reset timer if not explored tile._creatureDespawnTimer = 0; } } // --- Cubix no longer despawn after 5 minutes (despawn logic removed) --- // --- Guarantee at least one cubixion (creature) is always visible and collectible on the player's screen (vision area) --- (function guaranteeCubixionOnScreen() { // Get player's vision area var visionRadius = 2; var px = Math.round(player.gridX); var py = Math.round(player.gridY); var foundOnScreen = false; // Check if any visible, collectible cubixion is in vision for (var y = py - visionRadius; y <= py + visionRadius; y++) { for (var x = px - visionRadius; x <= px + visionRadius; x++) { var tile = gameMap.getTileAt(x, y); if (tile && tile.hasCreature && tile.visible && !tile._creatureCaught) { foundOnScreen = true; break; } } if (foundOnScreen) break; } // If not, spawn one in a random visible, collectible tile in vision if (!foundOnScreen) { var candidates = []; for (var y = py - visionRadius; y <= py + visionRadius; y++) { for (var x = px - visionRadius; x <= px + visionRadius; x++) { var tile = gameMap.getTileAt(x, y); // Prevent spawning on player's current tile if (tile && tile.visible && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual && !tile._creatureCaught && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) { candidates.push(tile); } } } if (candidates.length > 0) { var foundTile = candidates[Math.floor(Math.random() * candidates.length)]; var cType = new Creature().getRandomType(foundTile.biomeType); var rarityRand = Math.random(); var rarity = "common"; if (rarityRand < 0.60) rarity = "common";else if (rarityRand < 0.85) rarity = "uncommon";else if (rarityRand < 0.96) rarity = "rare";else if (rarityRand < 0.995) rarity = "epic";else rarity = "legendary"; foundTile.hasCreature = true; mapCreatures.push({ type: cType, rarity: rarity }); mapCreatureTiles.push(foundTile); mapCreatureLabels.push(null); // Also ensure the visual is created for the cubixion if (!foundTile._creatureVisual) { var creatureImageAsset = null; if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) { for (var ci = 0; ci < Creature.prototype.creatureList.length; ci++) { var c = Creature.prototype.creatureList[ci]; if (c.type === cType) { if (c.image && LK.assets && LK.assets[c.image]) { creatureImageAsset = c.image; } if (LK.assets && LK.assets["creature_" + c.id + "_" + c.name + "_" + c.type]) { creatureImageAsset = "creature_" + c.id + "_" + c.name + "_" + c.type; } break; } } } // Fallback to typeCubixion if no image found if (!creatureImageAsset) { if (LK.assets && LK.assets[cType + "Cubixion"]) { creatureImageAsset = cType + "Cubixion"; } else { creatureImageAsset = "grassCubixion"; } } var creatureVisual = LK.getAsset(creatureImageAsset, { 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; foundTile.addChild(creatureVisual); foundTile._creatureVisual = creatureVisual; } } } })(); // --- Guarantee at least one cubix is always visible and collectible on the player's screen (vision area) --- (function guaranteeCubixOnScreen() { var visionRadius = 2; var px = Math.round(player.gridX); var py = Math.round(player.gridY); var foundOnScreen = false; // Check if any visible, collectible cubix is in vision for (var y = py - visionRadius; y <= py + visionRadius; y++) { for (var x = px - visionRadius; x <= px + visionRadius; x++) { var tile = gameMap.getTileAt(x, y); if (tile && tile._cubixVisual && tile.visible) { foundOnScreen = true; break; } } if (foundOnScreen) break; } // If not, spawn one in a random visible, collectible tile in vision if (!foundOnScreen) { var candidates = []; for (var y = py - visionRadius; y <= py + visionRadius; y++) { for (var x = px - visionRadius; x <= px + visionRadius; x++) { var tile = gameMap.getTileAt(x, y); // Prevent spawning on player's current tile if (tile && tile.visible && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) { candidates.push(tile); } } } if (candidates.length > 0) { var foundCubixTile = candidates[Math.floor(Math.random() * candidates.length)]; var rand = Math.random(); var cType = cubixTypes[0]; if (rand > 0.7) cType = cubixTypes[3]; // Legendary: 30% else if (rand > 0.45) cType = cubixTypes[2]; // Rare: 25% else if (rand > 0.2) cType = cubixTypes[1]; // Uncommon: 25% // Basic: 20% var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); cubixVisual.scale.set(0.5, 0.5); foundCubixTile.addChild(cubixVisual); foundCubixTile._cubixVisual = cubixVisual; cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"); if (cType && typeof cType.rarity !== "undefined") { mapCubix.push(cType.rarity); } else { mapCubix.push("basic"); } mapCubixTiles.push(foundCubixTile); } } })(); // Spawn new creatures sometimes, up to max if (mapCreatures.length < mapCreatureMax && mapCreatureSpawnCooldown <= 0) { // Try to spawn a new creature var tries = 0; // --- New: spawn near player if standing still --- var spawnNearPlayer = false; if (typeof lastPlayerGridX !== "undefined" && typeof lastPlayerGridY !== "undefined") { if (player.gridX === lastPlayerGridX && player.gridY === lastPlayerGridY) { // 30% chance to try to spawn near player if standing still if (Math.random() < 0.3) spawnNearPlayer = true; } } if (spawnNearPlayer) { // Try to spawn in a random adjacent tile (including diagonals) var px = Math.round(player.gridX); var py = Math.round(player.gridY); var adjacents = []; for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nx = px + dx; var ny = py + dy; if (nx >= 0 && nx < MAP_WIDTH && ny >= 0 && ny < MAP_HEIGHT) { var t = gameMap.getTileAt(nx, ny); if (t && !t.explored && !t.hasCreature && t.biomeType !== "dark") { adjacents.push(t); } } } } if (adjacents.length > 0) { var tile = adjacents[Math.floor(Math.random() * adjacents.length)]; // Mark tile as having a creature tile.hasCreature = true; var cType = new Creature().getRandomType(tile.biomeType); mapCreatures.push(cType); mapCreatureTiles.push(tile); mapCreatureLabels.push(null); // Only one spawn near player per update tries = 15; } } // Only spawn cubixion (creature) in player's nearest area (vision radius) var px = Math.round(player.gridX); var py = Math.round(player.gridY); var visionRadius = 2; var spawnCandidates = []; for (var y = py - visionRadius; y <= py + visionRadius; y++) { for (var x = px - visionRadius; x <= px + visionRadius; x++) { var tile = gameMap.getTileAt(x, y); var crossable = tile && tile.biomeType !== "dark"; // Prevent spawning on player's current tile if (tile && !tile.hasCreature && crossable && !tile._cubixVisual && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) { spawnCandidates.push(tile); } } } var tries = 0; while (tries < 200 && mapCreatures.length < mapCreatureMax && spawnCandidates.length > 0) { // Pick a random candidate tile near player var tile = spawnCandidates[Math.floor(Math.random() * spawnCandidates.length)]; // Mark tile as having a creature tile.hasCreature = true; // Pick a random type for this biome var cType = new Creature().getRandomType(tile.biomeType); // Assign rarity with much higher chance for rare/epic/legendary var rarityRand = Math.random(); var rarity = "common"; if (rarityRand < 0.45) rarity = "common";else if (rarityRand < 0.7) rarity = "uncommon";else if (rarityRand < 0.88) rarity = "rare";else if (rarityRand < 0.97) rarity = "epic";else rarity = "legendary"; mapCreatures.push({ type: cType, rarity: rarity }); mapCreatureTiles.push(tile); mapCreatureLabels.push(null); // Remove this tile from candidates so we don't double-spawn var idx = spawnCandidates.indexOf(tile); if (idx !== -1) spawnCandidates.splice(idx, 1); tries++; } // Reduce cooldown for much faster respawn (was 180 + Math.floor(Math.random() * 120)) mapCreatureSpawnCooldown = 20 + Math.floor(Math.random() * 10); // 0.33-0.5s, much faster } else { mapCreatureSpawnCooldown--; } // --- Cubix spawn logic with max 30, image, and no despawn --- if (typeof mapCubix === "undefined") { mapCubix = []; mapCubixTiles = []; mapCubixMax = 30; // Max cubix on map at once mapCubixSpawnCooldown = 0; } // Remove despawn logic: cubix will not despawn anymore // --- Guarantee at least one cubix is always visible on the map --- // If there are no cubix spawns, guarantee at least one lowest rarity cubix is spawned if (mapCubix.length === 0) { // Find a random valid tile for lowest rarity cubix var foundTile = null; for (var attempt = 0; attempt < 200; attempt++) { var gx = Math.floor(Math.random() * MAP_WIDTH); var gy = Math.floor(Math.random() * MAP_HEIGHT); var tile = gameMap.getTileAt(gx, gy); var crossable = tile && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual; // Prevent spawning on player's current tile if (tile && crossable && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) { foundTile = tile; break; } } if (foundTile) { // Always spawn a random cubix (any rarity) if map is empty var rand = Math.random(); var cType = cubixTypes[0]; if (rand > 0.7) cType = cubixTypes[3]; // Legendary: 30% else if (rand > 0.45) cType = cubixTypes[2]; // Rare: 25% else if (rand > 0.2) cType = cubixTypes[1]; // Uncommon: 25% // Basic: 20% var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); cubixVisual.scale.set(0.5, 0.5); foundTile.addChild(cubixVisual); foundTile._cubixVisual = cubixVisual; cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"); if (cType && typeof cType.rarity !== "undefined") { mapCubix.push(cType.rarity); } else { mapCubix.push("basic"); } mapCubixTiles.push(foundTile); } } // Continue normal spawn logic, but never exceed max 30 if (mapCubix.length < mapCubixMax && mapCubixSpawnCooldown <= 0) { var tries = 0; // --- New: spawn cubix near player if standing still --- var spawnCubixNearPlayer = false; if (typeof lastPlayerGridX !== "undefined" && typeof lastPlayerGridY !== "undefined") { if (player.gridX === lastPlayerGridX && player.gridY === lastPlayerGridY) { // 30% chance to try to spawn cubix near player if standing still if (Math.random() < 0.3) spawnCubixNearPlayer = true; } } if (spawnCubixNearPlayer) { // Try to spawn in a random adjacent tile (including diagonals) var px = Math.round(player.gridX); var py = Math.round(player.gridY); var adjacents = []; for (var dx = -1; dx <= 1; dx++) { for (var dy = -1; dy <= 1; dy++) { if (dx === 0 && dy === 0) continue; var nx = px + dx; var ny = py + dy; if (nx >= 0 && nx < MAP_WIDTH && ny >= 0 && ny < MAP_HEIGHT) { var t = gameMap.getTileAt(nx, ny); var crossable = t && t.biomeType !== "dark" && !t.hasCreature && !t._cubixVisual; if (t && !t.explored && crossable) { adjacents.push(t); } } } } if (adjacents.length > 0) { var tile = adjacents[Math.floor(Math.random() * adjacents.length)]; // Pick a random cubix type (weighted: much more rare/legendary spawns) var rand = Math.random(); var cType = cubixTypes[0]; if (rand > 0.7) cType = cubixTypes[3]; // Legendary: 30% else if (rand > 0.45) cType = cubixTypes[2]; // Rare: 25% else if (rand > 0.2) cType = cubixTypes[1]; // Uncommon: 25% // Basic: 20% // Show cubix on tile with correct rarity image var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); cubixVisual.scale.set(0.5, 0.5); tile.addChild(cubixVisual); tile._cubixVisual = cubixVisual; cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"); if (cType && typeof cType.rarity !== "undefined") { mapCubix.push(cType.rarity); } else { mapCubix.push("basic"); } mapCubixTiles.push(tile); // Only one spawn near player per update tries = 60; } } // Increase number of tries and increase spawn rate for more cubix spawns // Make cubix spawn rate 10x higher than cubixion by increasing tries 10x // Spawn cubix in a much wider area and for all types (not just adjacent or limited tiles) // Check if player has no cubix of any type var playerHasNoCubix = true; if (typeof playerCubix !== "undefined") { for (var i = 0; i < cubixTypes.length; i++) { if ((playerCubix[cubixTypes[i].rarity] || 0) > 0) { playerHasNoCubix = false; break; } } } while (tries < 400 && mapCubix.length < mapCubixMax) { 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 dark, not already with a cubix or creature var crossable = tile && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual; // Prevent spawning on player's current tile if (tile && crossable && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) { // If player has no cubix, increase spawn chance for basic cubix if (playerHasNoCubix) { // 80% chance to spawn a basic cubix, 20% chance to spawn other types if (Math.random() < 0.8) { var cType = cubixTypes[0]; // basic } else { var cType = cubixTypes[Math.floor(Math.random() * cubixTypes.length)]; } // Always spawn if player has no cubix (skip random chance) var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); cubixVisual.scale.set(0.5, 0.5); tile.addChild(cubixVisual); tile._cubixVisual = cubixVisual; cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"); if (cType && typeof cType.rarity !== "undefined") { mapCubix.push(cType.rarity); } else { mapCubix.push("basic"); } mapCubixTiles.push(tile); } else { // Greatly increase spawn chance per tile (much higher for all types/rarities) if (Math.random() < 0.98) { // Instead of only using weighted cubixTypes, allow all types to spawn // Pick a random cubix type from all available types var cType = cubixTypes[Math.floor(Math.random() * cubixTypes.length)]; // Show cubix on tile with correct rarity image var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), { anchorX: 0.5, anchorY: 0.5, x: TILE_SIZE / 2, y: TILE_SIZE / 2 }); cubixVisual.scale.set(0.5, 0.5); tile.addChild(cubixVisual); tile._cubixVisual = cubixVisual; cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"); if (cType && typeof cType.rarity !== "undefined") { mapCubix.push(cType.rarity); } else { mapCubix.push("basic"); } mapCubixTiles.push(tile); } } } tries++; } // Reduce cooldown for much faster respawn (was 120 + Math.floor(Math.random() * 120)) mapCubixSpawnCooldown = 10 + Math.floor(Math.random() * 10); // 0.16-0.33s, much faster } else { mapCubixSpawnCooldown--; } // --- End creature spawn/despawn logic --- // --- Cubix collection logic --- // Always allow collection if player is on the tile with cubix (any type/rarity) var playerTile = gameMap.getTileAt(player.gridX, player.gridY); if (playerTile && playerTile._cubixVisual) { var foundType = null; for (var i = 0; i < cubixTypes.length; i++) { if (playerTile._cubixVisual.assetId === 'cubix_' + cubixTypes[i].rarity) { foundType = cubixTypes[i]; break; } } if (foundType) { // Add to inventory (bag) for use in catching cubixion if (!playerCubix[foundType.rarity]) playerCubix[foundType.rarity] = 0; playerCubix[foundType.rarity]++; // Defensive: always sync playerCubix to storage and global scope after change syncPlayerCubix(); // Remove cubix from map (despawn) if (playerTile._cubixVisual && playerTile.children.indexOf(playerTile._cubixVisual) !== -1) { playerTile.removeChild(playerTile._cubixVisual); } playerTile._cubixVisual = null; // Remove from arrays for optimization if (typeof mapCubixTiles !== "undefined" && typeof mapCubix !== "undefined") { for (var i = mapCubixTiles.length - 1; i >= 0; i--) { // Defensive: check mapCubixTiles[i] is defined before comparing if (typeof mapCubixTiles[i] !== "undefined" && mapCubixTiles[i] === playerTile) { mapCubix.splice(i, 1); mapCubixTiles.splice(i, 1); break; } } } // Show a message var msg = new Text2('Collected ' + foundType.name.replace(' Cubix', 'ion') + '!', { 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); // --- Add to Cubixion collection menu if not already present --- if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {}; // Use a synthetic id for cubix type (e.g. rarity as id) var cubixionId = foundType.rarity; if (!playerCubixionCollection[cubixionId]) { playerCubixionCollection[cubixionId] = { count: 0, gems: 0 }; } playerCubixionCollection[cubixionId].count++; // Defensive: always sync to storage and window if (typeof storage !== "undefined" && typeof storage.set === "function") { storage.set("playerCubixionCollection", playerCubixionCollection); } else if (typeof storage !== "undefined") { try { storage.playerCubixionCollection = playerCubixionCollection; } catch (e) {} } if (typeof window !== "undefined") window.playerCubixionCollection = playerCubixionCollection; } } // 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 6th update for further optimization if (typeof minimapUpdateCounter === "undefined") minimapUpdateCounter = 0; minimapUpdateCounter++; if (minimapUpdateCounter % 6 === 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 so it doesn't respawn immediately currentTile.hasCreature = false; currentTile._creatureCaught = true; // Remove indicator from tile and all references for optimization if (typeof mapCreatureTiles !== "undefined" && typeof mapCreatures !== "undefined" && typeof mapCreatureLabels !== "undefined") { for (var k = mapCreatureTiles.length - 1; k >= 0; k--) { if (mapCreatureTiles[k] === currentTile) { // Remove label if present if (mapCreatureLabels[k]) { currentTile.removeChild(mapCreatureLabels[k]); mapCreatureLabels.splice(k, 1); } // Remove creature visual if present if (currentTile._creatureVisual) { currentTile.removeChild(currentTile._creatureVisual); currentTile._creatureVisual = null; } 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 and bag
self.scrollContainer = new Container();
self.addChild(self.scrollContainer);
self.scrollContainer.x = 50;
self.scrollContainer.y = 320; // Move down to make space for bag section
// Create scrollable Bag section container with mask
self.bagScrollContainer = new Container();
self.bagMaskContainer = new Container();
self.addChild(self.bagMaskContainer);
self.bagMaskContainer.x = 50;
self.bagMaskContainer.y = 200;
self.bagMaskContainer.addChild(self.bagScrollContainer);
// Create a mask for the scrollable area
var bagMask = LK.getAsset('grassTile', {
anchorX: 0,
anchorY: 0,
width: 2048 - 100,
height: 1000
});
bagMask.alpha = 0.0; // Invisible mask
self.bagMaskContainer.addChild(bagMask);
self.bagScrollContainer.mask = bagMask;
// Create a background for the bag area
var bagBackground = LK.getAsset('grassTile', {
anchorX: 0,
anchorY: 0,
width: 2048 - 100,
height: 1000
});
bagBackground.alpha = 0.3;
bagBackground.tint = 0x000088;
self.bagMaskContainer.addChild(bagBackground);
self.bagMaskContainer.setChildIndex(bagBackground, 0);
// Add Bag button to open Bag section
self.bagButton = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
// Move to right bottom
x: 2048 - 200,
y: 2732 - 200,
scaleX: 1.5,
scaleY: 1.5
});
self.bagButton.interactive = true;
self.bagBtnText = new Text2('BAG', {
size: 60,
fill: 0xFFD700
});
self.bagBtnText.anchor.set(0.5, 0.5);
// Move to right bottom
self.bagBtnText.x = 2048 - 200;
self.bagBtnText.y = 2732 - 200;
self.addChild(self.bagButton);
self.addChild(self.bagBtnText);
// Add bag section title
self.bagTitleText = new Text2('MY BAG', {
size: 70,
fill: 0xFFD700
});
self.bagTitleText.anchor.set(0.5, 0);
self.bagTitleText.x = 1024;
self.bagTitleText.y = 220;
self.addChild(self.bagTitleText);
self.bagTitleText.visible = true;
// Scrolling variables
self.bagScrollingActive = false;
self.bagScrollStartY = 0;
self.bagScrollStartPos = 0;
self.bagMaxScroll = 0;
// Add scroll indicators
self.scrollUpIndicator = new Text2('▲', {
size: 60,
fill: 0xFFFFFF
});
self.scrollUpIndicator.anchor.set(0.5, 0);
self.scrollUpIndicator.x = 1024;
self.scrollUpIndicator.y = 260;
self.scrollUpIndicator.alpha = 0.7;
self.addChild(self.scrollUpIndicator);
self.scrollDownIndicator = new Text2('▼', {
size: 60,
fill: 0xFFFFFF
});
self.scrollDownIndicator.anchor.set(0.5, 1);
self.scrollDownIndicator.x = 1024;
self.scrollDownIndicator.y = 1180;
self.scrollDownIndicator.alpha = 0.7;
self.addChild(self.scrollDownIndicator);
// Bag section is visible by default, but you can toggle it with the button if desired
self.bagButton.down = function () {
// Toggle bagMaskContainer visibility
self.bagMaskContainer.visible = !self.bagMaskContainer.visible;
self.bagTitleText.visible = self.bagMaskContainer.visible;
self.scrollUpIndicator.visible = self.bagMaskContainer.visible;
self.scrollDownIndicator.visible = self.bagMaskContainer.visible;
// Change title depending on bagContainer visibility
if (self.bagMaskContainer.visible && self.titleText) {
self.titleText.setText('THE BAG');
// Hide bag button when bag is visible
self.bagButton.visible = false;
if (self.bagBtnText) {
self.bagBtnText.visible = false;
}
// Create/show Cubixion menu button when in bag view
if (!self.cubixionMenuButton) {
self.cubixionMenuButton = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 200,
y: 2732 - 200,
scaleX: 1.5,
scaleY: 1.5
});
self.cubixionMenuButton.interactive = true;
self.cubixionMenuBtnText = new Text2('CUBIXION MENU', {
size: 60,
fill: 0xFFD700
});
self.cubixionMenuBtnText.anchor.set(0.5, 0.5);
self.cubixionMenuBtnText.x = 2048 - 200;
self.cubixionMenuBtnText.y = 2732 - 200;
self.addChild(self.cubixionMenuButton);
self.addChild(self.cubixionMenuBtnText);
self.cubixionMenuButton.down = function () {
// Hide bag view
self.bagMaskContainer.visible = false;
self.bagTitleText.visible = false;
self.scrollUpIndicator.visible = false;
self.scrollDownIndicator.visible = false;
// Hide Cubixion menu button
self.cubixionMenuButton.visible = false;
self.cubixionMenuBtnText.visible = false;
// Show bag button again
self.bagButton.visible = true;
if (self.bagBtnText) {
self.bagBtnText.visible = true;
}
// Change title back to collection menu
if (self.titleText) {
self.titleText.setText('CUBIXION MENU');
}
// Hide back button and collection button if they exist
if (self.backToCollectionButton) {
self.backToCollectionButton.visible = false;
if (self.backToCollectionButton.backBtnText) {
self.backToCollectionButton.backBtnText.visible = false;
}
}
if (self.collectionButton) {
self.collectionButton.visible = false;
if (self.collectionButton.colBtnText) {
self.collectionButton.colBtnText.visible = false;
}
}
};
}
self.cubixionMenuButton.visible = true;
self.cubixionMenuBtnText.visible = true;
} else if (self.titleText) {
self.titleText.setText('CUBIXION MENU');
// Show bag button when bag is not visible
self.bagButton.visible = true;
if (self.bagBtnText) {
self.bagBtnText.visible = true;
}
// Hide Cubixion menu button if it exists
if (self.cubixionMenuButton) {
self.cubixionMenuButton.visible = false;
self.cubixionMenuBtnText.visible = false;
}
}
// If showing bag, populate it with items
if (self.bagMaskContainer.visible) {
self.populateBagItems();
// Make sure back button is visible when bag is visible
if (self.backToCollectionButton) {
self.backToCollectionButton.visible = true;
if (self.backToCollectionButton.backBtnText) {
self.backToCollectionButton.backBtnText.visible = true;
}
}
}
};
// Function to populate bag with items
self.populateBagItems = function () {
// Remove previous cubixion images from bagScrollContainer
for (var i = self.bagScrollContainer.children.length - 1; i >= 0; i--) {
var child = self.bagScrollContainer.children[i];
if (child && child.isCubixionBagImage) {
self.bagScrollContainer.removeChild(child);
}
}
// Reset scroll position
self.bagScrollContainer.y = 0;
// Show a summary of how many cubix of each rarity the player has in the bag section, with their image on the left
var cubixRarities = ['basic', 'uncommon', 'rare', 'legendary'];
var cubixNames = {
basic: 'Basic Cubix',
uncommon: 'Uncommon Cubix',
rare: 'Rare Cubix',
legendary: 'Legendary Cubix'
};
var cubixColors = {
basic: 0xffffff,
uncommon: 0x00ff00,
rare: 0x0000ff,
legendary: 0xffd700
};
var itemHeight = 200; // Increased height for each item
var padding = 40; // Padding between items
var totalHeight = 0;
var containerWidth = 1900; // Width of the container
// Add title for each section
var headerText = new Text2('CUBIX BAG', {
size: 70,
fill: 0xFFFFFF
});
headerText.anchor.set(0.5, 0);
headerText.x = containerWidth / 2;
headerText.y = 20;
headerText.isCubixionBagImage = true;
self.bagScrollContainer.addChild(headerText);
totalHeight = 120; // Starting height after header
// Store references to cubix count Text2 for live updating
if (!self._cubixCountTexts) self._cubixCountTexts = {};
for (var i = 0; i < cubixRarities.length; i++) {
var rarity = cubixRarities[i];
// Always access the latest playerCubix from global or storage for UI accuracy
if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") {
playerCubix = window.playerCubix;
}
if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") {
playerCubix = storage.playerCubix;
}
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
}
// Defensive: always show all rarities, even if 0
var count = playerCubix && typeof playerCubix[rarity] === "number" ? playerCubix[rarity] : 0;
// Create a container for this item row for better organization
var itemContainer = new Container();
itemContainer.y = totalHeight;
itemContainer.isCubixionBagImage = true;
self.bagScrollContainer.addChild(itemContainer);
// Add background for this item
var itemBg = LK.getAsset('grassTile', {
anchorX: 0,
anchorY: 0,
width: containerWidth,
height: itemHeight
});
itemBg.alpha = 0.2;
itemBg.tint = cubixColors[rarity];
itemContainer.addChild(itemBg);
// Always show all rarities, even if 0
var cubixImg = LK.getAsset('cubix_' + rarity, {
anchorX: 0.5,
anchorY: 0.5,
x: 150,
y: itemHeight / 2,
scaleX: 2.5,
// Bigger image (increased from 2.0 to 2.5)
scaleY: 2.5 // Bigger image (increased from 2.0 to 2.5)
});
itemContainer.addChild(cubixImg);
var cubixName = new Text2(cubixNames[rarity], {
size: 80,
// Bigger text (increased from 70 to 80)
fill: cubixColors[rarity]
});
cubixName.anchor.set(0, 0.5);
cubixName.x = 300;
cubixName.y = itemHeight / 2 - 30;
itemContainer.addChild(cubixName);
var cubixDescription = new Text2('Used for capturing creatures', {
size: 55,
// Bigger text (increased from 45 to 55)
fill: 0xCCCCCC
});
cubixDescription.anchor.set(0, 0.5);
cubixDescription.x = 300;
cubixDescription.y = itemHeight / 2 + 30;
itemContainer.addChild(cubixDescription);
// Make sure to display the correct count from playerCubix
var countText = new Text2('x' + count, {
size: 100,
// Bigger text (increased from 90 to 100)
fill: 0xFFFFFF
});
countText.anchor.set(1, 0.5);
countText.x = containerWidth - 80;
countText.y = itemHeight / 2;
itemContainer.addChild(countText);
// Store reference for live updating
self._cubixCountTexts[rarity] = countText;
// Add a select button for this cubix type (for catching)
var selectBtn = LK.getAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: containerWidth - 300,
y: itemHeight / 2,
scaleX: 1.2,
scaleY: 1.2
});
selectBtn.interactive = true;
itemContainer.addChild(selectBtn);
var selectText = new Text2('SELECT', {
size: 40,
fill: 0xFFD700
});
selectText.anchor.set(0.5, 0.5);
selectText.x = containerWidth - 300;
selectText.y = itemHeight / 2;
itemContainer.addChild(selectText);
(function (rarity) {
selectBtn.down = function () {
// Set selected cubix type for catching
if (typeof encounter !== "undefined" && encounter.active) {
encounter.selectedCubix = rarity;
// Optionally, show a message or highlight
var msg = new Text2('Selected ' + cubixNames[rarity] + ' for catching!', {
size: 40,
fill: cubixColors[rarity]
});
msg.anchor.set(0.5, 0.5);
msg.x = 1024;
msg.y = 400;
game.addChild(msg);
LK.setTimeout(function () {
game.removeChild(msg);
}, 800);
}
};
})(rarity);
totalHeight += itemHeight + padding;
}
// Add a live update function for cubix counts
if (!self._cubixBagLiveUpdateAdded) {
self._cubixBagLiveUpdateAdded = true;
// Add to game.update for live updating
var oldGameUpdate = typeof game.update === "function" ? game.update : null;
game.update = function () {
// Live update cubix counts in bag if bag is visible
if (self.bagMaskContainer && self.bagMaskContainer.visible && self._cubixCountTexts) {
// Always get latest playerCubix from global or storage
if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") {
playerCubix = window.playerCubix;
}
if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") {
playerCubix = storage.playerCubix;
}
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
}
for (var i = 0; i < cubixRarities.length; i++) {
var rarity = cubixRarities[i];
if (self._cubixCountTexts[rarity]) {
var newCount = playerCubix[rarity] || 0;
var txt = self._cubixCountTexts[rarity];
if (txt.text !== 'x' + newCount) {
txt.setText('x' + newCount);
}
}
}
}
if (oldGameUpdate) oldGameUpdate();
};
}
// Add a section for other potential items (empty for now, but prepared for future items)
if (totalHeight > 0) {
var otherItemsHeader = new Text2('OTHER ITEMS', {
size: 50,
fill: 0xFFFFFF
});
otherItemsHeader.anchor.set(0.5, 0);
otherItemsHeader.x = containerWidth / 2;
otherItemsHeader.y = totalHeight;
otherItemsHeader.isCubixionBagImage = true;
self.bagScrollContainer.addChild(otherItemsHeader);
totalHeight += 80;
// Add placeholder message for future items
var placeholderText = new Text2('No other items in your bag yet', {
size: 36,
fill: 0xAAAAAA
});
placeholderText.anchor.set(0.5, 0);
placeholderText.x = containerWidth / 2;
placeholderText.y = totalHeight;
placeholderText.isCubixionBagImage = true;
self.bagScrollContainer.addChild(placeholderText);
totalHeight += 100;
}
// Set the max scroll value based on content height
self.bagMaxScroll = Math.max(0, totalHeight - 900); // 900 is approximate visible area height
// Update scroll indicators visibility
self.updateScrollIndicators();
// Add scroll functionality
self.bagMaskContainer.interactive = true;
// Remove any existing listeners to prevent duplicates
if (self.bagMaskContainer.down) {
self.bagMaskContainer.down = null;
}
if (self.bagMaskContainer.up) {
self.bagMaskContainer.up = null;
}
if (self.bagMaskContainer.move) {
self.bagMaskContainer.move = null;
}
self.bagMaskContainer.down = function (x, y, obj) {
self.bagScrollingActive = true;
self.bagScrollStartY = y;
self.bagScrollStartPos = self.bagScrollContainer.y;
};
self.bagMaskContainer.move = function (x, y, obj) {
if (self.bagScrollingActive) {
var delta = y - self.bagScrollStartY;
var newY = self.bagScrollStartPos + delta;
// Clamp scrolling
if (newY > 0) newY = 0;
if (newY < -self.bagMaxScroll) newY = -self.bagMaxScroll;
self.bagScrollContainer.y = newY;
self.updateScrollIndicators();
}
};
self.bagMaskContainer.up = function () {
self.bagScrollingActive = false;
// Only one spawn near player per update
if (Math.abs(self.bagScrollContainer.y - self.bagScrollStartPos) > 5) {
var velocity = (self.bagScrollContainer.y - self.bagScrollStartPos) * 0.3;
var targetY = self.bagScrollContainer.y + velocity;
// Clamp target
if (targetY > 0) targetY = 0;
if (targetY < -self.bagMaxScroll) targetY = -self.bagMaxScroll;
// Animate to target position
tween(self.bagScrollContainer, {
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onUpdate: function onUpdate() {
self.updateScrollIndicators();
}
});
}
// Add back button if not already added
if (!self.backToCollectionButton) {
self.backToCollectionButton = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1180,
scaleX: 1.5,
scaleY: 1.5
});
self.backToCollectionButton.interactive = true;
var backBtnText = new Text2('BACK TO COLLECTION', {
size: 50,
fill: 0xFFD700
});
backBtnText.anchor.set(0.5, 0.5);
backBtnText.x = 1024;
backBtnText.y = 1180;
self.addChild(self.backToCollectionButton);
self.addChild(backBtnText);
self.backToCollectionButton.backBtnText = backBtnText;
self.backToCollectionButton.down = function () {
// Toggle bag visibility off and show collection menu
self.bagMaskContainer.visible = false;
self.bagTitleText.visible = false;
self.scrollUpIndicator.visible = false;
self.scrollDownIndicator.visible = false;
self.backToCollectionButton.visible = false;
self.backToCollectionButton.backBtnText.visible = false;
// Change title back to collection menu
if (self.titleText) {
self.titleText.setText('CUBIXION MENU');
}
// Show bag button again
self.bagButton.visible = true;
if (self.bagBtnText) {
self.bagBtnText.visible = true;
}
// Hide Cubixion menu button if it exists
if (self.cubixionMenuButton) {
self.cubixionMenuButton.visible = false;
self.cubixionMenuBtnText.visible = false;
}
// Also hide collection button if it exists
if (self.collectionButton) {
self.collectionButton.visible = false;
if (self.collectionButton.colBtnText) {
self.collectionButton.colBtnText.visible = false;
}
}
};
}
// Make sure the back button is visible when the bag is visible
if (self.backToCollectionButton) {
self.backToCollectionButton.visible = self.bagMaskContainer.visible;
if (self.backToCollectionButton.backBtnText) {
self.backToCollectionButton.backBtnText.visible = self.bagMaskContainer.visible;
}
}
// Make sure the back button is visible when the bag is visible
if (self.backToCollectionButton) {
self.backToCollectionButton.visible = self.bagMaskContainer.visible;
if (self.backToCollectionButton.backBtnText) {
self.backToCollectionButton.backBtnText.visible = self.bagMaskContainer.visible;
}
}
// Add Collection button to left bottom corner in bag menu
if (!self.collectionButton) {
self.collectionButton = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 1180,
scaleX: 1.5,
scaleY: 1.5
});
self.collectionButton.interactive = true;
var colBtnText = new Text2('COLLECTION', {
size: 50,
fill: 0xFFD700
});
colBtnText.anchor.set(0.5, 0.5);
colBtnText.x = 200;
colBtnText.y = 1180;
self.addChild(self.collectionButton);
self.addChild(colBtnText);
self.collectionButton.colBtnText = colBtnText;
self.collectionButton.down = function () {
// Same function as backToCollectionButton - toggle bag visibility off and show collection menu
self.bagMaskContainer.visible = false;
self.bagTitleText.visible = false;
self.scrollUpIndicator.visible = false;
self.scrollDownIndicator.visible = false;
self.backToCollectionButton.visible = false;
self.backToCollectionButton.backBtnText.visible = false;
self.collectionButton.visible = false;
self.collectionButton.colBtnText.visible = false;
// Change title back to collection menu
if (self.titleText) {
self.titleText.setText('CUBIX MENU');
}
};
}
// Make collection button visible when bag is visible
if (self.collectionButton) {
self.collectionButton.visible = self.bagMaskContainer.visible;
if (self.collectionButton.colBtnText) {
self.collectionButton.colBtnText.visible = self.bagMaskContainer.visible;
}
}
// Add Collection button to left bottom corner in bag menu
if (!self.collectionButton) {
self.collectionButton = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 1180,
scaleX: 1.5,
scaleY: 1.5
});
self.collectionButton.interactive = true;
var colBtnText = new Text2('COLLECTION', {
size: 50,
fill: 0xFFD700
});
colBtnText.anchor.set(0.5, 0.5);
colBtnText.x = 200;
colBtnText.y = 1180;
self.addChild(self.collectionButton);
self.addChild(colBtnText);
self.collectionButton.colBtnText = colBtnText;
self.collectionButton.down = function () {
// Same function as backToCollectionButton - toggle bag visibility off and show collection menu
self.bagMaskContainer.visible = false;
self.bagTitleText.visible = false;
self.scrollUpIndicator.visible = false;
self.scrollDownIndicator.visible = false;
self.backToCollectionButton.visible = false;
self.backToCollectionButton.backBtnText.visible = false;
self.collectionButton.visible = false;
self.collectionButton.colBtnText.visible = false;
// Change title back to collection menu
if (self.titleText) {
self.titleText.setText('CUBIXION MENU');
}
// Show bag button again
self.bagButton.visible = true;
if (self.bagBtnText) {
self.bagBtnText.visible = true;
}
// Hide Cubixion menu button if it exists
if (self.cubixionMenuButton) {
self.cubixionMenuButton.visible = false;
self.cubixionMenuBtnText.visible = false;
}
};
}
// Make collection button visible when bag is visible
if (self.collectionButton) {
self.collectionButton.visible = self.bagMaskContainer.visible;
if (self.collectionButton.colBtnText) {
self.collectionButton.colBtnText.visible = self.bagMaskContainer.visible;
}
}
};
};
// Update scroll indicators based on current scroll position
self.updateScrollIndicators = function () {
if (self.bagMaxScroll <= 0) {
// No need for indicators if content fits
self.scrollUpIndicator.visible = false;
self.scrollDownIndicator.visible = false;
return;
}
// Show up indicator only if scrolled down
self.scrollUpIndicator.visible = self.bagMaskContainer.visible && self.bagScrollContainer.y < 0;
// Show down indicator only if can scroll further down
self.scrollDownIndicator.visible = self.bagMaskContainer.visible && self.bagScrollContainer.y > -self.bagMaxScroll;
// Fade indicators based on scroll position
self.scrollUpIndicator.alpha = Math.min(1, Math.abs(self.bagScrollContainer.y) / 100);
self.scrollDownIndicator.alpha = Math.min(1, (self.bagMaxScroll + self.bagScrollContainer.y) / 100);
};
// Example: Add a placeholder for "other items" in the bag
// You can add more items here as needed
var bagItemY = 70;
// Remove potion and revive from the bag
playerBagItems = [];
// No items to display in the bag
// Add title
self.titleText = new Text2('CUBIXION MENU', {
size: 80,
fill: 0xFFFFFF
});
self.titleText.anchor.set(0.5, 0);
self.titleText.x = 1024;
self.titleText.y = 50;
self.addChild(self.titleText);
// Show owned Cubixion with image, name, type, and count in collection menu
// Remove previous Cubixion display if present
if (self.cubixionDisplay && self.cubixionDisplay.length) {
for (var i = 0; i < self.cubixionDisplay.length; i++) {
self.removeChild(self.cubixionDisplay[i]);
}
}
self.cubixionDisplay = [];
// Defensive: get playerCubixionCollection from storage or window if not present
if (typeof playerCubixionCollection === "undefined" && typeof storage !== "undefined" && typeof storage.playerCubixionCollection !== "undefined") {
playerCubixionCollection = storage.playerCubixionCollection;
}
if (typeof playerCubixionCollection === "undefined" && typeof window !== "undefined" && typeof window.playerCubixionCollection !== "undefined") {
playerCubixionCollection = window.playerCubixionCollection;
}
if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {};
// Build ownedCubixion array from playerCubixionCollection
var ownedCubixion = [];
for (var key in playerCubixionCollection) {
if (playerCubixionCollection.hasOwnProperty(key) && playerCubixionCollection[key].count > 0) {
// Try to find matching cubixType for bag cubix
var cType = null;
for (var j = 0; j < cubixTypes.length; j++) {
if (cubixTypes[j].rarity === key) {
cType = cubixTypes[j];
break;
}
}
// Try to find matching creature for creature cubixion
if (!cType && typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) {
for (var j = 0; j < Creature.prototype.creatureList.length; j++) {
if (String(Creature.prototype.creatureList[j].id) === String(key)) {
cType = {
id: Creature.prototype.creatureList[j].id,
name: Creature.prototype.creatureList[j].name,
type: Creature.prototype.creatureList[j].type,
image: Creature.prototype.creatureList[j].image,
rarity: 'common',
color: 0xffffff
};
break;
}
}
}
if (cType) {
ownedCubixion.push({
type: cType,
count: playerCubixionCollection[key].count
});
}
}
}
// Sort by element (type) alphabetically
ownedCubixion.sort(function (a, b) {
if (!a.type.type) return -1;
if (!b.type.type) return 1;
if (a.type.type < b.type.type) return -1;
if (a.type.type > b.type.type) return 1;
return 0;
});
var cubixionStartX = 400;
var cubixionStartY = 140;
var cubixionSpacing = 320;
for (var i = 0; i < ownedCubixion.length; i++) {
var cType = ownedCubixion[i].type;
var count = ownedCubixion[i].count;
// Cubixion image
var cubixionImg = LK.getAsset(cType.image ? cType.image : 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: cubixionStartX + i * cubixionSpacing,
y: cubixionStartY + 80,
scaleX: 0.7,
scaleY: 0.7
});
self.addChild(cubixionImg);
self.cubixionDisplay.push(cubixionImg);
// Cubixion name
var cubixionName = new Text2(cType.name ? cType.name.replace(' Cubix', 'ion') : cType.rarity ? cType.rarity.charAt(0).toUpperCase() + cType.rarity.slice(1) + " Cubixion" : "Cubixion", {
size: 38,
fill: cType.color || 0xffffff
});
cubixionName.anchor.set(0.5, 0);
cubixionName.x = cubixionStartX + i * cubixionSpacing;
cubixionName.y = cubixionStartY + 160;
self.addChild(cubixionName);
self.cubixionDisplay.push(cubixionName);
// Cubixion type (element)
var elementText = new Text2('Type: ' + (cType.type ? cType.type.toUpperCase() : '-'), {
size: 32,
fill: 0xFFFFFF
});
elementText.anchor.set(0.5, 0);
elementText.x = cubixionStartX + i * cubixionSpacing;
elementText.y = cubixionStartY + 200;
self.addChild(elementText);
self.cubixionDisplay.push(elementText);
// Cubixion count
var countText = new Text2('x' + count, {
size: 36,
fill: 0xFFFFFF
});
countText.anchor.set(0.5, 0);
countText.x = cubixionStartX + i * cubixionSpacing;
countText.y = cubixionStartY + 240;
self.addChild(countText);
self.cubixionDisplay.push(countText);
}
// 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;
// Make sure the bag container is initialized but hidden initially
if (self.bagMaskContainer) {
self.bagMaskContainer.visible = false;
self.bagTitleText.visible = false;
self.scrollUpIndicator.visible = false;
self.scrollDownIndicator.visible = false;
// Initialize bag items on first show
self.populateBagItems();
// Make sure back button is properly hidden
if (self.backToCollectionButton) {
self.backToCollectionButton.visible = false;
if (self.backToCollectionButton.backBtnText) {
self.backToCollectionButton.backBtnText.visible = false;
}
}
}
// Remove any collection menu under the cross (close) button if present
for (var i = self.children.length - 1; i >= 0; i--) {
var child = self.children[i];
// Remove any Text2 or Container that is at or near the close button position (1900, 100)
if ((child instanceof Text2 || typeof child.x === "number" && typeof child.y === "number") && Math.abs(child.x - 1900) < 80 && Math.abs(child.y - 100) < 80 && child !== self.closeButton) {
self.removeChild(child);
}
}
// 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;
// Calculate total Cubixion count
var totalCubixion = 0;
var ownedCubixion = [];
if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") {
playerCubix = window.playerCubix;
}
if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") {
playerCubix = storage.playerCubix;
}
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
}
// --- Fix: Show caught Cubixion in collection menu with image ---
// Use playerCubixionCollection to show all caught Cubixion
if (typeof playerCubixionCollection === "undefined" && typeof storage !== "undefined" && typeof storage.playerCubixionCollection !== "undefined") {
playerCubixionCollection = storage.playerCubixionCollection;
}
if (typeof playerCubixionCollection === "undefined" && typeof window !== "undefined" && typeof window.playerCubixionCollection !== "undefined") {
playerCubixionCollection = window.playerCubixionCollection;
}
if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {};
// Defensive: always sync to storage and window
if (typeof storage !== "undefined" && typeof storage.set === "function") {
storage.set("playerCubixionCollection", playerCubixionCollection);
} else if (typeof storage !== "undefined") {
try {
storage.playerCubixionCollection = playerCubixionCollection;
} catch (e) {
// Fallback: do nothing if assignment fails
}
}
if (typeof window !== "undefined") window.playerCubixionCollection = playerCubixionCollection;
// Build ownedCubixion from playerCubixionCollection
ownedCubixion = [];
totalCubixion = 0;
// Add Cubixion from creatures
if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) {
for (var i = 0; i < Creature.prototype.creatureList.length; i++) {
var c = Creature.prototype.creatureList[i];
if (playerCubixionCollection[c.id] && playerCubixionCollection[c.id].count > 0) {
ownedCubixion.push({
type: {
id: c.id,
name: c.name,
type: c.type,
image: c.image,
rarity: 'common',
color: 0xffffff // Optionally set color, or use a color map by type
},
count: playerCubixionCollection[c.id].count
});
totalCubixion += playerCubixionCollection[c.id].count;
}
}
}
// Do NOT add Cubix (pokeball) types to Cubixion menu; only show Cubixion (creatures) in collection menu
// (Intentionally left blank: skip adding cubixTypes to ownedCubixion here)
// Show Cubixion count at the top of collection menu
var cubixionCountText = new Text2('Cubixion: ' + totalCubixion, {
size: 50,
fill: 0xFFFFFF
});
cubixionCountText.anchor.set(0.5, 0);
cubixionCountText.x = 1024;
cubixionCountText.y = 150;
self.addChild(cubixionCountText);
// Remove previous Cubixion image display if present
if (self.cubixionImageDisplay && Array.isArray(self.cubixionImageDisplay) && self.cubixionImageDisplay.length) {
for (var i = 0; i < self.cubixionImageDisplay.length; i++) {
self.removeChild(self.cubixionImageDisplay[i]);
}
}
self.cubixionImageDisplay = [];
// Show Cubixion as images in a row
var imgStartX = 400;
var imgY = 220;
var imgSpacing = 320;
for (var i = 0; i < ownedCubixion.length; i++) {
var cType = ownedCubixion[i].type;
var count = ownedCubixion[i].count;
var cubixionImg = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: imgStartX + i * imgSpacing,
y: imgY + 80,
scaleX: 0.7,
scaleY: 0.7
});
self.addChild(cubixionImg);
self.cubixionImageDisplay.push(cubixionImg);
// Cubixion name
var cubixionName = new Text2(cType.name.replace(' Cubix', 'ion'), {
size: 38,
fill: cType.color
});
cubixionName.anchor.set(0.5, 0);
cubixionName.x = imgStartX + i * imgSpacing;
cubixionName.y = imgY + 160;
self.addChild(cubixionName);
self.cubixionImageDisplay.push(cubixionName);
// Cubixion count
var countText = new Text2('x' + count, {
size: 36,
fill: 0xFFFFFF
});
countText.anchor.set(0.5, 0);
countText.x = imgStartX + i * imgSpacing;
countText.y = imgY + 210;
self.addChild(countText);
self.cubixionImageDisplay.push(countText);
// Show gems for this cubixion
var cid = cType.id !== undefined ? cType.id : cType.creatureId;
var creatureIdx = typeof cType.creatureId !== "undefined" ? cType.creatureId : typeof cType.id !== "undefined" ? cType.id : null;
var gems = 0;
if (creatureIdx !== null && _typeof2(playerCubixionCollection) === "object" && playerCubixionCollection[creatureIdx]) {
gems = playerCubixionCollection[creatureIdx].gems || 0;
}
var gemText = new Text2('Gems: ' + gems, {
size: 28,
fill: 0xFFD700
});
gemText.anchor.set(0.5, 0);
gemText.x = imgStartX + i * imgSpacing;
gemText.y = imgY + 250;
self.addChild(gemText);
self.cubixionImageDisplay.push(gemText);
// Add evolve button if possible
if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) {
var evoId = null;
// Find the creature in the database
for (var ci = 0; ci < Creature.prototype.creatureList.length; ci++) {
var c = Creature.prototype.creatureList[ci];
if (c.name === cType.name.replace(' Cubix', '') && c.type === cType.type) {
evoId = c.evolvesTo;
break;
}
}
if (evoId !== null && typeof Creature.prototype.creatureList[evoId] !== "undefined" && gems > 0) {
// Show evolve button
var evolveBtn = self.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: imgStartX + i * imgSpacing,
y: imgY + 320
});
evolveBtn.interactive = true;
var evolveText = new Text2('EVOLVE', {
size: 28,
fill: 0x00FF00
});
evolveText.anchor.set(0.5, 0.5);
evolveText.x = imgStartX + i * imgSpacing;
evolveText.y = imgY + 320;
self.addChild(evolveBtn);
self.addChild(evolveText);
self.cubixionImageDisplay.push(evolveBtn);
self.cubixionImageDisplay.push(evolveText);
// Evolve logic: spend 1 gem, unlock evolved form
(function (creatureIdx, evoId) {
evolveBtn.down = function () {
if (playerCubixionCollection[creatureIdx] && playerCubixionCollection[creatureIdx].gems > 0) {
playerCubixionCollection[creatureIdx].gems--;
// Add evolved form to collection
if (!playerCubixionCollection[evoId]) playerCubixionCollection[evoId] = {
count: 0,
gems: 0
};
playerCubixionCollection[evoId].count++;
playerCubixionCollection[evoId].gems++;
// Save updated collection to storage
if (typeof storage !== "undefined" && typeof storage.set === "function") {
storage.set("playerCubixionCollection", playerCubixionCollection);
} else {
if (typeof storage !== "undefined" && typeof storage.set === "function") {
storage.set("playerCubixionCollection", playerCubixionCollection);
} else {
storage.playerCubixionCollection = playerCubixionCollection;
}
}
// Show message
var msg = new Text2('Evolved to ' + Creature.prototype.creatureList[evoId].name + '!', {
size: 60,
fill: 0x00FF00
});
msg.anchor.set(0.5, 0.5);
msg.x = 1024;
msg.y = 400;
self.addChild(msg);
LK.setTimeout(function () {
self.removeChild(msg);
// Refresh collection menu to show new evolution
if (typeof self.show === "function") self.show();
}, 1000);
}
};
})(creatureIdx, evoId);
}
}
}
// Create grid of discovered creatures
var gridX = 0;
var gridY = 0;
// Make collection menu much bigger
var itemsPerRow = 7;
var cellSize = 480;
// Build a list of all possible creatures (by name, not by element type)
var allCreatures = [];
if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) {
for (var i = 0; i < Creature.prototype.creatureList.length; i++) {
var c = Creature.prototype.creatureList[i];
// For each rarity, add a possible entry
var allRarities = ['common', 'uncommon', 'rare', 'epic', 'legendary'];
for (var r = 0; r < allRarities.length; r++) {
allCreatures.push({
name: c.name,
type: c.type,
image: c.image,
rarity: allRarities[r]
});
}
}
}
// Sort allCreatures by name (alphabetically)
allCreatures.sort(function (a, b) {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
var itemsPerRow = 7;
var cellSize = 480;
for (var idx = 0; idx < allCreatures.length; idx++) {
var creature = allCreatures[idx];
var key = creature.type + '-' + creature.rarity;
var gridX = idx % itemsPerRow;
var gridY = Math.floor(idx / itemsPerRow);
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(creature.image, {
anchorX: 0.5,
anchorY: 0.5,
x: cellSize / 2,
y: cellSize / 2 - 40
});
// Apply rarity styling
var rarityColor = 0xFFFFFF;
if (creature.rarity === 'uncommon') rarityColor = 0x00FF00;
if (creature.rarity === 'rare') rarityColor = 0x0000FF;
if (creature.rarity === 'epic') rarityColor = 0xFF00FF;
if (creature.rarity === 'legendary') rarityColor = 0xFFD700;
// Make rarer creatures slightly larger
var scale = 1.0;
if (creature.rarity === 'uncommon') scale = 1.1;
if (creature.rarity === 'rare') scale = 1.2;
if (creature.rarity === 'epic') scale = 1.3;
if (creature.rarity === 'legendary') scale = 1.5;
creatureGraphic.scale.set(scale, scale);
cell.addChild(creatureGraphic);
// Add info text with name, element type, and rarity under each discovered Cubixion
var infoText = new Text2(creature.name + '\nElement: ' + creature.type.toUpperCase() + '\nRarity: ' + creature.rarity.toUpperCase(), {
size: 40,
fill: 0xFFFFFF
});
infoText.anchor.set(0.5, 0);
infoText.x = cellSize / 2;
infoText.y = cellSize / 2 + 60;
cell.addChild(infoText);
} else {
// Undiscovered creature (shadow)
var undiscoveredGraphic = LK.getAsset(creature.type + 'Cubixion', {
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: 80,
fill: 0x999999
});
questionText.anchor.set(0.5, 0.5);
questionText.x = cellSize / 2;
questionText.y = cellSize / 2;
cell.addChild(questionText);
}
}
// --- Fluent drag-to-scroll logic for scrollContainer ---
self.scrollContainer.interactive = true;
self.scrollContainer.hitArea = new Rectangle(0, 0, itemsPerRow * cellSize, (gridY + 1) * cellSize);
var isDragging = false;
var dragStartY = 0;
var dragStartScrollY = 0;
var minY = 0;
var maxY = Math.max(0, (gridY + 1) * cellSize - 1800); // 1800px is approx. visible area
// For momentum/fluent scroll
var lastMoveTime = 0;
var lastMoveY = 0;
var velocityY = 0;
var momentumTimer = null;
self.scrollContainer.down = function (x, y, obj) {
isDragging = true;
dragStartY = y;
dragStartScrollY = self.scrollContainer.y;
lastMoveTime = Date.now();
lastMoveY = y;
velocityY = 0;
if (momentumTimer) {
LK.clearInterval(momentumTimer);
momentumTimer = null;
}
// Prevent accidental selection of creatures while dragging
if (obj && obj.event && obj.event.stopPropagation) obj.event.stopPropagation();
};
self.scrollContainer.move = function (x, y, obj) {
if (isDragging) {
var now = Date.now();
var newY = dragStartScrollY + (y - dragStartY);
// Clamp scroll
if (newY > 200) newY = 200;
if (newY < -maxY) newY = -maxY;
self.scrollContainer.y = newY;
// Calculate velocity for momentum
var dt = now - lastMoveTime;
if (dt > 0) {
velocityY = (y - lastMoveY) / dt;
lastMoveTime = now;
lastMoveY = y;
}
}
};
self.scrollContainer.up = function (x, y, obj) {
isDragging = false;
// Start momentum scroll if velocity is significant
if (Math.abs(velocityY) > 0.1) {
var decay = 0.95;
momentumTimer = LK.setInterval(function () {
self.scrollContainer.y += velocityY * 30;
// Clamp scroll
if (self.scrollContainer.y > 200) self.scrollContainer.y = 200;
if (self.scrollContainer.y < -maxY) self.scrollContainer.y = -maxY;
velocityY *= decay;
if (Math.abs(velocityY) < 0.05) {
LK.clearInterval(momentumTimer);
momentumTimer = null;
}
}, 16);
}
};
};
// Hide collection screen
self.hide = function () {
self.visible = false;
// Make sure to hide bag containers too
if (self.bagMaskContainer) {
self.bagMaskContainer.visible = false;
}
if (self.bagTitleText) {
self.bagTitleText.visible = false;
}
if (self.scrollUpIndicator) {
self.scrollUpIndicator.visible = false;
}
if (self.scrollDownIndicator) {
self.scrollDownIndicator.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 + "Cubixion";
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;
// --- Cubix UI at top middle for catching action (single UI, no Cubixion UI) ---
// Remove any previous Cubixion info UI in the middle (if any) and only show the top middle UI
// (Erase any Cubixion info UI in the middle of the screen, so only the top middle Cubix info UI is shown)
if (self.cubixionInfoUI) {
for (var key in self.cubixionInfoUI) {
if (self.cubixionInfoUI[key]) {
if (Array.isArray(self.cubixionInfoUI[key])) {
for (var j = 0; j < self.cubixionInfoUI[key].length; j++) {
if (self.cubixionInfoUI[key][j] && self.cubixionInfoUI[key][j].parent === self) {
// Only remove if it is not at the top middle (y < 300)
if (typeof self.cubixionInfoUI[key][j].y === "number" && self.cubixionInfoUI[key][j].y > 300) {
self.removeChild(self.cubixionInfoUI[key][j]);
}
}
}
} else if (self.cubixionInfoUI[key].parent === self) {
if (typeof self.cubixionInfoUI[key].y === "number" && self.cubixionInfoUI[key].y > 300) {
self.removeChild(self.cubixionInfoUI[key]);
}
}
}
}
}
self.cubixionInfoUI = {};
// Defensive: always ensure playerCubix is defined and up to date, and always use the global reference
if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") {
playerCubix = window.playerCubix;
}
if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") {
playerCubix = storage.playerCubix;
}
if (_typeof5(playerCubix) !== "object" || !playerCubix) {
playerCubix = {
basic: 0,
uncommon: 0,
rare: 0,
legendary: 0
};
}
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
}
// Show only Cubix (pokeball) UI at top middle with selection and catch chance
var cubixRarities = ['basic', 'uncommon', 'rare', 'legendary'];
var cubixNames = {
basic: 'Basic Cubix',
uncommon: 'Uncommon Cubix',
rare: 'Rare Cubix',
legendary: 'Legendary Cubix'
};
var cubixColors = {
basic: 0xffffff,
uncommon: 0x00ff00,
rare: 0x0000ff,
legendary: 0xffd700
};
var cubixStartX = 1024 - 1.5 * 220;
var cubixSpacing = 220;
var cubixY = 60;
self.cubixionInfoUI.cubixImgArr = [];
self.cubixionInfoUI.cubixNameArr = [];
self.cubixionInfoUI.cubixTypeArr = [];
self.cubixionInfoUI.cubixCountArr = [];
self.cubixionInfoUI.cubixSelectArr = [];
self.cubixionInfoUI.cubixChanceArr = [];
self.selectedCubix = null;
// Only one UI: show all cubix types in a row at top middle, with image, name, type, count, and selection, and catch chance
for (var i = 0; i < cubixRarities.length; i++) {
var rarity = cubixRarities[i];
var count = playerCubix && typeof playerCubix[rarity] === "number" ? playerCubix[rarity] : 0;
// Cubix image
var cubixImg = LK.getAsset('cubix_' + rarity, {
anchorX: 0.5,
anchorY: 0,
x: cubixStartX + i * cubixSpacing,
y: cubixY,
scaleX: 0.5,
scaleY: 0.5
});
self.addChild(cubixImg);
// Cubix name
var cubixName = new Text2(cubixNames[rarity], {
size: 36,
fill: cubixColors[rarity]
});
cubixName.anchor.set(0.5, 0);
cubixName.x = cubixStartX + i * cubixSpacing;
cubixName.y = cubixY + 110;
self.addChild(cubixName);
// Cubix type (rarity)
var cubixType = new Text2(rarity.charAt(0).toUpperCase() + rarity.slice(1), {
size: 28,
fill: cubixColors[rarity]
});
cubixType.anchor.set(0.5, 0);
cubixType.x = cubixStartX + i * cubixSpacing;
cubixType.y = cubixY + 150;
self.addChild(cubixType);
// Cubix count
var cubixCount = new Text2('x' + count, {
size: 36,
fill: 0xffffff
});
cubixCount.anchor.set(0.5, 0);
cubixCount.x = cubixStartX + i * cubixSpacing;
cubixCount.y = cubixY + 190;
self.addChild(cubixCount);
// Show catch chance for this cubix
var baseChance = 0.5;
if (self.creature && self.creature.rarity) {
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 bonus = 0;
for (var j = 0; j < cubixTypes.length; j++) {
if (cubixTypes[j].rarity === rarity && typeof cubixTypes[j].catchBonus === "number") {
bonus = cubixTypes[j].catchBonus;
break;
}
}
var finalChance = baseChance + bonus;
if (finalChance > 1) finalChance = 1;
var percent = Math.round(finalChance * 100);
var cubixChance = new Text2('Chance: ' + percent + '%', {
size: 28,
fill: 0xFFD700
});
cubixChance.anchor.set(0.5, 0);
cubixChance.x = cubixStartX + i * cubixSpacing;
cubixChance.y = cubixY + 220;
self.addChild(cubixChance);
// Selection highlight (invisible by default)
var selectRect = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0,
x: cubixStartX + i * cubixSpacing,
y: cubixY - 10,
width: 120,
height: 240
});
selectRect.alpha = 0.0;
selectRect.tint = 0xFFD700;
self.addChild(selectRect);
// Make cubix image interactive for selection
cubixImg.interactive = true;
(function (rarity, idx) {
cubixImg.down = function () {
if (playerCubix[rarity] > 0) {
// Remove highlight from all
for (var j = 0; j < self.cubixionInfoUI.cubixSelectArr.length; j++) {
self.cubixionInfoUI.cubixSelectArr[j].alpha = 0.0;
}
// Highlight this one
self.cubixionInfoUI.cubixSelectArr[idx].alpha = 0.5;
self.selectedCubix = rarity;
}
};
})(rarity, i);
// If player has at least one, select the first available by default
if (self.selectedCubix === null && count > 0) {
self.selectedCubix = rarity;
selectRect.alpha = 0.5;
}
self.cubixionInfoUI.cubixImgArr.push(cubixImg);
self.cubixionInfoUI.cubixNameArr.push(cubixName);
self.cubixionInfoUI.cubixTypeArr.push(cubixType);
self.cubixionInfoUI.cubixCountArr.push(cubixCount);
self.cubixionInfoUI.cubixSelectArr.push(selectRect);
self.cubixionInfoUI.cubixChanceArr.push(cubixChance);
}
// --- Cubix selection UI ---
self.selectedCubix = 'basic'; // Default
// Defensive: always ensure playerCubix is defined and up to date, and always use the global reference
if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") {
playerCubix = window.playerCubix;
}
if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") {
playerCubix = storage.playerCubix;
}
if (_typeof3(playerCubix) !== "object" || !playerCubix) {
playerCubix = {
basic: 0,
uncommon: 0,
rare: 0,
legendary: 0
};
}
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
}
// Defensive: ensure all rarities are present and are numbers, and always update the global reference
for (var i = 0; i < cubixTypes.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
var rarity = cubixTypes[i].rarity;
if (typeof playerCubix[rarity] !== "number" || isNaN(playerCubix[rarity])) {
playerCubix[rarity] = 0;
}
}
// Defensive: always sync playerCubix to storage and global scope
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
} else {
if (typeof storage !== "undefined") {
if (typeof storage.set === "function") {
storage.set("playerCubix", playerCubix);
} else {
storage.playerCubix = {
basic: playerCubix.basic,
uncommon: playerCubix.uncommon,
rare: playerCubix.rare,
legendary: playerCubix.legendary
};
}
}
if (typeof window !== "undefined") {
window.playerCubix = playerCubix;
}
}
// Only allow selection of cubix the player has (at least one of any type)
function getTotalCubixCount() {
var total = 0;
for (var i = 0; i < cubixTypes.length; i++) {
total += playerCubix[cubixTypes[i].rarity] || 0;
}
return total;
}
// Defensive: always get the latest value from global and storage before checking
var totalCubixCount = getTotalCubixCount();
var hasAnyCubix = false;
for (var i = 0; i < cubixTypes.length; i++) {
if ((playerCubix[cubixTypes[i].rarity] || 0) > 0) {
hasAnyCubix = true;
break;
}
}
// Defensive: also check for negative or non-integer values (should never happen, but just in case)
// --- FIX: Check for all possible causes of 'No Cubix' and resync inventory if needed ---
var cubixError = false;
var cubixErrorMsg = '';
// Defensive: check for undefined, null, negative, or non-integer values
for (var i = 0; i < cubixTypes.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
var rarity = cubixTypes[i].rarity;
if (typeof playerCubix[rarity] !== "number" || isNaN(playerCubix[rarity]) || playerCubix[rarity] < 0) {
playerCubix[rarity] = 0;
cubixError = true;
cubixErrorMsg = 'Cubix inventory error detected. Inventory was reset.';
}
}
// Defensive: check for missing playerCubix object
if (!playerCubix || _typeof4(playerCubix) !== "object") {
playerCubix = {
basic: 0,
uncommon: 0,
rare: 0,
legendary: 0
};
cubixError = true;
cubixErrorMsg = 'Cubix inventory missing. Inventory was reset.';
}
// Defensive: check for out-of-sync inventory (storage vs global)
if (typeof storage !== "undefined" && typeof storage.playerCubix !== "undefined") {
for (var i = 0; i < cubixTypes.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
var rarity = cubixTypes[i].rarity;
if (typeof storage.playerCubix[rarity] !== "number" || isNaN(storage.playerCubix[rarity]) || storage.playerCubix[rarity] < 0) {
storage.playerCubix[rarity] = 0;
cubixError = true;
cubixErrorMsg = 'Cubix inventory in storage was invalid. Inventory was reset.';
}
}
}
// Defensive: check for out-of-sync between window and storage
if (typeof window !== "undefined" && typeof window.playerCubix !== "undefined") {
for (var i = 0; i < cubixTypes.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
var rarity = cubixTypes[i].rarity;
if (typeof window.playerCubix[rarity] !== "number" || isNaN(window.playerCubix[rarity]) || window.playerCubix[rarity] < 0) {
window.playerCubix[rarity] = 0;
cubixError = true;
cubixErrorMsg = 'Cubix inventory in window was invalid. Inventory was reset.';
}
}
}
// Defensive: always sync after any fix
if (cubixError && typeof syncPlayerCubix === "function") {
syncPlayerCubix();
}
// Defensive: check for totalCubixCount again after all fixes
totalCubixCount = getTotalCubixCount();
hasAnyCubix = false;
for (var i = 0; i < cubixTypes.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
if ((playerCubix[cubixTypes[i].rarity] || 0) > 0) {
hasAnyCubix = true;
break;
}
}
// If player has no Cubix, allow normal catching animation, but player must leave manually.
// Do not show any warning or block the encounter. Just proceed as normal.
// Show cubix selection UI with image and name, require player to choose for catching
// Redesigned Cubix selection UI for better readability and info
self.cubixButtons = [];
self.cubixButtonLabels = [];
self.cubixButtonNameLabels = [];
var firstAvailableIdx = -1;
var cubixButtonStartX = 400;
var cubixButtonSpacing = 320;
var cubixButtonY = 1580;
var cubixPanelHeight = 200;
var cubixPanelWidth = 300;
for (var i = 0; i < cubixTypes.length; i++) {
var cType = cubixTypes[i];
if (!cType || typeof cType.rarity === "undefined") continue;
// Panel background for each cubix type
var panelBg = LK.getAsset('grassTile', {
anchorX: 0.5,
anchorY: 0.5,
x: cubixButtonStartX + i * cubixButtonSpacing,
y: cubixButtonY + cubixPanelHeight / 2,
width: cubixPanelWidth,
height: cubixPanelHeight
});
panelBg.alpha = 0.18;
panelBg.tint = cType.color;
self.addChild(panelBg);
// Cubix image button
var btn = self.attachAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: cubixButtonStartX + i * cubixButtonSpacing,
y: cubixButtonY + 60
});
btn.scale.set(0.38, 0.38);
btn.interactive = true;
(function (type, idx) {
btn.down = function () {
if ((playerCubix[type.rarity] || 0) > 0) {
self.selectedCubix = type.rarity;
// Highlight selected
for (var j = 0; j < self.cubixButtons.length; j++) {
self.cubixButtons[j].alpha = self.cubixButtons[j].rarity === type.rarity ? 1.0 : 0.5;
if (self.cubixButtonNameLabels[j] && self.cubixButtonNameLabels[j].style) {
self.cubixButtonNameLabels[j].style.fill = self.cubixButtons[j].rarity === type.rarity ? 0xFFD700 : type.color;
}
}
// Show info about catch chance
if (self.cubixCatchInfoText) {
self.removeChild(self.cubixCatchInfoText);
}
var bonus = typeof type.catchBonus === "number" ? type.catchBonus : 0;
var baseChance = 0.5;
if (self.creature && self.creature.rarity) {
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 + bonus;
if (finalChance > 1) finalChance = 1;
var percent = Math.round(finalChance * 100);
self.cubixCatchInfoText = new Text2('Catch chance: ' + percent + '%', {
size: 38,
fill: 0xFFD700
});
self.cubixCatchInfoText.anchor.set(0.5, 0);
self.cubixCatchInfoText.x = 1024;
self.cubixCatchInfoText.y = 1750;
self.addChild(self.cubixCatchInfoText);
}
};
})(cType, i);
btn.rarity = cType.rarity;
if ((playerCubix[cType.rarity] || 0) <= 0) {
btn.alpha = 0.2;
btn.interactive = false;
} else {
if (firstAvailableIdx === -1) firstAvailableIdx = i;
}
self.addChild(btn);
self.cubixButtons.push(btn);
// Cubix name label (bigger, bold, above count)
var nameLabel = new Text2(cType.name, {
size: 40,
fill: cType.color
});
nameLabel.anchor.set(0.5, 0);
nameLabel.x = cubixButtonStartX + i * cubixButtonSpacing;
nameLabel.y = cubixButtonY + 110;
self.addChild(nameLabel);
self.cubixButtonNameLabels.push(nameLabel);
// Cubix count label (large, white, below name)
var label = new Text2('x' + (playerCubix[cType.rarity] || 0), {
size: 36,
fill: 0xFFFFFF
});
label.anchor.set(0.5, 0);
label.x = cubixButtonStartX + i * cubixButtonSpacing;
label.y = cubixButtonY + 160;
self.addChild(label);
self.cubixButtonLabels.push(label);
// Cubix bonus info (smaller, below count)
var bonus = typeof cType.catchBonus === "number" ? cType.catchBonus : 0;
var bonusText = new Text2(bonus > 0 ? "+" + Math.round(bonus * 100) + "% catch" : "No bonus", {
size: 24,
fill: 0xFFD700
});
bonusText.anchor.set(0.5, 0);
bonusText.x = cubixButtonStartX + i * cubixButtonSpacing;
bonusText.y = cubixButtonY + 200;
self.addChild(bonusText);
// Rarity label (smaller, below bonus)
var rarityText = new Text2(cType.rarity.charAt(0).toUpperCase() + cType.rarity.slice(1), {
size: 22,
fill: cType.color
});
rarityText.anchor.set(0.5, 0);
rarityText.x = cubixButtonStartX + i * cubixButtonSpacing;
rarityText.y = cubixButtonY + 230;
self.addChild(rarityText);
btn.alpha = i === firstAvailableIdx ? 1.0 : 0.5;
if (self.cubixButtonNameLabels[i] && self.cubixButtonNameLabels[i].style) {
self.cubixButtonNameLabels[i].style.fill = i === firstAvailableIdx ? 0xFFD700 : cType.color;
}
}
// Set default selectedCubix to first available type
if (firstAvailableIdx !== -1) {
self.selectedCubix = cubixTypes[firstAvailableIdx].rarity;
// Show info about catch chance for default
if (self.cubixCatchInfoText) {
self.removeChild(self.cubixCatchInfoText);
}
var type = cubixTypes[firstAvailableIdx];
var bonus = typeof type.catchBonus === "number" ? type.catchBonus : 0;
var baseChance = 0.5;
if (self.creature && self.creature.rarity) {
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 + bonus;
if (finalChance > 1) finalChance = 1;
var percent = Math.round(finalChance * 100);
self.cubixCatchInfoText = new Text2('Catch chance: ' + percent + '%', {
size: 38,
fill: 0xFFD700
});
self.cubixCatchInfoText.anchor.set(0.5, 0);
self.cubixCatchInfoText.x = 1024;
self.cubixCatchInfoText.y = 1750;
self.addChild(self.cubixCatchInfoText);
// Highlight default selected
for (var j = 0; j < self.cubixButtons.length; j++) {
self.cubixButtons[j].alpha = j === firstAvailableIdx ? 1.0 : 0.5;
if (self.cubixButtonNameLabels[j] && self.cubixButtonNameLabels[j].style) {
self.cubixButtonNameLabels[j].style.fill = j === firstAvailableIdx ? 0xFFD700 : cubixTypes[j].color;
}
}
}
// 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
});
// Defensive: check if player has any cubix before showing capture button/text
var totalCubixCount = 0;
if (typeof playerCubix !== "undefined" && typeof cubixTypes !== "undefined") {
for (var i = 0; i < cubixTypes.length; i++) {
totalCubixCount += playerCubix[cubixTypes[i].rarity] || 0;
}
}
// --- Cubix counter UI: Show cubix type-by-type counter below cubix selection buttons in encounter ---
// Remove previous cubixCountTexts if present
if (self.cubixCountTexts) {
for (var i = 0; i < self.cubixCountTexts.length; i++) {
if (self.cubixCountTexts[i] && self.cubixCountTexts[i].parent === self) {
self.removeChild(self.cubixCountTexts[i]);
}
}
}
self.cubixCountTexts = [];
for (var i = 0; i < cubixTypes.length; i++) {
var cType = cubixTypes[i];
if (!cType || typeof cType.rarity === "undefined") continue;
// Show cubix image and count for each type
var img = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: 700 + i * 200,
y: 1720,
scaleX: 0.18,
scaleY: 0.18
});
self.addChild(img);
// Show count text
var count = playerCubix && typeof playerCubix[cType.rarity] === "number" ? playerCubix[cType.rarity] : 0;
var countText = new Text2('x' + count, {
size: 32,
fill: cType.color
});
countText.anchor.set(0.5, 0);
countText.x = 700 + i * 200;
countText.y = 1745;
self.addChild(countText);
self.cubixCountTexts.push(countText);
}
// Fix: Always show capture button if player has at least 1 of any cubix (including 10 basic cubix)
if (playerCubix && (playerCubix.basic > 0 || playerCubix.uncommon > 0 || playerCubix.rare > 0 || playerCubix.legendary > 0)) {
// 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 (removed attempts display)
var infoText = new Text2('TYPE: ' + self.creature.type.toUpperCase() + '\n' + 'RARITY: ' + self.creature.rarity.toUpperCase(), {
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 cubix, reduce inventory
var selectedType = cubixTypes[0];
for (var i = 0; i < cubixTypes.length; i++) {
if (cubixTypes[i].rarity === self.selectedCubix) {
selectedType = cubixTypes[i];
break;
}
}
if (selectedType && typeof selectedType.rarity === "string" && playerCubix && typeof playerCubix[selectedType.rarity] === "number" && playerCubix[selectedType.rarity] > 0) {
playerCubix[selectedType.rarity]--;
// Defensive: always sync playerCubix to storage and global scope after change
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
} else {
if (typeof storage !== "undefined") {
if (typeof storage.set === "function") {
storage.set("playerCubix", playerCubix);
} else {
storage.playerCubix = {
basic: playerCubix.basic,
uncommon: playerCubix.uncommon,
rare: playerCubix.rare,
legendary: playerCubix.legendary
};
}
}
if (typeof window !== "undefined") {
window.playerCubix = playerCubix;
}
// Also decrement from Cubixion bag collection if present
if (typeof playerCubixionCollection !== "undefined") {
var cubixionId = selectedType.rarity;
if (playerCubixionCollection[cubixionId] && typeof playerCubixionCollection[cubixionId].count === "number" && playerCubixionCollection[cubixionId].count > 0) {
playerCubixionCollection[cubixionId].count--;
// Defensive: always sync to storage and window
if (typeof storage !== "undefined" && typeof storage.set === "function") {
storage.set("playerCubixionCollection", playerCubixionCollection);
} else if (typeof storage !== "undefined") {
try {
storage.playerCubixionCollection = playerCubixionCollection;
} catch (e) {}
}
if (typeof window !== "undefined") window.playerCubixionCollection = playerCubixionCollection;
}
}
}
// Update cubix button labels and cubix count display
if (self.cubixButtons) {
for (var i = 0; i < self.cubixButtons.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
// Update label
if (self.cubixButtonLabels && self.cubixButtonLabels[i]) {
self.cubixButtonLabels[i].setText(cubixTypes[i].name + ' (' + (playerCubix[cubixTypes[i].rarity] || 0) + ')');
}
if (playerCubix[cubixTypes[i].rarity] <= 0) self.cubixButtons[i].alpha = 0.2;
}
}
// Update cubix count text below (by type with image)
if (self.cubixCountTexts) {
for (var i = 0; i < self.cubixCountTexts.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
var rarity = cubixTypes[i].rarity;
if (self.cubixCountTexts[i]) {
self.cubixCountTexts[i].setText('x' + (playerCubix[rarity] || 0));
}
}
}
}
// 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 selected cubix type for 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 catchBonus = 0;
var selectedType = cubixTypes[0];
for (var i = 0; i < cubixTypes.length; i++) {
if (cubixTypes[i].rarity === self.selectedCubix) {
selectedType = cubixTypes[i];
break;
}
}
if (selectedType && typeof selectedType.catchBonus === "number") {
catchBonus = selectedType.catchBonus;
}
var finalChance = baseChance + catchBonus;
if (finalChance > 1) finalChance = 1;
if (Math.random() < finalChance) captured = true;
}
if (captured) {
// Success!
self.captureSuccess();
} else if (self.captureAttempts >= self.maxAttempts) {
// Failed after max attempts
self.captureFail();
} else {
// No attempts text to update (removed attempts UI)
// Decrease player's cubix of used type by 1 on every failed catch attempt
var selectedType = cubixTypes[0];
for (var i = 0; i < cubixTypes.length; i++) {
if (cubixTypes[i].rarity === self.selectedCubix) {
selectedType = cubixTypes[i];
break;
}
}
if (selectedType && typeof selectedType.rarity === "string" && playerCubix && typeof playerCubix[selectedType.rarity] === "number" && playerCubix[selectedType.rarity] > 0) {
playerCubix[selectedType.rarity]--;
// Defensive: always sync playerCubix to storage and global scope after change
if (typeof syncPlayerCubix === "function") {
syncPlayerCubix();
} else {
if (typeof storage !== "undefined") {
if (typeof storage.set === "function") {
storage.set("playerCubix", playerCubix);
} else {
storage.playerCubix = {
basic: playerCubix.basic,
uncommon: playerCubix.uncommon,
rare: playerCubix.rare,
legendary: playerCubix.legendary
};
}
}
if (typeof window !== "undefined") {
window.playerCubix = playerCubix;
}
}
}
// Update cubix button labels and cubix count display
if (self.cubixButtons) {
for (var i = 0; i < self.cubixButtons.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
// Update label
if (self.cubixButtonLabels && self.cubixButtonLabels[i]) {
self.cubixButtonLabels[i].setText(cubixTypes[i].name + " (" + (playerCubix[cubixTypes[i].rarity] || 0) + ")");
}
if (playerCubix[cubixTypes[i].rarity] <= 0) self.cubixButtons[i].alpha = 0.2;
}
}
// Update cubix count text below (by type with image)
if (self.cubixCountTexts) {
for (var i = 0; i < self.cubixCountTexts.length; i++) {
if (!cubixTypes[i] || typeof cubixTypes[i].rarity === "undefined") continue;
var rarity = cubixTypes[i].rarity;
if (self.cubixCountTexts[i]) {
self.cubixCountTexts[i].setText("x" + (playerCubix[rarity] || 0));
}
}
}
}
};
// 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;
// --- Cubixion collection and gem system ---
if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {};
var cid = self.creature.creatureId;
if (!playerCubixionCollection[cid]) {
playerCubixionCollection[cid] = {
count: 0,
gems: 0
};
}
playerCubixionCollection[cid].count++;
playerCubixionCollection[cid].gems++; // 1 gem per catch
// 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 from mapCreatures/mapCreatureTiles/mapCreatureLabels if present
if (typeof mapCreatureTiles !== "undefined" && typeof mapCreatures !== "undefined" && typeof mapCreatureLabels !== "undefined") {
for (var i = mapCreatureTiles.length - 1; i >= 0; i--) {
if (mapCreatureTiles[i] === tile) {
// Defensive: Only remove label if it exists and index is valid
if (Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && mapCreatureLabels[i]) {
tile.removeChild(mapCreatureLabels[i]);
mapCreatureLabels.splice(i, 1);
}
// Defensive: Only remove from mapCreatures and mapCreatureTiles if index is valid
if (Array.isArray(mapCreatures) && i < mapCreatures.length) {
mapCreatures.splice(i, 1);
}
if (Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length) {
mapCreatureTiles.splice(i, 1);
}
break;
}
}
}
// Remove encounterIndicator if present (yellow circle)
}
}
// 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 () {
// --- Refresh collection menu if open ---
if (typeof collection !== "undefined" && collection.visible && typeof collection.show === "function") {
collection.show();
}
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
// 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, using unique tile for each biome
var biomeTileAssetMap = {
grass: ['grassTile', 'grassTile2'],
water: ['waterTile', 'waterTile2'],
desert: ['desertTile', 'desertTile2'],
forest: ['forestTile', 'forestTile2'],
mountain: ['mountainTile', 'mountainTile2'],
street: ['streetTile', 'streetTile2'],
urban: ['urbanTile', 'urbanTile2']
};
// Special handling for out-of-world tiles removed. All tiles use biome tile assets.
var assetList = biomeTileAssetMap[self.biomeType] || [self.biomeType + 'Tile'];
// Randomly pick one of the two tile assets for this biome
var assetId = assetList[Math.floor(Math.random() * assetList.length)];
// Use a single tile for the biome, with the new TILE_SIZE (already 3x bigger)
self.tileImage = self.attachAsset(assetId, {
anchorX: 0,
anchorY: 0,
width: TILE_SIZE,
height: TILE_SIZE,
x: 0,
y: 0
});
self.tileImage.alpha = 0.9;
// Set the logical tile size to TILE_SIZE for correct movement and centering
self.tileSize = TILE_SIZE;
// 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;
}
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 = 1; // Match the reduced player vision radius for less lag
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);
// Store reference for orientation logic
self.playerGraphics = playerGraphics;
// Always reset rotation and scale.x on init
self.playerGraphics.rotation = 0;
self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x);
// 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;
// --- Determine facing direction and update sprite orientation ---
// We'll use self.facing: 'left', 'right', 'up', 'down'
if (typeof self.facing === "undefined") self.facing = "down";
if (typeof self.playerGraphics === "undefined") self.playerGraphics = self.children[0]; // Assume first child is player sprite
var dx = newGridX - self.gridX;
var dy = newGridY - self.gridY;
if (Math.abs(dx) > Math.abs(dy)) {
// Horizontal move
if (dx > 0) {
self.facing = "right";
// Swap to 'player' asset if not already
if (self.playerGraphics && self.playerGraphics.assetId !== 'player') {
self.removeChild(self.playerGraphics);
self.playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
if (self.playerGraphics) {
self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); // Face right (default)
}
} else if (dx < 0) {
self.facing = "left";
// Swap to 'player' asset if not already
if (self.playerGraphics && self.playerGraphics.assetId !== 'player') {
self.removeChild(self.playerGraphics);
self.playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
if (self.playerGraphics) {
self.playerGraphics.scale.x = -Math.abs(self.playerGraphics.scale.x); // Flip horizontally to face left
}
}
} else if (Math.abs(dy) > 0) {
// Vertical move
if (dy < 0) {
self.facing = "up";
// Swap to 'player_up' asset if not already
if (self.playerGraphics && self.playerGraphics.assetId !== 'player_up') {
// Remove old graphic
self.removeChild(self.playerGraphics);
// Attach new up-facing asset
self.playerGraphics = self.attachAsset('player_up', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
if (self.playerGraphics) {
self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); // Face right (default)
self.playerGraphics.rotation = Math.PI; // Show back (rotate 180deg)
}
} else if (dy > 0) {
self.facing = "down";
// Swap to 'player' asset if not already
if (self.playerGraphics && self.playerGraphics.assetId !== 'player') {
self.removeChild(self.playerGraphics);
self.playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
if (self.playerGraphics) {
self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x); // Face right (default)
self.playerGraphics.rotation = 0; // Face forward
}
}
}
// If not moving vertically, always reset rotation
if (Math.abs(dx) > 0 && Math.abs(dy) === 0 && self.playerGraphics) {
self.playerGraphics.rotation = 0;
}
// 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
// Only animate player if actually on a water tile (not on edge)
if (targetTile && targetTile.biomeType === "water") {
// No shaking or swimming animation when entering water
self._inWaterSwim = false;
} else {
// If not in water, stop swimming animation if it was running
if (self._inWaterSwim) {
self._inWaterSwim = false;
if (typeof tween !== "undefined") {
tween.stop(self, {
y: true
});
}
}
}
}
// 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
}
// Always reset player sprite orientation if not moving
if (self.facing === "up") {
if (!self.playerGraphics || self.playerGraphics.assetId !== 'player_up') {
if (self.playerGraphics) self.removeChild(self.playerGraphics);
self.playerGraphics = self.attachAsset('player_up', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x);
self.playerGraphics.rotation = Math.PI;
} else if (self.facing === "left") {
if (!self.playerGraphics || self.playerGraphics.assetId !== 'player') {
if (self.playerGraphics) self.removeChild(self.playerGraphics);
self.playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
self.playerGraphics.scale.x = -Math.abs(self.playerGraphics.scale.x);
self.playerGraphics.rotation = 0;
} else if (self.facing === "right") {
if (!self.playerGraphics || self.playerGraphics.assetId !== 'player') {
if (self.playerGraphics) self.removeChild(self.playerGraphics);
self.playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x);
self.playerGraphics.rotation = 0;
} else {
if (!self.playerGraphics || self.playerGraphics.assetId !== 'player') {
if (self.playerGraphics) self.removeChild(self.playerGraphics);
self.playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
y: -44
});
self.playerGraphics.scale.set(0.34, 0.34);
}
self.playerGraphics.scale.x = Math.abs(self.playerGraphics.scale.x);
self.playerGraphics.rotation = 0;
}
// Hide player sprite if on dark (black) block
if (typeof gameMap !== "undefined") {
var tile = gameMap.getTileAt(self.gridX, self.gridY);
// Do not hide player, but do not show dark tiles
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');
}
// --- Rotate player if next to world border ---
if (typeof self.rotation !== "undefined") {
self.rotation = 0;
}
var nearBorder = false;
var borderDir = 0; // 0=none, 1=left, 2=right, 3=top, 4=bottom
if (self.gridX <= 0.5) {
nearBorder = true;
borderDir = 1;
} else if (self.gridX >= MAP_WIDTH - 1) {
nearBorder = true;
borderDir = 2;
} else if (self.gridY <= 0.5) {
nearBorder = true;
borderDir = 3;
} else if (self.gridY >= MAP_HEIGHT - 1) {
nearBorder = true;
borderDir = 4;
}
if (nearBorder) {
// Rotate player to indicate border direction
if (borderDir === 1) self.rotation = Math.PI / 2; // Left: face down
else if (borderDir === 2) self.rotation = -Math.PI / 2; // Right: face up
// Remove upside-down rotation at top border (no Math.PI)
else if (borderDir === 3) self.rotation = 0; // Top: normal (no upside down)
else if (borderDir === 4) self.rotation = 0; // Bottom: normal
} else {
self.rotation = 0;
}
// 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 = 1; // Reduced from 2 to 1 for even fewer visible tiles and less lag
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) {
// Hide dark (out-of-world) tiles so black area is never shown
if (tile.biomeType === "dark") {
tile.visible = false;
continue;
}
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
// 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) {
// --- 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";
}
// Try to find the correct image asset for the cubixion (creature) type
var creatureImageAsset = null;
if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) {
// Find a matching creature in the database for this type
for (var ci = 0; ci < Creature.prototype.creatureList.length; ci++) {
var c = Creature.prototype.creatureList[ci];
if (c.type === creatureType) {
// Prefer image asset if available
if (c.image && LK.assets && LK.assets[c.image]) {
creatureImageAsset = c.image;
}
// If there is a specific image asset for this type+name, use it
if (LK.assets && LK.assets["creature_" + c.id + "_" + c.name + "_" + c.type]) {
creatureImageAsset = "creature_" + c.id + "_" + c.name + "_" + c.type;
}
break;
}
}
}
// Fallback to typeCreature if no image found
if (!creatureImageAsset) {
// Try to use a generic image for this type if available
if (LK.assets && LK.assets[creatureType + "Cubixion"]) {
creatureImageAsset = creatureType + "Cubixion";
} else {
// As a last fallback, use a generic ellipse
creatureImageAsset = "grassCubixion";
}
}
// Show the cubixion visually using the correct image asset
var creatureVisual = LK.getAsset(creatureImageAsset, {
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) {
// 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
// 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
****/
// Example: use desertTile as legendary cubix
// Example: use Voltix as rare cubix
// Example: use Boulder as uncommon cubix
// Example: use Sproutle as basic cubix
// Cubix (pokeball) image assets
// Out-of-world tile asset (default color, can be changed in MapTile)
// JSON helper functions for parsing and stringifying
// Game dimensions and settings
// Tween for animations
// Storage for saving game progress
// Player character
// Map tiles
// Creatures by elemental type
// UI elements
// Encounter indicator
// Sounds
// Unique image for urban biome
// Unique biome tile assets for each biome (example: add one more for each)
// Creature type base shapes (for fallback and type-based visuals)
// --- Creature image assets for all 100 unique creatures ---
// Example: LK.init.image('creature_0', {width:120, height:120, id:'<image_id>'})
// For demonstration, we use placeholder image ids. Replace with real ids as needed.
// The following assets are initialized with the creature's name and element in the asset id for clarity
function _typeof5(o) {
"@babel/helpers - typeof";
return _typeof5 = "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;
}, _typeof5(o);
}
function _typeof4(o) {
"@babel/helpers - typeof";
return _typeof4 = "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;
}, _typeof4(o);
}
function _typeof3(o) {
"@babel/helpers - typeof";
return _typeof3 = "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;
}, _typeof3(o);
}
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "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;
}, _typeof2(o);
}
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 * 3; // Make each tile 3 times bigger than before
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
// Remove special case for (0,0) tile, all tiles are now the same size (TILE_SIZE * 3)
for (var y = 0; y < MAP_HEIGHT; y++) {
var _loop = function _loop() {
t = gameMap.getTileAt(x, y);
if (t) {
t.tileSize = TILE_SIZE;
}
},
t,
originalY;
for (var x = 0; x < MAP_WIDTH; x++) {
_loop();
}
}
// Spawn player at a random non-water, non-dark tile, and ensure nothing else spawns at the same place at game start
function findRandomValidPlayerStart(gameMap) {
var validTiles = [];
for (var y = 0; y < MAP_HEIGHT; y++) {
for (var x = 0; x < MAP_WIDTH; x++) {
var biome = gameMap.tiles && gameMap.tiles[y] && gameMap.tiles[y][x];
if (biome && biome !== "water" && biome !== "dark") {
var tileObj = gameMap.getTileAt(x, y);
// Defensive: skip if tileObj is missing or already has a creature/cubix
// Prevent spawning on player's current tile
if (tileObj && !tileObj.hasCreature && !tileObj._cubixVisual && !tileObj._creatureVisual && !(tileObj.gridX === Math.round(playerStartX) && tileObj.gridY === Math.round(playerStartY))) {
validTiles.push({
x: x,
y: y
});
}
}
}
}
// Pick a random valid tile, fallback to 0,0 if none found
if (validTiles.length > 0) {
return validTiles[Math.floor(Math.random() * validTiles.length)];
}
return {
x: 0,
y: 0
};
}
var validStart = findRandomValidPlayerStart(gameMap);
var playerStartX = validStart.x;
var playerStartY = validStart.y;
// Ensure start tile is not water or dark for consistency
if (gameMap.tiles && gameMap.tiles[playerStartY] && gameMap.tiles[playerStartY][playerStartX]) {
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 {
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();
centerViewOnPlayer();
centerViewOnPlayer();
// Prevent spawning cubix/cubixion on the same tile as the player at game start
var playerStartTile = gameMap.getTileAt(playerStartX, playerStartY);
if (playerStartTile) {
// Remove cubix visual if present
if (playerStartTile._cubixVisual) {
playerStartTile.removeChild(playerStartTile._cubixVisual);
playerStartTile._cubixVisual = null;
if (typeof mapCubixTiles !== "undefined" && typeof mapCubix !== "undefined") {
for (var i = mapCubixTiles.length - 1; i >= 0; i--) {
if (mapCubixTiles[i] === playerStartTile) {
mapCubix.splice(i, 1);
mapCubixTiles.splice(i, 1);
break;
}
}
}
}
// Remove cubixion (creature) visual if present
if (playerStartTile._creatureVisual) {
playerStartTile.removeChild(playerStartTile._creatureVisual);
playerStartTile._creatureVisual = null;
playerStartTile.hasCreature = false;
if (typeof mapCreatureTiles !== "undefined" && typeof mapCreatures !== "undefined" && typeof mapCreatureLabels !== "undefined") {
for (var i = mapCreatureTiles.length - 1; i >= 0; i--) {
if (mapCreatureTiles[i] === playerStartTile) {
// Defensive: Only remove label if it exists and index is valid
if (Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && mapCreatureLabels[i]) {
playerStartTile.removeChild(mapCreatureLabels[i]);
mapCreatureLabels.splice(i, 1);
}
// Defensive: Only remove from mapCreatures and mapCreatureTiles if index is valid
if (Array.isArray(mapCreatures) && i < mapCreatures.length) {
mapCreatures.splice(i, 1);
}
if (Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length) {
mapCreatureTiles.splice(i, 1);
}
break;
}
}
}
}
}
// 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,
scaleX: 1.5,
scaleY: 1.5
});
collectionButton.interactive = true;
// Collection button icon
var collectionText = new Text2('COL', {
size: 48,
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;
// Cubix inventory UI
// Removed cubixInventoryText from main UI (Cubix inventory is shown in collection menu)
// Define playerCubix in global scope if not already defined
// Reset playerCubix and Cubixion collection at game start
var playerCubix = {
basic: 10,
uncommon: 0,
rare: 0,
legendary: 0
};
// Remove all Cubix except 10 basic Cubix from storage and window at game start
if (typeof storage !== "undefined") {
if (typeof storage.set === "function") {
storage.set("playerCubix", {
basic: 10,
uncommon: 0,
rare: 0,
legendary: 0
});
storage.set("playerCubixionCollection", {}); // Reset Cubixion collection
} else {
storage.playerCubix = {
basic: 10,
uncommon: 0,
rare: 0,
legendary: 0
};
storage.playerCubixionCollection = {};
}
}
if (typeof window !== "undefined") {
window.playerCubix = {
basic: 10,
uncommon: 0,
rare: 0,
legendary: 0
};
window.playerCubixionCollection = {};
}
// Defensive: always sync playerCubix to storage and global scope after every update
function syncPlayerCubix() {
if (typeof storage !== "undefined") {
if (typeof storage.set === "function") {
storage.set("playerCubix", playerCubix);
} else {
// fallback for legacy: assign a shallow copy to avoid circular reference
storage.playerCubix = {
basic: playerCubix.basic,
uncommon: playerCubix.uncommon,
rare: playerCubix.rare,
legendary: playerCubix.legendary
};
}
}
if (typeof window !== "undefined") {
window.playerCubix = playerCubix;
}
}
// Cubixion collection and gem system
var playerCubixionCollection = {}; // {creatureId: {count: 0, gems: 0}}
// Reset Cubixion collection at game start
if (typeof storage !== "undefined") {
if (typeof storage.set === "function") {
storage.set("playerCubixionCollection", {});
} else {
storage.playerCubixionCollection = {};
}
}
if (typeof window !== "undefined") {
window.playerCubixionCollection = {};
}
// Removed updateCubixInventoryText and its calls (Cubix inventory now shown in collection menu)
// 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.25 tile up per press
var newGridY = player.gridY - 0.25;
if (newGridY < 0) newGridY = 0;
// Prevent moving into black area (dark biome)
var targetTile = gameMap.getTileAt(Math.round(player.gridX), Math.round(newGridY));
if (targetTile && targetTile.biomeType !== "dark") {
player.moveToGrid(player.gridX, newGridY);
}
}
};
dpad.downButton.down = function () {
if (!player.moving && !encounterActive && !collection.visible) {
// Move exactly 0.25 tile down per press
var newGridY = player.gridY + 0.25;
if (newGridY > MAP_HEIGHT - 1) newGridY = MAP_HEIGHT - 1;
// Prevent moving into black area (dark biome)
var targetTile = gameMap.getTileAt(Math.round(player.gridX), Math.round(newGridY));
if (targetTile && targetTile.biomeType !== "dark") {
player.moveToGrid(player.gridX, newGridY);
}
}
};
dpad.leftButton.down = function () {
if (!player.moving && !encounterActive && !collection.visible) {
// Move exactly 0.25 tile left per press
var newGridX = player.gridX - 0.25;
if (newGridX < 0) newGridX = 0;
// Prevent moving into black area (dark biome)
var targetTile = gameMap.getTileAt(Math.round(newGridX), Math.round(player.gridY));
if (targetTile && targetTile.biomeType !== "dark") {
player.moveToGrid(newGridX, player.gridY);
}
}
};
dpad.rightButton.down = function () {
if (!player.moving && !encounterActive && !collection.visible) {
// Move exactly 0.25 tile right per press
var newGridX = player.gridX + 0.25;
if (newGridX > MAP_WIDTH - 1) newGridX = MAP_WIDTH - 1;
// Prevent moving into black area (dark biome)
var targetTile = gameMap.getTileAt(Math.round(newGridX), Math.round(player.gridY));
if (targetTile && targetTile.biomeType !== "dark") {
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) {
// Hide collection button and text when menu is open
collectionButton.visible = false;
collectionText.visible = false;
// Always reload the bag menu for all creatures
if (typeof collection.show === "function") {
collection.show();
}
// Show exit button in collection menu
if (!collection.exitButton) {
collection.exitButton = collection.attachAsset('dpadButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1900,
y: 150
});
collection.exitButton.interactive = true;
var exitText = new Text2('EXIT', {
size: 48,
fill: 0xFFFFFF
});
exitText.anchor.set(0.5, 0.5);
exitText.x = 1900;
exitText.y = 150;
collection.addChild(exitText);
collection.exitButton.exitText = exitText;
collection.exitButton.down = function () {
collection.hide();
// Show player and collection button again
player.visible = true;
collectionButton.visible = true;
collectionText.visible = true;
// Hide exit button and text
if (collection.exitButton) {
collection.exitButton.visible = false;
if (collection.exitButton.exitText) {
collection.exitButton.exitText.visible = false;
}
}
};
}
// Show exit button and text
if (collection.exitButton) {
collection.exitButton.visible = true;
if (collection.exitButton.exitText) {
collection.exitButton.exitText.visible = true;
}
}
}
};
// Handle collection close button
collection.closeButton.down = function () {
collection.hide();
// Show player and collection button again
player.visible = true;
collectionButton.visible = true;
collectionText.visible = true;
// Hide exit button and text
if (collection.exitButton) {
collection.exitButton.visible = false;
if (collection.exitButton.exitText) {
collection.exitButton.exitText.visible = false;
}
}
};
// Track last player position to detect changes
var lastPlayerGridX = player.gridX;
var lastPlayerGridY = player.gridY;
// --- Cubix system ---
// Cubix rarities and their catch chances
if (typeof cubixTypes === "undefined") {
var cubixTypes = [{
id: 'cubix_basic',
name: 'Basic Cubix',
color: 0xffffff,
rarity: 'basic',
catchBonus: 0,
chance: 0.5
}, {
id: 'cubix_uncommon',
name: 'Uncommon Cubix',
color: 0x00ff00,
rarity: 'uncommon',
catchBonus: 0.15,
chance: 0.65
}, {
id: 'cubix_rare',
name: 'Rare Cubix',
color: 0x0000ff,
rarity: 'rare',
catchBonus: 0.3,
chance: 0.8
}, {
id: 'cubix_legendary',
name: 'Legendary Cubix',
color: 0xffd700,
rarity: 'legendary',
catchBonus: 0.5,
chance: 0.95
}];
}
// 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) {
// Defensive: check for undefined or not arrays before accessing index 0
if (typeof mapCubixTiles === "undefined" || typeof mapCubix === "undefined" || !Array.isArray(mapCubixTiles) || !Array.isArray(mapCubix) || mapCubixTiles.length === 0 || mapCubix.length === 0 || typeof mapCubixTiles[0] === "undefined" || typeof mapCubix[0] === "undefined") {
return;
}
// Defensive: do not access index 0 if arrays are empty or undefined
return;
}
lastUpdateTime = now;
// Player's cubix inventory (legacy pokeball variable replaced)
if (typeof playerCubix === "undefined") {
playerCubix = {
basic: 3,
uncommon: 1,
rare: 0,
legendary: 0
};
}
// Defensive: always sync playerCubix to storage and global scope at start of update
syncPlayerCubix();
// Cubix map objects (legacy pokeball variable replaced)
if (typeof mapCubix === "undefined") {
mapCubix = [];
mapCubixTiles = [];
mapCubixMax = 12; // Max cubix on map at once (reduced for performance)
mapCubixSpawnCooldown = 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 = 12; // Max creatures on map at once (reduced for performance)
mapCreatureSpawnCooldown = 0;
}
// --- Add despawn timer for cubixion (creature) spawns: despawn after 5 minutes (300 seconds) ---
var nowTime = Date.now();
for (var i = mapCreatures.length - 1; i >= 0; i--) {
// Defensive: skip if arrays are out of sync or index is out of bounds
if (!mapCreatureTiles || !mapCreatures || i >= mapCreatureTiles.length || i >= mapCreatures.length) {
continue;
}
var tile = mapCreatureTiles[i];
// Defensive: skip if tile is undefined
if (typeof tile === "undefined" || tile === null) {
continue;
}
// Prevent despawn if player is currently on this tile
if (player && Math.round(player.gridX) === tile.gridX && Math.round(player.gridY) === tile.gridY) {
// Do not run despawn logic for this tile while player is on it
continue;
}
// Initialize spawn timestamp if not set
if (tile && typeof tile._creatureSpawnedAt === "undefined") {
tile._creatureSpawnedAt = nowTime;
}
// Prevent despawn until it has spawned for 5 minutes (300000 ms)
// (No despawn here, so skip this block entirely)
// --- Keep original explored despawn logic ---
// Initialize despawn timer if not set
if (tile && typeof tile._creatureDespawnTimer === "undefined") {
tile._creatureDespawnTimer = 0;
}
// If tile is explored, start despawn timer
if (tile && tile.explored) {
tile._creatureDespawnTimer++;
// Only despawn if timer exceeds threshold (e.g. 90 frames ~1.5s)
if (tile._creatureDespawnTimer > 90) {
if (typeof mapCreatureLabels !== "undefined" && Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && typeof mapCreatureLabels[i] !== "undefined" && mapCreatureLabels[i]) {
if (tile) tile.removeChild(mapCreatureLabels[i]);
mapCreatureLabels.splice(i, 1);
}
if (tile && tile.hasCreature) {
tile.hasCreature = false;
}
// Defensive: Only remove from mapCreatures and mapCreatureTiles if index is valid
if (Array.isArray(mapCreatures) && i < mapCreatures.length && typeof mapCreatures[i] !== "undefined") {
mapCreatures.splice(i, 1);
}
if (Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length && typeof mapCreatureTiles[i] !== "undefined") {
mapCreatureTiles.splice(i, 1);
}
continue;
}
} else if (!tile || Math.random() < 0.002) {
// Lower random despawn chance
// Defensive: Only remove label if arrays and index are valid
if (typeof mapCreatureLabels !== "undefined" && Array.isArray(mapCreatureLabels) && i < mapCreatureLabels.length && mapCreatureLabels[i] !== undefined) {
if (tile) tile.removeChild(mapCreatureLabels[i]);
mapCreatureLabels.splice(i, 1);
}
if (tile && tile.hasCreature) {
tile.hasCreature = false;
}
// Defensive: Only remove from mapCreatures and mapCreatureTiles if arrays and index are valid
if (typeof mapCreatures !== "undefined" && Array.isArray(mapCreatures) && i < mapCreatures.length && mapCreatures[i] !== undefined) {
mapCreatures.splice(i, 1);
}
if (typeof mapCreatureTiles !== "undefined" && Array.isArray(mapCreatureTiles) && i < mapCreatureTiles.length && mapCreatureTiles[i] !== undefined) {
mapCreatureTiles.splice(i, 1);
}
continue;
} else if (tile) {
// Reset timer if not explored
tile._creatureDespawnTimer = 0;
}
}
// --- Cubix no longer despawn after 5 minutes (despawn logic removed) ---
// --- Guarantee at least one cubixion (creature) is always visible and collectible on the player's screen (vision area) ---
(function guaranteeCubixionOnScreen() {
// Get player's vision area
var visionRadius = 2;
var px = Math.round(player.gridX);
var py = Math.round(player.gridY);
var foundOnScreen = false;
// Check if any visible, collectible cubixion is in vision
for (var y = py - visionRadius; y <= py + visionRadius; y++) {
for (var x = px - visionRadius; x <= px + visionRadius; x++) {
var tile = gameMap.getTileAt(x, y);
if (tile && tile.hasCreature && tile.visible && !tile._creatureCaught) {
foundOnScreen = true;
break;
}
}
if (foundOnScreen) break;
}
// If not, spawn one in a random visible, collectible tile in vision
if (!foundOnScreen) {
var candidates = [];
for (var y = py - visionRadius; y <= py + visionRadius; y++) {
for (var x = px - visionRadius; x <= px + visionRadius; x++) {
var tile = gameMap.getTileAt(x, y);
// Prevent spawning on player's current tile
if (tile && tile.visible && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual && !tile._creatureCaught && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) {
candidates.push(tile);
}
}
}
if (candidates.length > 0) {
var foundTile = candidates[Math.floor(Math.random() * candidates.length)];
var cType = new Creature().getRandomType(foundTile.biomeType);
var rarityRand = Math.random();
var rarity = "common";
if (rarityRand < 0.60) rarity = "common";else if (rarityRand < 0.85) rarity = "uncommon";else if (rarityRand < 0.96) rarity = "rare";else if (rarityRand < 0.995) rarity = "epic";else rarity = "legendary";
foundTile.hasCreature = true;
mapCreatures.push({
type: cType,
rarity: rarity
});
mapCreatureTiles.push(foundTile);
mapCreatureLabels.push(null);
// Also ensure the visual is created for the cubixion
if (!foundTile._creatureVisual) {
var creatureImageAsset = null;
if (typeof Creature !== "undefined" && Creature.prototype && Creature.prototype.creatureList) {
for (var ci = 0; ci < Creature.prototype.creatureList.length; ci++) {
var c = Creature.prototype.creatureList[ci];
if (c.type === cType) {
if (c.image && LK.assets && LK.assets[c.image]) {
creatureImageAsset = c.image;
}
if (LK.assets && LK.assets["creature_" + c.id + "_" + c.name + "_" + c.type]) {
creatureImageAsset = "creature_" + c.id + "_" + c.name + "_" + c.type;
}
break;
}
}
}
// Fallback to typeCubixion if no image found
if (!creatureImageAsset) {
if (LK.assets && LK.assets[cType + "Cubixion"]) {
creatureImageAsset = cType + "Cubixion";
} else {
creatureImageAsset = "grassCubixion";
}
}
var creatureVisual = LK.getAsset(creatureImageAsset, {
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;
foundTile.addChild(creatureVisual);
foundTile._creatureVisual = creatureVisual;
}
}
}
})();
// --- Guarantee at least one cubix is always visible and collectible on the player's screen (vision area) ---
(function guaranteeCubixOnScreen() {
var visionRadius = 2;
var px = Math.round(player.gridX);
var py = Math.round(player.gridY);
var foundOnScreen = false;
// Check if any visible, collectible cubix is in vision
for (var y = py - visionRadius; y <= py + visionRadius; y++) {
for (var x = px - visionRadius; x <= px + visionRadius; x++) {
var tile = gameMap.getTileAt(x, y);
if (tile && tile._cubixVisual && tile.visible) {
foundOnScreen = true;
break;
}
}
if (foundOnScreen) break;
}
// If not, spawn one in a random visible, collectible tile in vision
if (!foundOnScreen) {
var candidates = [];
for (var y = py - visionRadius; y <= py + visionRadius; y++) {
for (var x = px - visionRadius; x <= px + visionRadius; x++) {
var tile = gameMap.getTileAt(x, y);
// Prevent spawning on player's current tile
if (tile && tile.visible && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) {
candidates.push(tile);
}
}
}
if (candidates.length > 0) {
var foundCubixTile = candidates[Math.floor(Math.random() * candidates.length)];
var rand = Math.random();
var cType = cubixTypes[0];
if (rand > 0.7) cType = cubixTypes[3]; // Legendary: 30%
else if (rand > 0.45) cType = cubixTypes[2]; // Rare: 25%
else if (rand > 0.2) cType = cubixTypes[1]; // Uncommon: 25%
// Basic: 20%
var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
cubixVisual.scale.set(0.5, 0.5);
foundCubixTile.addChild(cubixVisual);
foundCubixTile._cubixVisual = cubixVisual;
cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic");
if (cType && typeof cType.rarity !== "undefined") {
mapCubix.push(cType.rarity);
} else {
mapCubix.push("basic");
}
mapCubixTiles.push(foundCubixTile);
}
}
})();
// Spawn new creatures sometimes, up to max
if (mapCreatures.length < mapCreatureMax && mapCreatureSpawnCooldown <= 0) {
// Try to spawn a new creature
var tries = 0;
// --- New: spawn near player if standing still ---
var spawnNearPlayer = false;
if (typeof lastPlayerGridX !== "undefined" && typeof lastPlayerGridY !== "undefined") {
if (player.gridX === lastPlayerGridX && player.gridY === lastPlayerGridY) {
// 30% chance to try to spawn near player if standing still
if (Math.random() < 0.3) spawnNearPlayer = true;
}
}
if (spawnNearPlayer) {
// Try to spawn in a random adjacent tile (including diagonals)
var px = Math.round(player.gridX);
var py = Math.round(player.gridY);
var adjacents = [];
for (var dx = -1; dx <= 1; dx++) {
for (var dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue;
var nx = px + dx;
var ny = py + dy;
if (nx >= 0 && nx < MAP_WIDTH && ny >= 0 && ny < MAP_HEIGHT) {
var t = gameMap.getTileAt(nx, ny);
if (t && !t.explored && !t.hasCreature && t.biomeType !== "dark") {
adjacents.push(t);
}
}
}
}
if (adjacents.length > 0) {
var tile = adjacents[Math.floor(Math.random() * adjacents.length)];
// Mark tile as having a creature
tile.hasCreature = true;
var cType = new Creature().getRandomType(tile.biomeType);
mapCreatures.push(cType);
mapCreatureTiles.push(tile);
mapCreatureLabels.push(null);
// Only one spawn near player per update
tries = 15;
}
}
// Only spawn cubixion (creature) in player's nearest area (vision radius)
var px = Math.round(player.gridX);
var py = Math.round(player.gridY);
var visionRadius = 2;
var spawnCandidates = [];
for (var y = py - visionRadius; y <= py + visionRadius; y++) {
for (var x = px - visionRadius; x <= px + visionRadius; x++) {
var tile = gameMap.getTileAt(x, y);
var crossable = tile && tile.biomeType !== "dark";
// Prevent spawning on player's current tile
if (tile && !tile.hasCreature && crossable && !tile._cubixVisual && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) {
spawnCandidates.push(tile);
}
}
}
var tries = 0;
while (tries < 200 && mapCreatures.length < mapCreatureMax && spawnCandidates.length > 0) {
// Pick a random candidate tile near player
var tile = spawnCandidates[Math.floor(Math.random() * spawnCandidates.length)];
// Mark tile as having a creature
tile.hasCreature = true;
// Pick a random type for this biome
var cType = new Creature().getRandomType(tile.biomeType);
// Assign rarity with much higher chance for rare/epic/legendary
var rarityRand = Math.random();
var rarity = "common";
if (rarityRand < 0.45) rarity = "common";else if (rarityRand < 0.7) rarity = "uncommon";else if (rarityRand < 0.88) rarity = "rare";else if (rarityRand < 0.97) rarity = "epic";else rarity = "legendary";
mapCreatures.push({
type: cType,
rarity: rarity
});
mapCreatureTiles.push(tile);
mapCreatureLabels.push(null);
// Remove this tile from candidates so we don't double-spawn
var idx = spawnCandidates.indexOf(tile);
if (idx !== -1) spawnCandidates.splice(idx, 1);
tries++;
}
// Reduce cooldown for much faster respawn (was 180 + Math.floor(Math.random() * 120))
mapCreatureSpawnCooldown = 20 + Math.floor(Math.random() * 10); // 0.33-0.5s, much faster
} else {
mapCreatureSpawnCooldown--;
}
// --- Cubix spawn logic with max 30, image, and no despawn ---
if (typeof mapCubix === "undefined") {
mapCubix = [];
mapCubixTiles = [];
mapCubixMax = 30; // Max cubix on map at once
mapCubixSpawnCooldown = 0;
}
// Remove despawn logic: cubix will not despawn anymore
// --- Guarantee at least one cubix is always visible on the map ---
// If there are no cubix spawns, guarantee at least one lowest rarity cubix is spawned
if (mapCubix.length === 0) {
// Find a random valid tile for lowest rarity cubix
var foundTile = null;
for (var attempt = 0; attempt < 200; attempt++) {
var gx = Math.floor(Math.random() * MAP_WIDTH);
var gy = Math.floor(Math.random() * MAP_HEIGHT);
var tile = gameMap.getTileAt(gx, gy);
var crossable = tile && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual;
// Prevent spawning on player's current tile
if (tile && crossable && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) {
foundTile = tile;
break;
}
}
if (foundTile) {
// Always spawn a random cubix (any rarity) if map is empty
var rand = Math.random();
var cType = cubixTypes[0];
if (rand > 0.7) cType = cubixTypes[3]; // Legendary: 30%
else if (rand > 0.45) cType = cubixTypes[2]; // Rare: 25%
else if (rand > 0.2) cType = cubixTypes[1]; // Uncommon: 25%
// Basic: 20%
var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
cubixVisual.scale.set(0.5, 0.5);
foundTile.addChild(cubixVisual);
foundTile._cubixVisual = cubixVisual;
cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic");
if (cType && typeof cType.rarity !== "undefined") {
mapCubix.push(cType.rarity);
} else {
mapCubix.push("basic");
}
mapCubixTiles.push(foundTile);
}
}
// Continue normal spawn logic, but never exceed max 30
if (mapCubix.length < mapCubixMax && mapCubixSpawnCooldown <= 0) {
var tries = 0;
// --- New: spawn cubix near player if standing still ---
var spawnCubixNearPlayer = false;
if (typeof lastPlayerGridX !== "undefined" && typeof lastPlayerGridY !== "undefined") {
if (player.gridX === lastPlayerGridX && player.gridY === lastPlayerGridY) {
// 30% chance to try to spawn cubix near player if standing still
if (Math.random() < 0.3) spawnCubixNearPlayer = true;
}
}
if (spawnCubixNearPlayer) {
// Try to spawn in a random adjacent tile (including diagonals)
var px = Math.round(player.gridX);
var py = Math.round(player.gridY);
var adjacents = [];
for (var dx = -1; dx <= 1; dx++) {
for (var dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue;
var nx = px + dx;
var ny = py + dy;
if (nx >= 0 && nx < MAP_WIDTH && ny >= 0 && ny < MAP_HEIGHT) {
var t = gameMap.getTileAt(nx, ny);
var crossable = t && t.biomeType !== "dark" && !t.hasCreature && !t._cubixVisual;
if (t && !t.explored && crossable) {
adjacents.push(t);
}
}
}
}
if (adjacents.length > 0) {
var tile = adjacents[Math.floor(Math.random() * adjacents.length)];
// Pick a random cubix type (weighted: much more rare/legendary spawns)
var rand = Math.random();
var cType = cubixTypes[0];
if (rand > 0.7) cType = cubixTypes[3]; // Legendary: 30%
else if (rand > 0.45) cType = cubixTypes[2]; // Rare: 25%
else if (rand > 0.2) cType = cubixTypes[1]; // Uncommon: 25%
// Basic: 20%
// Show cubix on tile with correct rarity image
var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
cubixVisual.scale.set(0.5, 0.5);
tile.addChild(cubixVisual);
tile._cubixVisual = cubixVisual;
cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic");
if (cType && typeof cType.rarity !== "undefined") {
mapCubix.push(cType.rarity);
} else {
mapCubix.push("basic");
}
mapCubixTiles.push(tile);
// Only one spawn near player per update
tries = 60;
}
}
// Increase number of tries and increase spawn rate for more cubix spawns
// Make cubix spawn rate 10x higher than cubixion by increasing tries 10x
// Spawn cubix in a much wider area and for all types (not just adjacent or limited tiles)
// Check if player has no cubix of any type
var playerHasNoCubix = true;
if (typeof playerCubix !== "undefined") {
for (var i = 0; i < cubixTypes.length; i++) {
if ((playerCubix[cubixTypes[i].rarity] || 0) > 0) {
playerHasNoCubix = false;
break;
}
}
}
while (tries < 400 && mapCubix.length < mapCubixMax) {
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 dark, not already with a cubix or creature
var crossable = tile && tile.biomeType !== "dark" && !tile.hasCreature && !tile._cubixVisual;
// Prevent spawning on player's current tile
if (tile && crossable && !(tile.gridX === Math.round(player.gridX) && tile.gridY === Math.round(player.gridY))) {
// If player has no cubix, increase spawn chance for basic cubix
if (playerHasNoCubix) {
// 80% chance to spawn a basic cubix, 20% chance to spawn other types
if (Math.random() < 0.8) {
var cType = cubixTypes[0]; // basic
} else {
var cType = cubixTypes[Math.floor(Math.random() * cubixTypes.length)];
}
// Always spawn if player has no cubix (skip random chance)
var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
cubixVisual.scale.set(0.5, 0.5);
tile.addChild(cubixVisual);
tile._cubixVisual = cubixVisual;
cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic");
if (cType && typeof cType.rarity !== "undefined") {
mapCubix.push(cType.rarity);
} else {
mapCubix.push("basic");
}
mapCubixTiles.push(tile);
} else {
// Greatly increase spawn chance per tile (much higher for all types/rarities)
if (Math.random() < 0.98) {
// Instead of only using weighted cubixTypes, allow all types to spawn
// Pick a random cubix type from all available types
var cType = cubixTypes[Math.floor(Math.random() * cubixTypes.length)];
// Show cubix on tile with correct rarity image
var cubixVisual = LK.getAsset('cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic"), {
anchorX: 0.5,
anchorY: 0.5,
x: TILE_SIZE / 2,
y: TILE_SIZE / 2
});
cubixVisual.scale.set(0.5, 0.5);
tile.addChild(cubixVisual);
tile._cubixVisual = cubixVisual;
cubixVisual.assetId = 'cubix_' + (cType && typeof cType.rarity !== "undefined" ? cType.rarity : "basic");
if (cType && typeof cType.rarity !== "undefined") {
mapCubix.push(cType.rarity);
} else {
mapCubix.push("basic");
}
mapCubixTiles.push(tile);
}
}
}
tries++;
}
// Reduce cooldown for much faster respawn (was 120 + Math.floor(Math.random() * 120))
mapCubixSpawnCooldown = 10 + Math.floor(Math.random() * 10); // 0.16-0.33s, much faster
} else {
mapCubixSpawnCooldown--;
}
// --- End creature spawn/despawn logic ---
// --- Cubix collection logic ---
// Always allow collection if player is on the tile with cubix (any type/rarity)
var playerTile = gameMap.getTileAt(player.gridX, player.gridY);
if (playerTile && playerTile._cubixVisual) {
var foundType = null;
for (var i = 0; i < cubixTypes.length; i++) {
if (playerTile._cubixVisual.assetId === 'cubix_' + cubixTypes[i].rarity) {
foundType = cubixTypes[i];
break;
}
}
if (foundType) {
// Add to inventory (bag) for use in catching cubixion
if (!playerCubix[foundType.rarity]) playerCubix[foundType.rarity] = 0;
playerCubix[foundType.rarity]++;
// Defensive: always sync playerCubix to storage and global scope after change
syncPlayerCubix();
// Remove cubix from map (despawn)
if (playerTile._cubixVisual && playerTile.children.indexOf(playerTile._cubixVisual) !== -1) {
playerTile.removeChild(playerTile._cubixVisual);
}
playerTile._cubixVisual = null;
// Remove from arrays for optimization
if (typeof mapCubixTiles !== "undefined" && typeof mapCubix !== "undefined") {
for (var i = mapCubixTiles.length - 1; i >= 0; i--) {
// Defensive: check mapCubixTiles[i] is defined before comparing
if (typeof mapCubixTiles[i] !== "undefined" && mapCubixTiles[i] === playerTile) {
mapCubix.splice(i, 1);
mapCubixTiles.splice(i, 1);
break;
}
}
}
// Show a message
var msg = new Text2('Collected ' + foundType.name.replace(' Cubix', 'ion') + '!', {
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);
// --- Add to Cubixion collection menu if not already present ---
if (typeof playerCubixionCollection === "undefined") playerCubixionCollection = {};
// Use a synthetic id for cubix type (e.g. rarity as id)
var cubixionId = foundType.rarity;
if (!playerCubixionCollection[cubixionId]) {
playerCubixionCollection[cubixionId] = {
count: 0,
gems: 0
};
}
playerCubixionCollection[cubixionId].count++;
// Defensive: always sync to storage and window
if (typeof storage !== "undefined" && typeof storage.set === "function") {
storage.set("playerCubixionCollection", playerCubixionCollection);
} else if (typeof storage !== "undefined") {
try {
storage.playerCubixionCollection = playerCubixionCollection;
} catch (e) {}
}
if (typeof window !== "undefined") window.playerCubixionCollection = playerCubixionCollection;
}
}
// 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 6th update for further optimization
if (typeof minimapUpdateCounter === "undefined") minimapUpdateCounter = 0;
minimapUpdateCounter++;
if (minimapUpdateCounter % 6 === 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 so it doesn't respawn immediately
currentTile.hasCreature = false;
currentTile._creatureCaught = true;
// Remove indicator from tile and all references for optimization
if (typeof mapCreatureTiles !== "undefined" && typeof mapCreatures !== "undefined" && typeof mapCreatureLabels !== "undefined") {
for (var k = mapCreatureTiles.length - 1; k >= 0; k--) {
if (mapCreatureTiles[k] === currentTile) {
// Remove label if present
if (mapCreatureLabels[k]) {
currentTile.removeChild(mapCreatureLabels[k]);
mapCreatureLabels.splice(k, 1);
}
// Remove creature visual if present
if (currentTile._creatureVisual) {
currentTile.removeChild(currentTile._creatureVisual);
currentTile._creatureVisual = null;
}
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