/****
* 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