User prompt
make a world enter menu
User prompt
make a world for the character
User prompt
make a character
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'placeToolBtn.buttonText.style.fill = "#FFFF00";' Line Number: 468
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading '14')' in or related to this line: 'if (worldData[gridY][gridX] !== null) {' Line Number: 514
User prompt
add a main menu ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Pixel Realms
Initial prompt
make a game like pixel worlds
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { worldData: "undefined", playerPosition: { x: 1024, y: 1600 }, inventory: {} }); /**** * Classes ****/ var Block = Container.expand(function (type, size) { var self = Container.call(this); self.type = type; self.size = size || 64; // Use 'bedrock' skin for bedrock blocks, otherwise use type as asset var assetType = type === 'bedrock' ? 'bedrock' : type; var graphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5, width: self.size, height: self.size }); self.down = function (x, y, obj) { if (currentTool === 'break') { self.breakBlock(); } }; self.breakBlock = function () { // Make main door and bedrock blocks unbreakable if (self.type === 'main_door' || self.type === 'bedrock') { // Optionally play a sound or show a message here return; } // Add to inventory inventory[self.type] = (inventory[self.type] || 0) + 1; updateInventoryDisplay(); // Play break sound LK.getSound('break').play(); // Remove from world var gridX = Math.floor(self.x / blockSize); var gridY = Math.floor(self.y / blockSize); if (worldData[gridY] && worldData[gridY][gridX] !== undefined && worldData[gridY][gridX] !== null) { worldData[gridY][gridX] = null; self.destroy(); } // Save world data // Convert worldData to string before storing storage.worldData = JSON.stringify(worldData); }; return self; }); // --- Butterfly Class --- var Butterfly = Container.expand(function () { var self = Container.call(this); // Use 'Leaf' as a placeholder for butterfly, tint for color var butterflyColors = [0xffb347, 0xff69b4, 0x87ceeb, 0x7fff00, 0xffffff]; var color = butterflyColors[Math.floor(Math.random() * butterflyColors.length)]; var sprite = self.attachAsset('Leaf', { anchorX: 0.5, anchorY: 0.5, width: 40, height: 30, tint: color, alpha: 0.85 }); self.x = Math.random() * 2048; self.y = 600 + Math.random() * 800; self.vx = (Math.random() - 0.5) * 3; self.vy = (Math.random() - 0.5) * 1.5; self.flapTime = 0; self.update = function () { // Fluttering movement self.x += self.vx + Math.sin(LK.ticks / 10 + self.y) * 0.5; self.y += self.vy + Math.cos(LK.ticks / 15 + self.x) * 0.3; // Flap wings (scale) self.flapTime += 0.2 + Math.random() * 0.1; sprite.scale.y = 0.8 + Math.sin(self.flapTime) * 0.3; // Stay in bounds if (self.x < 0) self.x = 0, self.vx *= -1; if (self.x > 2048) self.x = 2048, self.vx *= -1; if (self.y < 400) self.y = 400, self.vy *= -1; if (self.y > 1800) self.y = 1800, self.vy *= -1; // Fade out at night var t = dayNightCycle.time; var isDay = t > 0.18 && t < 0.82; self.alpha = isDay ? 1 : 0; }; return self; }); // --- Butterfly Spawner --- var Button = Container.expand(function (text, callback) { var self = Container.call(this); var background = self.attachAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5 }); self.buttonText = new Text2(text, { size: 36, fill: 0xFFFFFF }); self.buttonText.anchor.set(0.5, 0.5); self.addChild(self.buttonText); self.callback = callback; self.down = function (x, y, obj) { if (self.callback) { self.up = function (x, y, obj) { // Animate button release tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOutQuad }); }; tween(self, { scaleX: 0.92, scaleY: 0.92 }, { duration: 80, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeOutQuad }); } }); self.callback(); } }; return self; }); var Character = Container.expand(function () { var self = Container.call(this); // Add shadow for polish var shadow = self.attachAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, width: 70, height: 20, y: 0, x: 0, alpha: 0.25 }); shadow.y = 0; shadow.x = 0; // Create character sprite var sprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 1.0, width: 80, height: 100 }); // Character properties self.direction = 1; // 1 = right, -1 = left self.isMoving = false; self.speed = 4; // Animation properties self.frameTime = 0; self.frameRate = 8; // frames per second // Change direction method self.faceDirection = function (dir) { if (dir !== self.direction) { self.direction = dir; sprite.scale.x = self.direction; } }; // Start/stop movement self.startMoving = function (dir) { self.isMoving = true; self.faceDirection(dir); }; self.stopMoving = function () { self.isMoving = false; }; // Update method - called every frame self.update = function () { // Store last position for collision detection var lastY = self.y; var lastX = self.x; // Handle movement if (self.isMoving) { self.x += self.speed * self.direction; // Update animation self.frameTime += 1 / 60; if (self.frameTime >= 1 / self.frameRate) { self.frameTime = 0; // We would change animation frame here if we had sprite frames } } // Keep character in bounds if (self.x < blockSize / 2) self.x = blockSize / 2; if (self.x > worldWidth * blockSize - blockSize / 2) self.x = worldWidth * blockSize - blockSize / 2; if (self.y < blockSize / 2) self.y = blockSize / 2; if (self.y > worldHeight * blockSize - blockSize / 2) self.y = worldHeight * blockSize - blockSize / 2; // --- Begin Physics for Character --- if (typeof self.velocityY === "undefined") self.velocityY = 0; if (typeof self.lastOnGround === "undefined") self.lastOnGround = false; // Gravity var gravityForce = 1.2; self.velocityY += gravityForce; // Limit max fall speed if (self.velocityY > 32) self.velocityY = 32; // --- Horizontal collision check before moving horizontally --- var nextX = self.x; if (self.isMoving) { nextX += self.speed * self.direction; // Check for block at new horizontal position (character's feet and head) var checkGridYFeet = Math.floor((self.y - 10) / blockSize); var checkGridYHead = Math.floor((self.y - 90) / blockSize); var checkGridX = Math.floor(nextX / blockSize); var blocked = false; // Check feet if (checkGridYFeet >= 0 && checkGridYFeet < worldData.length && checkGridX >= 0 && checkGridX < worldData[checkGridYFeet].length && worldData[checkGridYFeet][checkGridX] && worldData[checkGridYFeet][checkGridX] !== 'water') { blocked = true; } // Check head if (checkGridYHead >= 0 && checkGridYHead < worldData.length && checkGridX >= 0 && checkGridX < worldData[checkGridYHead].length && worldData[checkGridYHead][checkGridX] && worldData[checkGridYHead][checkGridX] !== 'water') { blocked = true; } if (!blocked) { self.x = nextX; } else { self.isMoving = false; } // Update animation self.frameTime += 1 / 60; if (self.frameTime >= 1 / self.frameRate) { self.frameTime = 0; // We would change animation frame here if we had sprite frames } } // Move vertically self.y += self.velocityY; // --- Vertical collision check after moving vertically --- var gridX = Math.floor(self.x / blockSize); var gridY = Math.floor((self.y + 10) / blockSize); var onGround = false; for (var checkX = gridX - 1; checkX <= gridX + 1; checkX++) { if (checkX >= 0 && gridY >= 0 && gridY < worldData.length && checkX < (worldData[gridY] ? worldData[gridY].length : 0) && worldData[gridY] && worldData[gridY][checkX] && worldData[gridY][checkX] !== 'water') { var distX = Math.abs(self.x - (checkX * blockSize + blockSize / 2)); if (distX < blockSize * 0.8) { // Snap to top of block self.y = gridY * blockSize - 10; self.velocityY = 0; onGround = true; break; } } } // Check if we just landed on a platform if (!self.lastOnGround && onGround) { self.velocityY = 0; self.y = Math.floor(self.y / blockSize) * blockSize - 10; } self.lastOnGround = onGround; // --- End Physics for Character --- // Prevent character from being inside a block vertically (head stuck in block above) var gridYHead = Math.floor((self.y - 90) / blockSize); if (gridYHead >= 0 && gridYHead < worldData.length && gridX >= 0 && gridX < worldData[gridYHead].length && worldData[gridYHead][gridX] && worldData[gridYHead][gridX] !== 'water') { // Push character down just below the block self.y = (gridYHead + 1) * blockSize + 90; self.velocityY = Math.max(self.velocityY, 0); } // Check for horizontal collisions (legacy, now handled above, but keep for safety) if (lastX !== self.x) { var checkGridY = Math.floor((self.y - 50) / blockSize); // Check at character's mid height var newGridX = Math.floor(self.x / blockSize); if (checkGridY >= 0 && checkGridY < worldData.length && newGridX >= 0 && newGridX < worldData[checkGridY].length && worldData[checkGridY][newGridX] && worldData[checkGridY][newGridX] !== 'water') { self.x = lastX; self.isMoving = false; } } }; // Interactive events self.down = function (x, y, obj) { // Handle touch down on character self.startDrag(); // Convert to world container's coordinate system for proper positioning var worldX = obj.parent.toGlobal(obj.position).x; var worldY = obj.parent.toGlobal(obj.position).y; // Determine initial direction based on touch position if (x > self.width / 2) { self.faceDirection(1); // Face right } else { self.faceDirection(-1); // Face left } }; // Add touch controls for character self.startDrag = function () { self.isDragging = true; }; self.stopDrag = function () { self.isDragging = false; self.isMoving = false; }; // Add jump functionality self.jump = function () { // Find where the player is standing var gridX = Math.floor(self.x / blockSize); var gridY = Math.floor((self.y + 10) / blockSize); // Check if character is on ground var onGround = false; for (var checkX = gridX - 1; checkX <= gridX + 1; checkX++) { if (checkX >= 0 && gridY >= 0 && gridY < worldData.length && checkX < (worldData[gridY] ? worldData[gridY].length : 0) && worldData[gridY] && worldData[gridY][checkX] && worldData[gridY][checkX] !== 'water') { var distX = Math.abs(self.x - (checkX * blockSize + blockSize / 2)); if (distX < blockSize * 0.8) { onGround = true; break; } } } if (onGround) { // Apply upward force when jumping self.velocityY = -22; } }; self.drag = function (x, y) { if (self.isDragging) { // Convert global coordinates to character's local frame var localX = x - game.toLocal(self.parent.toGlobal(self.position)).x + self.x; // Calculate difference for movement threshold var diffX = localX - self.x; // Determine direction based on current position and touch position if (diffX > 10) { self.faceDirection(1); self.isMoving = true; } else if (diffX < -10) { self.faceDirection(-1); self.isMoving = true; } else { self.isMoving = false; } } }; return self; }); var ControlButton = Container.expand(function (text, callback, width, height) { var self = Container.call(this); width = width || 150; height = height || 150; // Determine control type and image asset self.controlType = ''; var imageAsset = null; if (text === '←') { self.controlType = 'left'; imageAsset = 'LeftButton'; } else if (text === '→') { self.controlType = 'right'; imageAsset = 'RightButton'; } else if (text === '↑') { self.controlType = 'jump'; imageAsset = 'UpButton'; } else if (text === '■') { self.controlType = 'stop'; imageAsset = null; } // Create button background (use image for direction, shape for stop) var background; if (imageAsset) { background = self.attachAsset(imageAsset, { anchorX: 0.5, anchorY: 0.5, width: width, height: height }); } else { background = self.attachAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, width: width, height: height }); } // Only show text for stop button, not for direction buttons if (!imageAsset) { self.buttonText = new Text2(text, { size: 48, fill: 0xFFFFFF }); self.buttonText.anchor.set(0.5, 0.5); self.addChild(self.buttonText); } // Store callback function self.callback = callback; // Button interaction self.down = function (x, y, obj) { if (self.callback) { // Animate button press tween(self, { scaleX: 0.92, scaleY: 0.92 }, { duration: 80, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.easeOutQuad }); } }); background.alpha = 0.7; self.callback(); // Set control state for continuous button press if (self.controlType && controlState) { if (self.controlType === 'left') { controlState.left = true; controlState.right = false; } else if (self.controlType === 'right') { controlState.right = true; controlState.left = false; } else if (self.controlType === 'jump') { controlState.jump = true; } else if (self.controlType === 'stop') { controlState.left = false; controlState.right = false; } } } }; self.up = function (x, y, obj) { background.alpha = 1.0; // Animate button release tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeOutQuad }); // Clear control state when button is released if (self.controlType && controlState) { if (self.controlType === 'left') { controlState.left = false; } else if (self.controlType === 'right') { controlState.right = false; } else if (self.controlType === 'jump') { controlState.jump = false; } } }; return self; }); var InventorySlot = Container.expand(function (index, type) { var self = Container.call(this); self.index = index; self.type = type; var background = self.attachAsset('inventorySlot', { anchorX: 0.5, anchorY: 0.5 }); if (type) { var blockIcon = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60 }); } self.countText = new Text2("0", { size: 24, fill: 0xFFFFFF }); self.countText.anchor.set(1, 1); self.countText.x = 30; self.countText.y = 30; self.addChild(self.countText); self.updateCount = function (count) { self.countText.setText(count.toString()); }; self.down = function (x, y, obj) { selectInventorySlot(self.index); }; return self; }); var MainMenu = Container.expand(function () { var self = Container.call(this); // Create a semi-transparent background overlay var overlay = LK.getAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, alpha: 0.7 }); overlay.tint = 0x000000; self.addChild(overlay); // Game title self.title = new Text2("Pixel Realms", { size: 120, fill: 0xFFFFFF }); self.title.anchor.set(0.5, 0.5); self.title.x = 2048 / 2; self.title.y = 800; self.addChild(self.title); // Start game button self.startButton = new Button("Start Game", function () { self.hide(); worldEnterMenu.show(); }); self.startButton.x = 2048 / 2; self.startButton.y = 1200; self.addChild(self.startButton); // Add animated elements self.show = function () { self.visible = true; // Reset positions for animation self.title.y = 600; self.title.alpha = 0; self.startButton.y = 1300; self.startButton.alpha = 0; // Animate title tween(self.title, { y: 800, alpha: 1 }, { duration: 800, easing: tween.easeOutBack }); // Animate button with slight delay tween(self.startButton, { y: 1200, alpha: 1 }, { duration: 800, easing: tween.easeOutBack }); }; self.hide = function () { // Animate out tween(self.title, { y: 600, alpha: 0 }, { duration: 500, easing: tween.easeInBack }); tween(self.startButton, { y: 1300, alpha: 0 }, { duration: 500, easing: tween.easeInBack, onFinish: function onFinish() { self.visible = false; } }); }; return self; }); var Platform = Container.expand(function (width, height) { var self = Container.call(this); self.width = width || 5; self.height = height || 1; // Create platform blocks for (var x = 0; x < self.width; x++) { for (var y = 0; y < self.height; y++) { var block = new Block('stone', blockSize); block.x = x * blockSize + blockSize / 2; block.y = y * blockSize + blockSize / 2; self.addChild(block); } } return self; }); var Player = Container.expand(function () { var self = Container.call(this); // Add shadow for polish var shadow = self.attachAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, width: 50, height: 18, y: 0, x: 0, alpha: 0.22 }); shadow.y = 0; shadow.x = 0; var graphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 1, width: 60, height: 120 }); // Movement properties self.speed = 5; self.velocity = { x: 0, y: 0 }; self.gravity = 0.5; self.jumping = false; self.grounded = false; self.move = function (direction) { if (direction === 'left') { self.velocity.x = -self.speed; } else if (direction === 'right') { self.velocity.x = self.speed; } }; self.stopMove = function () { self.velocity.x = 0; }; self.jump = function () { if (self.grounded) { self.velocity.y = -22; self.grounded = false; self.jumping = true; } }; self.update = function () { // --- Begin Physics for Player --- if (typeof self.velocity === "undefined") self.velocity = { x: 0, y: 0 }; if (typeof self.gravity === "undefined") self.gravity = 1.2; if (typeof self.grounded === "undefined") self.grounded = false; if (!self.grounded) { self.velocity.y += self.gravity; if (self.velocity.y > 32) self.velocity.y = 32; } self.x += self.velocity.x; self.y += self.velocity.y; // --- End Physics for Player --- // Prevent player from leaving world boundaries if (self.x < blockSize / 2) self.x = blockSize / 2; if (self.x > worldWidth * blockSize - blockSize / 2) self.x = worldWidth * blockSize - blockSize / 2; if (self.y < blockSize / 2) self.y = blockSize / 2; if (self.y > worldHeight * blockSize - blockSize / 2) self.y = worldHeight * blockSize - blockSize / 2; // Check for ground collision var gridX = Math.floor(self.x / blockSize); var gridY = Math.floor((self.y + 10) / blockSize); // Simple collision check with the block below if (gridY >= 0 && gridY < worldData.length && gridX >= 0 && worldData[gridY] && gridX < worldData[gridY].length && worldData[gridY][gridX] && worldData[gridY][gridX] !== 'water') { if (self.velocity.y > 0) { self.y = gridY * blockSize - 10; self.velocity.y = 0; self.grounded = true; self.jumping = false; } } else { self.grounded = false; } // Update camera to follow player updateCamera(); // Save player position periodically if (LK.ticks % 60 === 0) { storage.playerPosition = { x: self.x, y: self.y }; } }; return self; }); var WorldEnterMenu = Container.expand(function () { var self = Container.call(this); // Create a semi-transparent background var overlay = LK.getAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, alpha: 0.8 }); overlay.tint = 0x000022; self.addChild(overlay); // World title self.title = new Text2("Enter World", { size: 100, fill: 0xFFFFFF }); self.title.anchor.set(0.5, 0.5); self.title.x = 2048 / 2; self.title.y = 600; self.addChild(self.title); // World description self.description = new Text2("Select your world to start building", { size: 48, fill: 0xCCCCCC }); self.description.anchor.set(0.5, 0.5); self.description.x = 2048 / 2; self.description.y = 720; self.addChild(self.description); // World selection buttons self.createWorldButton = new Button("Create New World", function () { // Prompt for world name (simple prompt, can be improved) var worldName = "world" + Math.floor(Math.random() * 10000); // Generate new world and save with name var newWorldData = generateWorld(); pixelWorlds.saveWorld(worldName, newWorldData); worldData = newWorldData; pixelWorlds.currentWorld = worldName; // Reset player position player.x = 1024; player.y = 1600; storage.playerPosition = { x: player.x, y: player.y }; // Move character to main door entrance (bottom center) on new world creation if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") { setCharacterToMainDoor(); } // Reset inventory inventory = { "grass": 10, "stone": 10, "wood": 10, "water": 5 }; storage.inventory = inventory; // Start game self.hide(); gameStarted = true; initializeWorld(); setupUI(); }); self.createWorldButton.x = 2048 / 2; self.createWorldButton.y = 1000; self.addChild(self.createWorldButton); // Add a world selection menu for pixel worlds self.loadWorldButton = new Button("Select World", function () { // Show a simple world selection overlay var overlay = new Container(); var bg = LK.getAsset('buttonBg', { anchorX: 0.5, anchorY: 0.5, width: 1200, height: 1000, alpha: 0.95 }); bg.tint = 0x222244; overlay.addChild(bg); overlay.x = 2048 / 2; overlay.y = 1200; // List all worlds var worlds = pixelWorlds.listWorlds(); if (worlds.length === 0) { var noWorlds = new Text2("No worlds found", { size: 60, fill: 0xffffff }); noWorlds.anchor.set(0.5, 0.5); noWorlds.y = 0; overlay.addChild(noWorlds); } else { for (var i = 0; i < worlds.length; i++) { (function (worldName, idx) { var btn = new Button(worldName, function () { // Switch to selected world pixelWorlds.switchWorld(worldName); // Move character to main door entrance (bottom center) on world select if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") { setCharacterToMainDoor(); } overlay.visible = false; self.hide(); gameStarted = true; }); btn.x = 0; btn.y = -400 + idx * 120; overlay.addChild(btn); })(worlds[i], i); } } // Add close button var closeBtn = new Button("Close", function () { overlay.visible = false; }); closeBtn.x = 0; closeBtn.y = 500; overlay.addChild(closeBtn); LK.gui.center.addChild(overlay); }); self.loadWorldButton.x = 2048 / 2; self.loadWorldButton.y = 1150; self.addChild(self.loadWorldButton); self.backButton = new Button("Back to Menu", function () { self.hide(); mainMenu.show(); }); self.backButton.x = 2048 / 2; self.backButton.y = 1400; self.addChild(self.backButton); // Show animation self.show = function () { self.visible = true; // Reset positions for animation self.title.y = 400; self.title.alpha = 0; self.description.y = 620; self.description.alpha = 0; self.createWorldButton.y = 1100; self.createWorldButton.alpha = 0; self.loadWorldButton.y = 1250; self.loadWorldButton.alpha = 0; self.backButton.y = 1500; self.backButton.alpha = 0; // Animate title tween(self.title, { y: 600, alpha: 1 }, { duration: 600, easing: tween.easeOutBack }); // Animate description tween(self.description, { y: 720, alpha: 1 }, { duration: 600, delay: 100, easing: tween.easeOutBack }); // Animate buttons tween(self.createWorldButton, { y: 1000, alpha: 1 }, { duration: 600, delay: 200, easing: tween.easeOutBack }); tween(self.loadWorldButton, { y: 1150, alpha: 1 }, { duration: 600, delay: 300, easing: tween.easeOutBack }); tween(self.backButton, { y: 1400, alpha: 1 }, { duration: 600, delay: 400, easing: tween.easeOutBack }); }; // Hide animation self.hide = function () { // Animate out tween(self.title, { y: 400, alpha: 0 }, { duration: 400, easing: tween.easeInBack }); tween(self.description, { y: 620, alpha: 0 }, { duration: 400, easing: tween.easeInBack }); tween(self.createWorldButton, { y: 1100, alpha: 0 }, { duration: 400, easing: tween.easeInBack }); tween(self.loadWorldButton, { y: 1250, alpha: 0 }, { duration: 400, easing: tween.easeInBack }); tween(self.backButton, { y: 1500, alpha: 0 }, { duration: 400, easing: tween.easeInBack, onFinish: function onFinish() { self.visible = false; } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue background }); /**** * Game Code ****/ // Game constants // Make sure JSON is defined for storage operations //{bedrock_skin} function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } var JSON = { parse: function parse(text) { return LK.parseJSON(text); }, stringify: function stringify(obj) { // Convert JavaScript object to a JSON string var str = ''; if (obj === null) return 'null'; if (typeof obj === 'undefined') return undefined; if (typeof obj === 'string') return '"' + obj.replace(/"/g, '\\"') + '"'; if (typeof obj === 'number' || typeof obj === 'boolean') return obj.toString(); if (Array.isArray(obj)) { str = '['; for (var i = 0; i < obj.length; i++) { str += (i > 0 ? ',' : '') + JSON.stringify(obj[i]); } return str + ']'; } if (_typeof(obj) === 'object') { str = '{'; var keys = Object.keys(obj); for (var i = 0; i < keys.length; i++) { var key = keys[i]; str += (i > 0 ? ',' : '') + '"' + key + '":' + JSON.stringify(obj[key]); } return str + '}'; } return ''; } }; var WorldGenerator = function WorldGenerator() { // Generate a new world with more advanced terrain features this.generate = function (width, height, blockSize) { var data = []; // Initialize with empty arrays for (var y = 0; y < height; y++) { data[y] = []; for (var x = 0; x < width; x++) { data[y][x] = null; } } // Add bedrock border (unbreakable) only at the bottom of the world for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { if (y === height - 1) { data[y][x] = 'bedrock'; } } } // Generate terrain using simple noise var terrain = this.generateTerrain(width, height); // Apply terrain to world data for (var x = 0; x < width; x++) { var groundHeight = Math.floor(terrain[x] * height * 0.4) + Math.floor(height * 0.5); // Fill all blocks below the surface with 'ground' (dirt) for (var y = groundHeight; y < height; y++) { if (y === groundHeight) { // Don't overwrite bedrock border if (data[y][x] !== 'bedrock') data[y][x] = 'grass'; } else { if (data[y][x] !== 'bedrock') data[y][x] = 'ground'; } } // Generate caves this.generateCaves(data, x, groundHeight, height); } // Add trees this.addTrees(data, width, height); // Add water bodies this.addWater(data, width, height); return data; }; // Generate basic terrain height map using simple noise this.generateTerrain = function (width, height) { var terrain = []; var smoothness = 8; // Higher = smoother terrain // Start with random height terrain[0] = Math.random(); // Generate rest using midpoint displacement-like approach for (var x = 1; x < width; x++) { // Get previous height with some randomness var prevHeight = terrain[x - 1]; var randomFactor = (Math.random() - 0.5) * 2 / smoothness; // New height with constraints terrain[x] = Math.max(0.1, Math.min(0.9, prevHeight + randomFactor)); // Add some hills/mountains occasionally if (Math.random() < 0.05) { var hillHeight = 0.1 + Math.random() * 0.2; var hillWidth = 3 + Math.floor(Math.random() * 5); // Create a hill centered at x for (var h = 0; h < hillWidth && x + h < width; h++) { var offset = hillHeight * Math.sin(h / hillWidth * Math.PI); if (x + h < width) { terrain[x + h] = Math.max(0.1, Math.min(0.9, terrain[x + h] - offset)); } } // Skip ahead x += hillWidth; } } return terrain; }; // Add random caves under the terrain this.generateCaves = function (data, x, groundHeight, height) { // Random caves deeper underground if (groundHeight + 5 < height) { for (var c = 0; c < 2; c++) { if (Math.random() < 0.05) { var caveY = groundHeight + 5 + Math.floor(Math.random() * (height - groundHeight - 10)); var caveSize = 1 + Math.floor(Math.random() * 3); for (var cy = caveY; cy < caveY + caveSize && cy < height; cy++) { if (data[cy] && data[cy][x]) { data[cy][x] = null; } } } } } }; // Add trees on the surface this.addTrees = function (data, width, height) { for (var x = 0; x < width; x++) { // Find the ground level var groundY = 0; for (var y = 0; y < height; y++) { if (data[y] && data[y][x] === 'grass') { groundY = y; break; } } // Add random trees (if we found ground and not at the edge) if (groundY > 0 && x > 2 && x < width - 2 && Math.random() < 0.1) { var treeHeight = 3 + Math.floor(Math.random() * 3); // Remove grass block below the tree (set to null) if (groundY + 1 < height && data[groundY + 1][x] === 'grass') { data[groundY + 1][x] = null; } // Tree trunk for (var ty = 1; ty <= treeHeight; ty++) { if (groundY - ty >= 0) { data[groundY - ty][x] = 'wood'; } } // Tree top (add leaves here) var treeTop = groundY - treeHeight - 1; if (treeTop >= 0) { // Add leaves in a 3x3 area centered on the trunk top for (var ly = -1; ly <= 1; ly++) { for (var lx = -1; lx <= 1; lx++) { var leafX = x + lx; var leafY = treeTop + ly; if (leafX >= 0 && leafX < width && leafY >= 0 && leafY < height && (ly !== 0 || lx !== 0) // Don't overwrite trunk top ) { data[leafY][leafX] = 'Leaf'; // Use 'Leaf' as leaf block } } } // Place trunk top (wood) in the center data[treeTop][x] = 'wood'; // Optionally, add a single leaf block above the trunk top if (treeTop - 1 >= 0) { data[treeTop - 1][x] = 'Leaf'; } } } } }; // Add water bodies (lakes, rivers) this.addWater = function (data, width, height) { // Add some water pools/lakes var lakeAttempts = Math.floor(width / 20); for (var l = 0; l < lakeAttempts; l++) { var lakeX = 5 + Math.floor(Math.random() * (width - 10)); var lakeWidth = 3 + Math.floor(Math.random() * 5); // Find ground level at lakeX var lakeY = 0; for (var y = 0; y < height; y++) { if (data[y] && data[y][lakeX] === 'grass') { lakeY = y; break; } } // Create lake if (lakeY > 0) { for (var wx = 0; wx < lakeWidth && lakeX + wx < width; wx++) { if (data[lakeY] && data[lakeY][lakeX + wx]) { data[lakeY][lakeX + wx] = 'water'; // Sometimes make deeper water if (Math.random() < 0.5 && lakeY + 1 < height) { data[lakeY + 1][lakeX + wx] = 'water'; } } } } } }; return this; }; var blockSize = 64; var worldWidth = 100; // 100 blocks horizontal var worldHeight = 54; // 54 blocks vertical var gravity = 0; // Game state var gameStarted = false; // Parse worldData from storage if it exists, checking for undefined/null values // Pixel World System: Support for multiple worlds and world switching var pixelWorlds = { currentWorld: "default", worlds: {}, // Load world data from storage or generate new loadWorld: function loadWorld(worldName) { var key = "pixelWorld_" + worldName; var data; try { data = storage[key] && storage[key] !== "undefined" ? JSON.parse(storage[key]) : null; } catch (e) { data = null; } if (!data) { data = generateWorld(); storage[key] = JSON.stringify(data); } this.worlds[worldName] = data; this.currentWorld = worldName; return data; }, // Save current world data to storage saveWorld: function saveWorld(worldName, data) { var key = "pixelWorld_" + worldName; storage[key] = JSON.stringify(data); this.worlds[worldName] = data; }, // Switch to a different world switchWorld: function switchWorld(worldName) { worldData = this.loadWorld(worldName); this.currentWorld = worldName; initializeWorld(); // Move character to main door entrance (bottom center) on world switch if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") { setCharacterToMainDoor(); } setupUI(); }, // List all saved worlds (by checking storage keys) listWorlds: function listWorlds() { var result = []; for (var k in storage) { if (k.indexOf("pixelWorld_") === 0) { result.push(k.replace("pixelWorld_", "")); } } return result; } }; // Use pixelWorlds to load the default world at start var worldData = pixelWorlds.loadWorld("default"); var inventory = storage.inventory || { "grass": 0, "stone": 0, "wood": 0, "water": 0 }; var currentTool = 'place'; // 'place' or 'break' var selectedBlockType = 'grass'; var selectedInventorySlot = 0; // Game containers var worldContainer = new Container(); var uiContainer = new Container(); var inventoryContainer = new Container(); var toolbarContainer = new Container(); game.addChild(worldContainer); game.addChild(uiContainer); // Create player var player = new Player(); player.x = storage.playerPosition ? storage.playerPosition.x : 1024; player.y = storage.playerPosition ? storage.playerPosition.y : 1600; worldContainer.addChild(player); // Create character at the main door entrance (bottom center) var character = new Character(); // Find the main door entrance (bottom center) function setCharacterToMainDoor() { var doorGridX = Math.floor(worldWidth / 2); var doorGridY = worldHeight - 2; character.x = doorGridX * blockSize + blockSize / 2; character.y = doorGridY * blockSize + blockSize / 2; } setCharacterToMainDoor(); worldContainer.addChild(character); // Initialize world blocks function initializeWorld() { // Clear any existing blocks while (worldContainer.children.length > 1) { // Keep player worldContainer.removeChildAt(1); } // Load world data using pixelWorlds loader for Pixel World style loading worldData = pixelWorlds.loadWorld(pixelWorlds.currentWorld); // Place blocks based on worldData for (var y = 0; y < worldData.length; y++) { for (var x = 0; x < worldData[y].length; x++) { if (worldData[y][x]) { var blockType = worldData[y][x]; // If this is a main door location, use 'main_door' type if (blockType === 'main_door') { var block = new Block('main_door', blockSize); } else { var block = new Block(blockType, blockSize); } block.x = x * blockSize + blockSize / 2; block.y = y * blockSize + blockSize / 2; worldContainer.addChild(block); } } } // Fade in world for polish worldContainer.alpha = 0; tween(worldContainer, { alpha: 1 }, { duration: 600, easing: tween.easeOutQuad }); // Place a main door at all world enter locations if not present // For this game, we define the main doors as blocks at the bottom center, top center, left center, and right center of the world var entrances = [{ x: Math.floor(worldWidth / 2), y: worldHeight - 2 }, // bottom center { x: Math.floor(worldWidth / 2), y: 1 }, // top center { x: 1, y: Math.floor(worldHeight / 2) }, // left center { x: worldWidth - 2, y: Math.floor(worldHeight / 2) } // right center ]; for (var i = 0; i < entrances.length; i++) { var ex = entrances[i].x; var ey = entrances[i].y; if (!worldData[ey]) worldData[ey] = []; if (worldData[ey][ex] !== 'main_door') { worldData[ey][ex] = 'main_door'; var mainDoorBlock = new Block('main_door', blockSize); mainDoorBlock.x = ex * blockSize + blockSize / 2; mainDoorBlock.y = ey * blockSize + blockSize / 2; worldContainer.addChild(mainDoorBlock); } } // Create platforms if this is a new world if (!storage.platformsCreated) { createPlatforms(); storage.platformsCreated = true; } } // Generate a new world function generateWorld() { var worldGen = new WorldGenerator(); var data = worldGen.generate(worldWidth, worldHeight, blockSize); // Place a main door at the bottom center of the world var doorGridX = Math.floor(worldWidth / 2); var doorGridY = worldHeight - 2; if (!data[doorGridY]) data[doorGridY] = []; data[doorGridY][doorGridX] = 'main_door'; // Place main doors at all four world entrances (bottom center, top center, left center, right center) var entrances = [{ x: Math.floor(worldWidth / 2), y: worldHeight - 2 }, // bottom center { x: Math.floor(worldWidth / 2), y: 1 }, // top center { x: 1, y: Math.floor(worldHeight / 2) }, // left center { x: worldWidth - 2, y: Math.floor(worldHeight / 2) } // right center ]; for (var i = 0; i < entrances.length; i++) { var ex = entrances[i].x; var ey = entrances[i].y; if (!data[ey]) data[ey] = []; data[ey][ex] = 'main_door'; } // Save the generated world pixelWorlds.saveWorld(pixelWorlds.currentWorld, data); // Give player some starter blocks inventory = { "grass": 10, "stone": 10, "wood": 10, "water": 5 }; storage.inventory = inventory; return data; } // Setup UI function setupUI() { // Create inventory bar setupInventory(); // Create tool selection buttons setupToolbar(); // Create control buttons (left, right, jump) setupControls(); // Create main menu button setupMainMenuButton(); } // Add main menu button to return to menu function setupMainMenuButton() { var menuButton = new Button("Menu", function () { // Hide game elements gameStarted = false; // Show main menu mainMenu.show(); }); menuButton.x = 120; menuButton.y = 100; LK.gui.topRight.addChild(menuButton); // Add a Change World button for switching between 'old world' and 'new world' var changeWorldButton = new Button("Change World", function () { // If current world is 'old world', switch to 'new world', else switch to 'old world' var nextWorld = pixelWorlds.currentWorld === "old world" ? "new world" : "old world"; // If the world does not exist, generate it if (!pixelWorlds.worlds[nextWorld]) { var newWorldData = generateWorld(); pixelWorlds.saveWorld(nextWorld, newWorldData); } pixelWorlds.switchWorld(nextWorld); // Optionally, reset player position for new world player.x = 1024; player.y = 1600; storage.playerPosition = { x: player.x, y: player.y }; // Move character to main door entrance (bottom center) on Change World if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") { setCharacterToMainDoor(); } }); // Move the button to the top left, but not in the top 100x100 area changeWorldButton.x = 120; changeWorldButton.y = 200; LK.gui.topLeft.addChild(changeWorldButton); // Add a character control button var characterButton = new Button("Character", function () { // Show character info or focus on character var targetX = -character.x + 2048 / 2; var targetY = -character.y + 2732 / 2; // Animate the camera movement to the character tween(worldContainer, { x: targetX, y: targetY }, { duration: 500, easing: tween.easeOutQuad }); }); characterButton.x = 120; characterButton.y = 200; LK.gui.topRight.addChild(characterButton); } function setupInventory() { inventoryContainer = new Container(); var blockTypes = ['grass', 'stone', 'wood', 'water']; var slotSpacing = 100; // Create the background for selected slot var selectedSlotBg = LK.getAsset('selectedSlot', { anchorX: 0.5, anchorY: 0.5 }); inventoryContainer.addChild(selectedSlotBg); // Create inventory slots for (var i = 0; i < blockTypes.length; i++) { var slot = new InventorySlot(i, blockTypes[i]); slot.x = i * slotSpacing; slot.countText.setText((inventory[blockTypes[i]] || 0).toString()); inventoryContainer.addChild(slot); } // Position the inventory at the top center inventoryContainer.x = 2048 / 2 - (blockTypes.length - 1) * slotSpacing / 2; inventoryContainer.y = 100; // Create inventory title var inventoryTitle = new Text2("Inventory", { size: 40, fill: 0xFFFFFF }); inventoryTitle.anchor.set(0.5, 1); inventoryTitle.x = (blockTypes.length - 1) * slotSpacing / 2; inventoryTitle.y = -20; inventoryContainer.addChild(inventoryTitle); // Update selected slot visual updateSelectedSlot(); LK.gui.top.addChild(inventoryContainer); } function setupToolbar() { toolbarContainer = new Container(); // Place tool var placeToolBtn = new Button("Place", function () { currentTool = 'place'; updateToolbarSelection(); }); placeToolBtn.x = 0; toolbarContainer.addChild(placeToolBtn); // Break tool var breakToolBtn = new Button("Break", function () { currentTool = 'break'; updateToolbarSelection(); }); breakToolBtn.x = 250; toolbarContainer.addChild(breakToolBtn); // Position toolbar at bottom right toolbarContainer.x = 2048 - 400; toolbarContainer.y = 2732 - 100; LK.gui.bottomRight.addChild(toolbarContainer); // Update the initial tool selection updateToolbarSelection(); } function setupControls() { // Container for player movement controls (bottom left) var moveControls = new Container(); // Player Up var upBtn = new ControlButton("↑", function () { player.jump(); }); upBtn.x = 100; upBtn.y = -100; moveControls.addChild(upBtn); // Player Left var leftBtn = new ControlButton("←", function () { player.move('left'); }); leftBtn.x = 0; leftBtn.y = 0; moveControls.addChild(leftBtn); // Player Stop var stopBtn = new ControlButton("■", function () { player.stopMove(); }); stopBtn.x = 100; stopBtn.y = 0; moveControls.addChild(stopBtn); // Player Right var rightBtn = new ControlButton("→", function () { player.move('right'); }); rightBtn.x = 200; rightBtn.y = 0; moveControls.addChild(rightBtn); // Player Down (optional, not used) var downBtn = new ControlButton("↓", function () { // Could be used for crouch or drop }); downBtn.x = 100; downBtn.y = 100; moveControls.addChild(downBtn); // Position at bottom left, but move to the left down corner moveControls.x = 250; moveControls.y = 2732 - 250; LK.gui.bottomLeft.addChild(moveControls); // Container for character movement controls (bottom left, above player controls) var charControls = new Container(); // Character Up Button var charUpBtn = new ControlButton("↑", function () { character.jump(); }, 120, 120); charUpBtn.x = 100; charUpBtn.y = -100; charControls.addChild(charUpBtn); // Character Left Button var charLeftBtn = new ControlButton("←", function () { character.startMoving(-1); }, 120, 120); charLeftBtn.x = 0; charLeftBtn.y = 0; charControls.addChild(charLeftBtn); // Character Stop Button var charStopBtn = new ControlButton("■", function () { character.stopMoving(); }, 120, 120); charStopBtn.x = 100; charStopBtn.y = 0; charControls.addChild(charStopBtn); // Character Right Button var charRightBtn = new ControlButton("→", function () { character.startMoving(1); }, 120, 120); charRightBtn.x = 200; charRightBtn.y = 0; charControls.addChild(charRightBtn); // Position at bottom left, just above player controls (left down) charControls.x = 50; charControls.y = 2732 - 450; LK.gui.bottomLeft.addChild(charControls); } function updateSelectedSlot() { var selectedSlotBg = inventoryContainer.getChildAt(0); var slot = inventoryContainer.getChildAt(selectedInventorySlot + 1); if (slot) { selectedSlotBg.x = slot.x; selectedSlotBg.y = slot.y; selectedBlockType = slot.type; } } function updateToolbarSelection() { var placeToolBtn = toolbarContainer.getChildAt(0); var breakToolBtn = toolbarContainer.getChildAt(1); if (currentTool === 'place') { if (placeToolBtn && placeToolBtn.buttonText) { placeToolBtn.buttonText.style = { fill: 0xFFFF00 }; } if (breakToolBtn && breakToolBtn.buttonText) { breakToolBtn.buttonText.style = { fill: 0xFFFFFF }; } } else { if (placeToolBtn && placeToolBtn.buttonText) { placeToolBtn.buttonText.style = { fill: 0xFFFFFF }; } if (breakToolBtn && breakToolBtn.buttonText) { breakToolBtn.buttonText.style = { fill: 0xFFFF00 }; } } } function selectInventorySlot(index) { selectedInventorySlot = index; updateSelectedSlot(); // Play select sound LK.getSound('select').play(); } function updateInventoryDisplay() { for (var i = 1; i < inventoryContainer.children.length; i++) { var slot = inventoryContainer.getChildAt(i); if (slot && typeof slot.updateCount === 'function') { slot.updateCount(inventory[slot.type] || 0); } else if (slot && slot.countText) { slot.countText.setText((inventory[slot.type] || 0).toString()); } } } function updateCamera() { // Calculate how much to offset the world to center the player var targetX = -player.x + 2048 / 2; var targetY = -player.y + 2732 / 2; // Add bounds to prevent seeing outside the world targetX = Math.min(0, Math.max(targetX, -(worldWidth * blockSize - 2048))); targetY = Math.min(0, Math.max(targetY, -(worldHeight * blockSize - 2732))); // Smooth camera movement worldContainer.x = targetX; worldContainer.y = targetY; } function placeBlock(x, y) { // Convert screen position to world position var worldX = x - worldContainer.x; var worldY = y - worldContainer.y; // Convert world position to grid position var gridX = Math.floor(worldX / blockSize); var gridY = Math.floor(worldY / blockSize); // Check if we have this block type in inventory if (inventory[selectedBlockType] <= 0) { return; } // Check if position is valid if (gridX < 0 || gridX >= worldWidth || gridY < 0 || gridY >= worldHeight) { return; } // Check if space is empty if (!worldData[gridY] || gridX >= worldData[gridY].length || gridX < 0 || gridY < 0 || worldData[gridY][gridX] !== null) { return; } // Don't allow placing blocks where the player is standing var playerGridX = Math.floor(player.x / blockSize); var playerGridY = Math.floor(player.y / blockSize); var playerGridYBottom = Math.floor((player.y - 60) / blockSize); // Check player's head position too if (gridX === playerGridX && (gridY === playerGridY || gridY === playerGridYBottom)) { return; // Can't place block where player is standing } // Place the block worldData[gridY][gridX] = selectedBlockType; // Remove from inventory inventory[selectedBlockType]--; updateInventoryDisplay(); // Create the block visually var block = new Block(selectedBlockType, blockSize); block.x = gridX * blockSize + blockSize / 2; block.y = gridY * blockSize + blockSize / 2; worldContainer.addChild(block); // Play place sound LK.getSound('place').play(); // Save world data // Save the data directly to storage pixelWorlds.saveWorld(pixelWorlds.currentWorld, worldData); storage.inventory = inventory; } // Game event handlers game.down = function (x, y, obj) { if (!gameStarted) return; // Don't handle interactions if game hasn't started if (currentTool === 'place') { placeBlock(x, y); } // Check if we clicked on the character if (obj && (obj === character || obj.parent === character)) { character.startDrag(); dragTarget = character; // Prevent placing blocks when clicking character return; } }; // Add move handler for dragging character game.move = function (x, y, obj) { if (dragTarget === character) { // Convert global coordinates to character's local space var localX = x - worldContainer.x; var localY = y - worldContainer.y; // Determine movement direction based on touch position relative to character if (localX > character.x + 10) { character.startMoving(1); // Move right } else if (localX < character.x - 10) { character.startMoving(-1); // Move left } else { character.stopMoving(); // Stop if touch is directly on character } } }; // Add up handler to stop dragging game.up = function (x, y, obj) { if (dragTarget === character) { character.stopDrag(); character.stopMoving(); // Make sure character stops moving when touch is released dragTarget = null; } }; // Initialize drag target variable var dragTarget = null; // Track control state for continuous button presses var controlState = { left: false, right: false, jump: false }; // Create platforms in the world function createPlatforms() { // Create several platforms at different positions var platforms = [{ x: 10, y: 20, width: 8, height: 1 }, { x: 25, y: 18, width: 6, height: 1 }, { x: 5, y: 15, width: 4, height: 1 }, { x: 15, y: 12, width: 5, height: 1 }, { x: 30, y: 10, width: 7, height: 1 }]; for (var i = 0; i < platforms.length; i++) { var p = platforms[i]; var platform = new Platform(p.width, p.height); platform.x = p.x * blockSize; platform.y = p.y * blockSize; worldContainer.addChild(platform); // Add platform blocks to worldData for (var x = 0; x < p.width; x++) { for (var y = 0; y < p.height; y++) { var gridX = p.x + x; var gridY = p.y + y; if (gridY < worldHeight && gridX < worldWidth) { if (!worldData[gridY]) worldData[gridY] = []; worldData[gridY][gridX] = 'stone'; } } } } // Save world data after creating platforms pixelWorlds.saveWorld(pixelWorlds.currentWorld, worldData); } // --- Day-Night Cycle Variables --- var dayNightCycle = { time: 0, // 0 to 1, where 0 is midnight, 0.5 is noon, 1 is midnight again speed: 1 / (60 * 60 * 4), // 4 minutes per full cycle (slower sun and moon movement) sun: null, moon: null, skyColors: [{ t: 0.00, color: 0x0a0a2a }, // Midnight { t: 0.20, color: 0x2a3a7a }, // Early dawn { t: 0.30, color: 0x87CEEB }, // Morning { t: 0.50, color: 0x87CEEB }, // Noon { t: 0.70, color: 0x2a3a7a }, // Dusk { t: 0.85, color: 0x0a0a2a }, // Night { t: 1.00, color: 0x0a0a2a } // Midnight ] }; // Add sun and moon visuals to the game background if (!dayNightCycle.sun) { dayNightCycle.sun = LK.getAsset('sun', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3, x: 2048 / 2, y: 2732 / 2, alpha: 0.85 }); dayNightCycle.sun.y = 600; dayNightCycle.sun.x = 2048 / 2; dayNightCycle.sun.zIndex = -100; game.addChild(dayNightCycle.sun); } if (!dayNightCycle.moon) { dayNightCycle.moon = LK.getAsset('moon', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.5, x: 2048 / 2, y: 2732 / 2, alpha: 0.55 }); dayNightCycle.moon.y = 600; dayNightCycle.moon.x = 2048 / 2; dayNightCycle.moon.zIndex = -99; game.addChild(dayNightCycle.moon); } // Helper to interpolate between two colors function lerpColor(a, b, t) { var ar = a >> 16 & 0xff, ag = a >> 8 & 0xff, ab = a & 0xff; var br = b >> 16 & 0xff, bg = b >> 8 & 0xff, bb = b & 0xff; var rr = Math.round(ar + (br - ar) * t); var rg = Math.round(ag + (bg - ag) * t); var rb = Math.round(ab + (bb - ab) * t); return rr << 16 | rg << 8 | rb; } // Helper to get sky color for current time function getSkyColor(t) { var colors = dayNightCycle.skyColors; for (var i = 0; i < colors.length - 1; i++) { if (t >= colors[i].t && t <= colors[i + 1].t) { var localT = (t - colors[i].t) / (colors[i + 1].t - colors[i].t); return lerpColor(colors[i].color, colors[i + 1].color, localT); } } return colors[colors.length - 1].color; } // --- Butterfly Spawner --- var butterflies = []; function spawnButterflies() { // Only spawn during the day var t = dayNightCycle.time; var isDay = t > 0.18 && t < 0.82; if (isDay && butterflies.length < 8) { if (Math.random() < 0.03) { var b = new Butterfly(); game.addChild(b); butterflies.push(b); } } // Remove excess or faded butterflies for (var i = butterflies.length - 1; i >= 0; i--) { if (butterflies[i].alpha < 0.01) { butterflies[i].destroy(); butterflies.splice(i, 1); } } } // --- Parallax Cloud Layer --- var clouds = []; function createCloud(x, y, scale, speed, alpha) { var c = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5, scaleX: scale * (1.2 + Math.random() * 0.5), scaleY: scale * (0.7 + Math.random() * 0.3), x: x, y: y, alpha: alpha }); c._cloudSpeed = speed; c._cloudScale = scale; c._cloudAlpha = alpha; c.zIndex = -50; game.addChild(c); return c; } function spawnClouds() { if (clouds.length < 7 && Math.random() < 0.02) { var y = 400 + Math.random() * 400; var scale = 2.5 + Math.random() * 2.5; var speed = 0.2 + Math.random() * 0.3; var alpha = 0.18 + Math.random() * 0.12; var x = -200; var c = createCloud(x, y, scale, speed, alpha); clouds.push(c); } for (var i = clouds.length - 1; i >= 0; i--) { var c = clouds[i]; c.x += c._cloudSpeed; // Fade out at night var t = dayNightCycle.time; var isDay = t > 0.18 && t < 0.82; c.alpha = isDay ? c._cloudAlpha : c._cloudAlpha * 0.3; if (c.x > 2048 + 300) { c.destroy(); clouds.splice(i, 1); } } } // --- Shooting Stars --- var shootingStars = []; function spawnShootingStar() { // Only at night var t = dayNightCycle.time; var isNight = t < 0.12 || t > 0.88; if (isNight && Math.random() < 0.012) { var star = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5 + Math.random() * 0.7, scaleY: 0.18 + Math.random() * 0.12, x: 200 + Math.random() * 1600, y: 300 + Math.random() * 400, tint: 0xffffff, alpha: 0.7 }); star._vx = 12 + Math.random() * 8; star._vy = 3 + Math.random() * 2; star._life = 0; game.addChild(star); shootingStars.push(star); } for (var i = shootingStars.length - 1; i >= 0; i--) { var s = shootingStars[i]; s.x += s._vx; s.y += s._vy; s._life += 1; s.alpha *= 0.97; if (s.x > 2200 || s.y > 900 || s._life > 60 || s.alpha < 0.05) { s.destroy(); shootingStars.splice(i, 1); } } } // Main game loop game.update = function () { // --- Day-Night Cycle Update --- dayNightCycle.time += dayNightCycle.speed; if (dayNightCycle.time > 1) dayNightCycle.time -= 1; var skyColor = getSkyColor(dayNightCycle.time); game.setBackgroundColor(skyColor); // Sun and moon movement (circular path) var sunAngle = Math.PI * 2 * (dayNightCycle.time - 0.25); // Noon at top var moonAngle = sunAngle + Math.PI; var sunRadius = 900; var moonRadius = 900; dayNightCycle.sun.x = 2048 / 2 + Math.cos(sunAngle) * sunRadius; dayNightCycle.sun.y = 1200 + Math.sin(sunAngle) * sunRadius * 0.5; dayNightCycle.sun.alpha = Math.max(0, Math.sin(sunAngle) * 0.9); dayNightCycle.moon.x = 2048 / 2 + Math.cos(moonAngle) * moonRadius; dayNightCycle.moon.y = 1200 + Math.sin(moonAngle) * moonRadius * 0.5; dayNightCycle.moon.alpha = Math.max(0, Math.sin(moonAngle) * 0.7); // --- Parallax Clouds Update --- spawnClouds(); // --- Shooting Stars Update --- spawnShootingStar(); // --- Butterflies Update --- spawnButterflies(); for (var i = 0; i < butterflies.length; i++) { butterflies[i].update(); } // --- Day-Night Cycle Update --- dayNightCycle.time += dayNightCycle.speed; if (dayNightCycle.time > 1) dayNightCycle.time -= 1; var skyColor = getSkyColor(dayNightCycle.time); game.setBackgroundColor(skyColor); // Sun and moon movement (circular path) var sunAngle = Math.PI * 2 * (dayNightCycle.time - 0.25); // Noon at top var moonAngle = sunAngle + Math.PI; var sunRadius = 900; var moonRadius = 900; dayNightCycle.sun.x = 2048 / 2 + Math.cos(sunAngle) * sunRadius; dayNightCycle.sun.y = 1200 + Math.sin(sunAngle) * sunRadius * 0.5; dayNightCycle.sun.alpha = Math.max(0, Math.sin(sunAngle) * 0.9); dayNightCycle.moon.x = 2048 / 2 + Math.cos(moonAngle) * moonRadius; dayNightCycle.moon.y = 1200 + Math.sin(moonAngle) * moonRadius * 0.5; dayNightCycle.moon.alpha = Math.max(0, Math.sin(moonAngle) * 0.7); // Only update game elements if the game has started if (gameStarted) { // --- Main Door Logic Start --- // Find all main door blocks in the world and check if player is at any of them if (typeof player.lastWasAtDoor === "undefined") player.lastWasAtDoor = false; var atDoor = false; for (var y = 0; y < worldData.length; y++) { for (var x = 0; x < worldData[y].length; x++) { if (worldData[y][x] === 'main_door') { var doorX = x * blockSize + blockSize / 2; var doorY = y * blockSize + blockSize / 2; var dx = Math.abs(player.x - doorX); var dy = Math.abs(player.y - doorY); if (dx < blockSize * 0.7 && dy < blockSize * 0.7) { atDoor = true; } } } } // Trigger world enter menu on entering any main door if (!player.lastWasAtDoor && atDoor) { worldEnterMenu.show(); gameStarted = false; } player.lastWasAtDoor = atDoor; // --- Main Door Logic End --- // Update player player.update(); // Update character // Handle character movement based on controlState if (controlState) { if (controlState.left) { character.startMoving(-1); } else if (controlState.right) { character.startMoving(1); } else { character.stopMoving(); } if (controlState.jump) { character.jump(); controlState.jump = false; // Only jump once per press } } character.update(); // Simple AI: Make character move back and forth if (LK.ticks % 120 === 0 && !dragTarget) { // Change direction every 2 seconds (only if not being controlled by player) character.startMoving(character.direction * -1); } // Save game data periodically if (LK.ticks % 300 === 0) { // Convert worldData to string before storing pixelWorlds.saveWorld(pixelWorlds.currentWorld, worldData); storage.inventory = inventory; } } }; // Create main menu var mainMenu = new MainMenu(); mainMenu.x = 0; mainMenu.y = 0; game.addChild(mainMenu); // Create world enter menu var worldEnterMenu = new WorldEnterMenu(); worldEnterMenu.x = 0; worldEnterMenu.y = 0; worldEnterMenu.visible = false; game.addChild(worldEnterMenu); // Show main menu to start mainMenu.show(); // Remove auto-show of worldEnterMenu. Instead, show it when player enters the main door. // --- Vignette Overlay --- var vignette = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 18, scaleY: 12, x: 2048 / 2, y: 2732 / 2, tint: 0x000000, alpha: 0.18 }); vignette.zIndex = 9999; game.addChild(vignette); // Start background music LK.playMusic('bgmusic'); ; ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
worldData: "undefined",
playerPosition: {
x: 1024,
y: 1600
},
inventory: {}
});
/****
* Classes
****/
var Block = Container.expand(function (type, size) {
var self = Container.call(this);
self.type = type;
self.size = size || 64;
// Use 'bedrock' skin for bedrock blocks, otherwise use type as asset
var assetType = type === 'bedrock' ? 'bedrock' : type;
var graphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5,
width: self.size,
height: self.size
});
self.down = function (x, y, obj) {
if (currentTool === 'break') {
self.breakBlock();
}
};
self.breakBlock = function () {
// Make main door and bedrock blocks unbreakable
if (self.type === 'main_door' || self.type === 'bedrock') {
// Optionally play a sound or show a message here
return;
}
// Add to inventory
inventory[self.type] = (inventory[self.type] || 0) + 1;
updateInventoryDisplay();
// Play break sound
LK.getSound('break').play();
// Remove from world
var gridX = Math.floor(self.x / blockSize);
var gridY = Math.floor(self.y / blockSize);
if (worldData[gridY] && worldData[gridY][gridX] !== undefined && worldData[gridY][gridX] !== null) {
worldData[gridY][gridX] = null;
self.destroy();
}
// Save world data
// Convert worldData to string before storing
storage.worldData = JSON.stringify(worldData);
};
return self;
});
// --- Butterfly Class ---
var Butterfly = Container.expand(function () {
var self = Container.call(this);
// Use 'Leaf' as a placeholder for butterfly, tint for color
var butterflyColors = [0xffb347, 0xff69b4, 0x87ceeb, 0x7fff00, 0xffffff];
var color = butterflyColors[Math.floor(Math.random() * butterflyColors.length)];
var sprite = self.attachAsset('Leaf', {
anchorX: 0.5,
anchorY: 0.5,
width: 40,
height: 30,
tint: color,
alpha: 0.85
});
self.x = Math.random() * 2048;
self.y = 600 + Math.random() * 800;
self.vx = (Math.random() - 0.5) * 3;
self.vy = (Math.random() - 0.5) * 1.5;
self.flapTime = 0;
self.update = function () {
// Fluttering movement
self.x += self.vx + Math.sin(LK.ticks / 10 + self.y) * 0.5;
self.y += self.vy + Math.cos(LK.ticks / 15 + self.x) * 0.3;
// Flap wings (scale)
self.flapTime += 0.2 + Math.random() * 0.1;
sprite.scale.y = 0.8 + Math.sin(self.flapTime) * 0.3;
// Stay in bounds
if (self.x < 0) self.x = 0, self.vx *= -1;
if (self.x > 2048) self.x = 2048, self.vx *= -1;
if (self.y < 400) self.y = 400, self.vy *= -1;
if (self.y > 1800) self.y = 1800, self.vy *= -1;
// Fade out at night
var t = dayNightCycle.time;
var isDay = t > 0.18 && t < 0.82;
self.alpha = isDay ? 1 : 0;
};
return self;
});
// --- Butterfly Spawner ---
var Button = Container.expand(function (text, callback) {
var self = Container.call(this);
var background = self.attachAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5
});
self.buttonText = new Text2(text, {
size: 36,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
self.callback = callback;
self.down = function (x, y, obj) {
if (self.callback) {
self.up = function (x, y, obj) {
// Animate button release
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOutQuad
});
};
tween(self, {
scaleX: 0.92,
scaleY: 0.92
}, {
duration: 80,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOutQuad
});
}
});
self.callback();
}
};
return self;
});
var Character = Container.expand(function () {
var self = Container.call(this);
// Add shadow for polish
var shadow = self.attachAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 70,
height: 20,
y: 0,
x: 0,
alpha: 0.25
});
shadow.y = 0;
shadow.x = 0;
// Create character sprite
var sprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0,
width: 80,
height: 100
});
// Character properties
self.direction = 1; // 1 = right, -1 = left
self.isMoving = false;
self.speed = 4;
// Animation properties
self.frameTime = 0;
self.frameRate = 8; // frames per second
// Change direction method
self.faceDirection = function (dir) {
if (dir !== self.direction) {
self.direction = dir;
sprite.scale.x = self.direction;
}
};
// Start/stop movement
self.startMoving = function (dir) {
self.isMoving = true;
self.faceDirection(dir);
};
self.stopMoving = function () {
self.isMoving = false;
};
// Update method - called every frame
self.update = function () {
// Store last position for collision detection
var lastY = self.y;
var lastX = self.x;
// Handle movement
if (self.isMoving) {
self.x += self.speed * self.direction;
// Update animation
self.frameTime += 1 / 60;
if (self.frameTime >= 1 / self.frameRate) {
self.frameTime = 0;
// We would change animation frame here if we had sprite frames
}
}
// Keep character in bounds
if (self.x < blockSize / 2) self.x = blockSize / 2;
if (self.x > worldWidth * blockSize - blockSize / 2) self.x = worldWidth * blockSize - blockSize / 2;
if (self.y < blockSize / 2) self.y = blockSize / 2;
if (self.y > worldHeight * blockSize - blockSize / 2) self.y = worldHeight * blockSize - blockSize / 2;
// --- Begin Physics for Character ---
if (typeof self.velocityY === "undefined") self.velocityY = 0;
if (typeof self.lastOnGround === "undefined") self.lastOnGround = false;
// Gravity
var gravityForce = 1.2;
self.velocityY += gravityForce;
// Limit max fall speed
if (self.velocityY > 32) self.velocityY = 32;
// --- Horizontal collision check before moving horizontally ---
var nextX = self.x;
if (self.isMoving) {
nextX += self.speed * self.direction;
// Check for block at new horizontal position (character's feet and head)
var checkGridYFeet = Math.floor((self.y - 10) / blockSize);
var checkGridYHead = Math.floor((self.y - 90) / blockSize);
var checkGridX = Math.floor(nextX / blockSize);
var blocked = false;
// Check feet
if (checkGridYFeet >= 0 && checkGridYFeet < worldData.length && checkGridX >= 0 && checkGridX < worldData[checkGridYFeet].length && worldData[checkGridYFeet][checkGridX] && worldData[checkGridYFeet][checkGridX] !== 'water') {
blocked = true;
}
// Check head
if (checkGridYHead >= 0 && checkGridYHead < worldData.length && checkGridX >= 0 && checkGridX < worldData[checkGridYHead].length && worldData[checkGridYHead][checkGridX] && worldData[checkGridYHead][checkGridX] !== 'water') {
blocked = true;
}
if (!blocked) {
self.x = nextX;
} else {
self.isMoving = false;
}
// Update animation
self.frameTime += 1 / 60;
if (self.frameTime >= 1 / self.frameRate) {
self.frameTime = 0;
// We would change animation frame here if we had sprite frames
}
}
// Move vertically
self.y += self.velocityY;
// --- Vertical collision check after moving vertically ---
var gridX = Math.floor(self.x / blockSize);
var gridY = Math.floor((self.y + 10) / blockSize);
var onGround = false;
for (var checkX = gridX - 1; checkX <= gridX + 1; checkX++) {
if (checkX >= 0 && gridY >= 0 && gridY < worldData.length && checkX < (worldData[gridY] ? worldData[gridY].length : 0) && worldData[gridY] && worldData[gridY][checkX] && worldData[gridY][checkX] !== 'water') {
var distX = Math.abs(self.x - (checkX * blockSize + blockSize / 2));
if (distX < blockSize * 0.8) {
// Snap to top of block
self.y = gridY * blockSize - 10;
self.velocityY = 0;
onGround = true;
break;
}
}
}
// Check if we just landed on a platform
if (!self.lastOnGround && onGround) {
self.velocityY = 0;
self.y = Math.floor(self.y / blockSize) * blockSize - 10;
}
self.lastOnGround = onGround;
// --- End Physics for Character ---
// Prevent character from being inside a block vertically (head stuck in block above)
var gridYHead = Math.floor((self.y - 90) / blockSize);
if (gridYHead >= 0 && gridYHead < worldData.length && gridX >= 0 && gridX < worldData[gridYHead].length && worldData[gridYHead][gridX] && worldData[gridYHead][gridX] !== 'water') {
// Push character down just below the block
self.y = (gridYHead + 1) * blockSize + 90;
self.velocityY = Math.max(self.velocityY, 0);
}
// Check for horizontal collisions (legacy, now handled above, but keep for safety)
if (lastX !== self.x) {
var checkGridY = Math.floor((self.y - 50) / blockSize); // Check at character's mid height
var newGridX = Math.floor(self.x / blockSize);
if (checkGridY >= 0 && checkGridY < worldData.length && newGridX >= 0 && newGridX < worldData[checkGridY].length && worldData[checkGridY][newGridX] && worldData[checkGridY][newGridX] !== 'water') {
self.x = lastX;
self.isMoving = false;
}
}
};
// Interactive events
self.down = function (x, y, obj) {
// Handle touch down on character
self.startDrag();
// Convert to world container's coordinate system for proper positioning
var worldX = obj.parent.toGlobal(obj.position).x;
var worldY = obj.parent.toGlobal(obj.position).y;
// Determine initial direction based on touch position
if (x > self.width / 2) {
self.faceDirection(1); // Face right
} else {
self.faceDirection(-1); // Face left
}
};
// Add touch controls for character
self.startDrag = function () {
self.isDragging = true;
};
self.stopDrag = function () {
self.isDragging = false;
self.isMoving = false;
};
// Add jump functionality
self.jump = function () {
// Find where the player is standing
var gridX = Math.floor(self.x / blockSize);
var gridY = Math.floor((self.y + 10) / blockSize);
// Check if character is on ground
var onGround = false;
for (var checkX = gridX - 1; checkX <= gridX + 1; checkX++) {
if (checkX >= 0 && gridY >= 0 && gridY < worldData.length && checkX < (worldData[gridY] ? worldData[gridY].length : 0) && worldData[gridY] && worldData[gridY][checkX] && worldData[gridY][checkX] !== 'water') {
var distX = Math.abs(self.x - (checkX * blockSize + blockSize / 2));
if (distX < blockSize * 0.8) {
onGround = true;
break;
}
}
}
if (onGround) {
// Apply upward force when jumping
self.velocityY = -22;
}
};
self.drag = function (x, y) {
if (self.isDragging) {
// Convert global coordinates to character's local frame
var localX = x - game.toLocal(self.parent.toGlobal(self.position)).x + self.x;
// Calculate difference for movement threshold
var diffX = localX - self.x;
// Determine direction based on current position and touch position
if (diffX > 10) {
self.faceDirection(1);
self.isMoving = true;
} else if (diffX < -10) {
self.faceDirection(-1);
self.isMoving = true;
} else {
self.isMoving = false;
}
}
};
return self;
});
var ControlButton = Container.expand(function (text, callback, width, height) {
var self = Container.call(this);
width = width || 150;
height = height || 150;
// Determine control type and image asset
self.controlType = '';
var imageAsset = null;
if (text === '←') {
self.controlType = 'left';
imageAsset = 'LeftButton';
} else if (text === '→') {
self.controlType = 'right';
imageAsset = 'RightButton';
} else if (text === '↑') {
self.controlType = 'jump';
imageAsset = 'UpButton';
} else if (text === '■') {
self.controlType = 'stop';
imageAsset = null;
}
// Create button background (use image for direction, shape for stop)
var background;
if (imageAsset) {
background = self.attachAsset(imageAsset, {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: height
});
} else {
background = self.attachAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
width: width,
height: height
});
}
// Only show text for stop button, not for direction buttons
if (!imageAsset) {
self.buttonText = new Text2(text, {
size: 48,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
}
// Store callback function
self.callback = callback;
// Button interaction
self.down = function (x, y, obj) {
if (self.callback) {
// Animate button press
tween(self, {
scaleX: 0.92,
scaleY: 0.92
}, {
duration: 80,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120,
easing: tween.easeOutQuad
});
}
});
background.alpha = 0.7;
self.callback();
// Set control state for continuous button press
if (self.controlType && controlState) {
if (self.controlType === 'left') {
controlState.left = true;
controlState.right = false;
} else if (self.controlType === 'right') {
controlState.right = true;
controlState.left = false;
} else if (self.controlType === 'jump') {
controlState.jump = true;
} else if (self.controlType === 'stop') {
controlState.left = false;
controlState.right = false;
}
}
}
};
self.up = function (x, y, obj) {
background.alpha = 1.0;
// Animate button release
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100,
easing: tween.easeOutQuad
});
// Clear control state when button is released
if (self.controlType && controlState) {
if (self.controlType === 'left') {
controlState.left = false;
} else if (self.controlType === 'right') {
controlState.right = false;
} else if (self.controlType === 'jump') {
controlState.jump = false;
}
}
};
return self;
});
var InventorySlot = Container.expand(function (index, type) {
var self = Container.call(this);
self.index = index;
self.type = type;
var background = self.attachAsset('inventorySlot', {
anchorX: 0.5,
anchorY: 0.5
});
if (type) {
var blockIcon = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 60
});
}
self.countText = new Text2("0", {
size: 24,
fill: 0xFFFFFF
});
self.countText.anchor.set(1, 1);
self.countText.x = 30;
self.countText.y = 30;
self.addChild(self.countText);
self.updateCount = function (count) {
self.countText.setText(count.toString());
};
self.down = function (x, y, obj) {
selectInventorySlot(self.index);
};
return self;
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Create a semi-transparent background overlay
var overlay = LK.getAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
alpha: 0.7
});
overlay.tint = 0x000000;
self.addChild(overlay);
// Game title
self.title = new Text2("Pixel Realms", {
size: 120,
fill: 0xFFFFFF
});
self.title.anchor.set(0.5, 0.5);
self.title.x = 2048 / 2;
self.title.y = 800;
self.addChild(self.title);
// Start game button
self.startButton = new Button("Start Game", function () {
self.hide();
worldEnterMenu.show();
});
self.startButton.x = 2048 / 2;
self.startButton.y = 1200;
self.addChild(self.startButton);
// Add animated elements
self.show = function () {
self.visible = true;
// Reset positions for animation
self.title.y = 600;
self.title.alpha = 0;
self.startButton.y = 1300;
self.startButton.alpha = 0;
// Animate title
tween(self.title, {
y: 800,
alpha: 1
}, {
duration: 800,
easing: tween.easeOutBack
});
// Animate button with slight delay
tween(self.startButton, {
y: 1200,
alpha: 1
}, {
duration: 800,
easing: tween.easeOutBack
});
};
self.hide = function () {
// Animate out
tween(self.title, {
y: 600,
alpha: 0
}, {
duration: 500,
easing: tween.easeInBack
});
tween(self.startButton, {
y: 1300,
alpha: 0
}, {
duration: 500,
easing: tween.easeInBack,
onFinish: function onFinish() {
self.visible = false;
}
});
};
return self;
});
var Platform = Container.expand(function (width, height) {
var self = Container.call(this);
self.width = width || 5;
self.height = height || 1;
// Create platform blocks
for (var x = 0; x < self.width; x++) {
for (var y = 0; y < self.height; y++) {
var block = new Block('stone', blockSize);
block.x = x * blockSize + blockSize / 2;
block.y = y * blockSize + blockSize / 2;
self.addChild(block);
}
}
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Add shadow for polish
var shadow = self.attachAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 50,
height: 18,
y: 0,
x: 0,
alpha: 0.22
});
shadow.y = 0;
shadow.x = 0;
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1,
width: 60,
height: 120
});
// Movement properties
self.speed = 5;
self.velocity = {
x: 0,
y: 0
};
self.gravity = 0.5;
self.jumping = false;
self.grounded = false;
self.move = function (direction) {
if (direction === 'left') {
self.velocity.x = -self.speed;
} else if (direction === 'right') {
self.velocity.x = self.speed;
}
};
self.stopMove = function () {
self.velocity.x = 0;
};
self.jump = function () {
if (self.grounded) {
self.velocity.y = -22;
self.grounded = false;
self.jumping = true;
}
};
self.update = function () {
// --- Begin Physics for Player ---
if (typeof self.velocity === "undefined") self.velocity = {
x: 0,
y: 0
};
if (typeof self.gravity === "undefined") self.gravity = 1.2;
if (typeof self.grounded === "undefined") self.grounded = false;
if (!self.grounded) {
self.velocity.y += self.gravity;
if (self.velocity.y > 32) self.velocity.y = 32;
}
self.x += self.velocity.x;
self.y += self.velocity.y;
// --- End Physics for Player ---
// Prevent player from leaving world boundaries
if (self.x < blockSize / 2) self.x = blockSize / 2;
if (self.x > worldWidth * blockSize - blockSize / 2) self.x = worldWidth * blockSize - blockSize / 2;
if (self.y < blockSize / 2) self.y = blockSize / 2;
if (self.y > worldHeight * blockSize - blockSize / 2) self.y = worldHeight * blockSize - blockSize / 2;
// Check for ground collision
var gridX = Math.floor(self.x / blockSize);
var gridY = Math.floor((self.y + 10) / blockSize);
// Simple collision check with the block below
if (gridY >= 0 && gridY < worldData.length && gridX >= 0 && worldData[gridY] && gridX < worldData[gridY].length && worldData[gridY][gridX] && worldData[gridY][gridX] !== 'water') {
if (self.velocity.y > 0) {
self.y = gridY * blockSize - 10;
self.velocity.y = 0;
self.grounded = true;
self.jumping = false;
}
} else {
self.grounded = false;
}
// Update camera to follow player
updateCamera();
// Save player position periodically
if (LK.ticks % 60 === 0) {
storage.playerPosition = {
x: self.x,
y: self.y
};
}
};
return self;
});
var WorldEnterMenu = Container.expand(function () {
var self = Container.call(this);
// Create a semi-transparent background
var overlay = LK.getAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
alpha: 0.8
});
overlay.tint = 0x000022;
self.addChild(overlay);
// World title
self.title = new Text2("Enter World", {
size: 100,
fill: 0xFFFFFF
});
self.title.anchor.set(0.5, 0.5);
self.title.x = 2048 / 2;
self.title.y = 600;
self.addChild(self.title);
// World description
self.description = new Text2("Select your world to start building", {
size: 48,
fill: 0xCCCCCC
});
self.description.anchor.set(0.5, 0.5);
self.description.x = 2048 / 2;
self.description.y = 720;
self.addChild(self.description);
// World selection buttons
self.createWorldButton = new Button("Create New World", function () {
// Prompt for world name (simple prompt, can be improved)
var worldName = "world" + Math.floor(Math.random() * 10000);
// Generate new world and save with name
var newWorldData = generateWorld();
pixelWorlds.saveWorld(worldName, newWorldData);
worldData = newWorldData;
pixelWorlds.currentWorld = worldName;
// Reset player position
player.x = 1024;
player.y = 1600;
storage.playerPosition = {
x: player.x,
y: player.y
};
// Move character to main door entrance (bottom center) on new world creation
if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") {
setCharacterToMainDoor();
}
// Reset inventory
inventory = {
"grass": 10,
"stone": 10,
"wood": 10,
"water": 5
};
storage.inventory = inventory;
// Start game
self.hide();
gameStarted = true;
initializeWorld();
setupUI();
});
self.createWorldButton.x = 2048 / 2;
self.createWorldButton.y = 1000;
self.addChild(self.createWorldButton);
// Add a world selection menu for pixel worlds
self.loadWorldButton = new Button("Select World", function () {
// Show a simple world selection overlay
var overlay = new Container();
var bg = LK.getAsset('buttonBg', {
anchorX: 0.5,
anchorY: 0.5,
width: 1200,
height: 1000,
alpha: 0.95
});
bg.tint = 0x222244;
overlay.addChild(bg);
overlay.x = 2048 / 2;
overlay.y = 1200;
// List all worlds
var worlds = pixelWorlds.listWorlds();
if (worlds.length === 0) {
var noWorlds = new Text2("No worlds found", {
size: 60,
fill: 0xffffff
});
noWorlds.anchor.set(0.5, 0.5);
noWorlds.y = 0;
overlay.addChild(noWorlds);
} else {
for (var i = 0; i < worlds.length; i++) {
(function (worldName, idx) {
var btn = new Button(worldName, function () {
// Switch to selected world
pixelWorlds.switchWorld(worldName);
// Move character to main door entrance (bottom center) on world select
if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") {
setCharacterToMainDoor();
}
overlay.visible = false;
self.hide();
gameStarted = true;
});
btn.x = 0;
btn.y = -400 + idx * 120;
overlay.addChild(btn);
})(worlds[i], i);
}
}
// Add close button
var closeBtn = new Button("Close", function () {
overlay.visible = false;
});
closeBtn.x = 0;
closeBtn.y = 500;
overlay.addChild(closeBtn);
LK.gui.center.addChild(overlay);
});
self.loadWorldButton.x = 2048 / 2;
self.loadWorldButton.y = 1150;
self.addChild(self.loadWorldButton);
self.backButton = new Button("Back to Menu", function () {
self.hide();
mainMenu.show();
});
self.backButton.x = 2048 / 2;
self.backButton.y = 1400;
self.addChild(self.backButton);
// Show animation
self.show = function () {
self.visible = true;
// Reset positions for animation
self.title.y = 400;
self.title.alpha = 0;
self.description.y = 620;
self.description.alpha = 0;
self.createWorldButton.y = 1100;
self.createWorldButton.alpha = 0;
self.loadWorldButton.y = 1250;
self.loadWorldButton.alpha = 0;
self.backButton.y = 1500;
self.backButton.alpha = 0;
// Animate title
tween(self.title, {
y: 600,
alpha: 1
}, {
duration: 600,
easing: tween.easeOutBack
});
// Animate description
tween(self.description, {
y: 720,
alpha: 1
}, {
duration: 600,
delay: 100,
easing: tween.easeOutBack
});
// Animate buttons
tween(self.createWorldButton, {
y: 1000,
alpha: 1
}, {
duration: 600,
delay: 200,
easing: tween.easeOutBack
});
tween(self.loadWorldButton, {
y: 1150,
alpha: 1
}, {
duration: 600,
delay: 300,
easing: tween.easeOutBack
});
tween(self.backButton, {
y: 1400,
alpha: 1
}, {
duration: 600,
delay: 400,
easing: tween.easeOutBack
});
};
// Hide animation
self.hide = function () {
// Animate out
tween(self.title, {
y: 400,
alpha: 0
}, {
duration: 400,
easing: tween.easeInBack
});
tween(self.description, {
y: 620,
alpha: 0
}, {
duration: 400,
easing: tween.easeInBack
});
tween(self.createWorldButton, {
y: 1100,
alpha: 0
}, {
duration: 400,
easing: tween.easeInBack
});
tween(self.loadWorldButton, {
y: 1250,
alpha: 0
}, {
duration: 400,
easing: tween.easeInBack
});
tween(self.backButton, {
y: 1500,
alpha: 0
}, {
duration: 400,
easing: tween.easeInBack,
onFinish: function onFinish() {
self.visible = false;
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Game constants
// Make sure JSON is defined for storage operations
//{bedrock_skin}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
var JSON = {
parse: function parse(text) {
return LK.parseJSON(text);
},
stringify: function stringify(obj) {
// Convert JavaScript object to a JSON string
var str = '';
if (obj === null) return 'null';
if (typeof obj === 'undefined') return undefined;
if (typeof obj === 'string') return '"' + obj.replace(/"/g, '\\"') + '"';
if (typeof obj === 'number' || typeof obj === 'boolean') return obj.toString();
if (Array.isArray(obj)) {
str = '[';
for (var i = 0; i < obj.length; i++) {
str += (i > 0 ? ',' : '') + JSON.stringify(obj[i]);
}
return str + ']';
}
if (_typeof(obj) === 'object') {
str = '{';
var keys = Object.keys(obj);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
str += (i > 0 ? ',' : '') + '"' + key + '":' + JSON.stringify(obj[key]);
}
return str + '}';
}
return '';
}
};
var WorldGenerator = function WorldGenerator() {
// Generate a new world with more advanced terrain features
this.generate = function (width, height, blockSize) {
var data = [];
// Initialize with empty arrays
for (var y = 0; y < height; y++) {
data[y] = [];
for (var x = 0; x < width; x++) {
data[y][x] = null;
}
}
// Add bedrock border (unbreakable) only at the bottom of the world
for (var y = 0; y < height; y++) {
for (var x = 0; x < width; x++) {
if (y === height - 1) {
data[y][x] = 'bedrock';
}
}
}
// Generate terrain using simple noise
var terrain = this.generateTerrain(width, height);
// Apply terrain to world data
for (var x = 0; x < width; x++) {
var groundHeight = Math.floor(terrain[x] * height * 0.4) + Math.floor(height * 0.5);
// Fill all blocks below the surface with 'ground' (dirt)
for (var y = groundHeight; y < height; y++) {
if (y === groundHeight) {
// Don't overwrite bedrock border
if (data[y][x] !== 'bedrock') data[y][x] = 'grass';
} else {
if (data[y][x] !== 'bedrock') data[y][x] = 'ground';
}
}
// Generate caves
this.generateCaves(data, x, groundHeight, height);
}
// Add trees
this.addTrees(data, width, height);
// Add water bodies
this.addWater(data, width, height);
return data;
};
// Generate basic terrain height map using simple noise
this.generateTerrain = function (width, height) {
var terrain = [];
var smoothness = 8; // Higher = smoother terrain
// Start with random height
terrain[0] = Math.random();
// Generate rest using midpoint displacement-like approach
for (var x = 1; x < width; x++) {
// Get previous height with some randomness
var prevHeight = terrain[x - 1];
var randomFactor = (Math.random() - 0.5) * 2 / smoothness;
// New height with constraints
terrain[x] = Math.max(0.1, Math.min(0.9, prevHeight + randomFactor));
// Add some hills/mountains occasionally
if (Math.random() < 0.05) {
var hillHeight = 0.1 + Math.random() * 0.2;
var hillWidth = 3 + Math.floor(Math.random() * 5);
// Create a hill centered at x
for (var h = 0; h < hillWidth && x + h < width; h++) {
var offset = hillHeight * Math.sin(h / hillWidth * Math.PI);
if (x + h < width) {
terrain[x + h] = Math.max(0.1, Math.min(0.9, terrain[x + h] - offset));
}
}
// Skip ahead
x += hillWidth;
}
}
return terrain;
};
// Add random caves under the terrain
this.generateCaves = function (data, x, groundHeight, height) {
// Random caves deeper underground
if (groundHeight + 5 < height) {
for (var c = 0; c < 2; c++) {
if (Math.random() < 0.05) {
var caveY = groundHeight + 5 + Math.floor(Math.random() * (height - groundHeight - 10));
var caveSize = 1 + Math.floor(Math.random() * 3);
for (var cy = caveY; cy < caveY + caveSize && cy < height; cy++) {
if (data[cy] && data[cy][x]) {
data[cy][x] = null;
}
}
}
}
}
};
// Add trees on the surface
this.addTrees = function (data, width, height) {
for (var x = 0; x < width; x++) {
// Find the ground level
var groundY = 0;
for (var y = 0; y < height; y++) {
if (data[y] && data[y][x] === 'grass') {
groundY = y;
break;
}
}
// Add random trees (if we found ground and not at the edge)
if (groundY > 0 && x > 2 && x < width - 2 && Math.random() < 0.1) {
var treeHeight = 3 + Math.floor(Math.random() * 3);
// Remove grass block below the tree (set to null)
if (groundY + 1 < height && data[groundY + 1][x] === 'grass') {
data[groundY + 1][x] = null;
}
// Tree trunk
for (var ty = 1; ty <= treeHeight; ty++) {
if (groundY - ty >= 0) {
data[groundY - ty][x] = 'wood';
}
}
// Tree top (add leaves here)
var treeTop = groundY - treeHeight - 1;
if (treeTop >= 0) {
// Add leaves in a 3x3 area centered on the trunk top
for (var ly = -1; ly <= 1; ly++) {
for (var lx = -1; lx <= 1; lx++) {
var leafX = x + lx;
var leafY = treeTop + ly;
if (leafX >= 0 && leafX < width && leafY >= 0 && leafY < height && (ly !== 0 || lx !== 0) // Don't overwrite trunk top
) {
data[leafY][leafX] = 'Leaf'; // Use 'Leaf' as leaf block
}
}
}
// Place trunk top (wood) in the center
data[treeTop][x] = 'wood';
// Optionally, add a single leaf block above the trunk top
if (treeTop - 1 >= 0) {
data[treeTop - 1][x] = 'Leaf';
}
}
}
}
};
// Add water bodies (lakes, rivers)
this.addWater = function (data, width, height) {
// Add some water pools/lakes
var lakeAttempts = Math.floor(width / 20);
for (var l = 0; l < lakeAttempts; l++) {
var lakeX = 5 + Math.floor(Math.random() * (width - 10));
var lakeWidth = 3 + Math.floor(Math.random() * 5);
// Find ground level at lakeX
var lakeY = 0;
for (var y = 0; y < height; y++) {
if (data[y] && data[y][lakeX] === 'grass') {
lakeY = y;
break;
}
}
// Create lake
if (lakeY > 0) {
for (var wx = 0; wx < lakeWidth && lakeX + wx < width; wx++) {
if (data[lakeY] && data[lakeY][lakeX + wx]) {
data[lakeY][lakeX + wx] = 'water';
// Sometimes make deeper water
if (Math.random() < 0.5 && lakeY + 1 < height) {
data[lakeY + 1][lakeX + wx] = 'water';
}
}
}
}
}
};
return this;
};
var blockSize = 64;
var worldWidth = 100; // 100 blocks horizontal
var worldHeight = 54; // 54 blocks vertical
var gravity = 0;
// Game state
var gameStarted = false;
// Parse worldData from storage if it exists, checking for undefined/null values
// Pixel World System: Support for multiple worlds and world switching
var pixelWorlds = {
currentWorld: "default",
worlds: {},
// Load world data from storage or generate new
loadWorld: function loadWorld(worldName) {
var key = "pixelWorld_" + worldName;
var data;
try {
data = storage[key] && storage[key] !== "undefined" ? JSON.parse(storage[key]) : null;
} catch (e) {
data = null;
}
if (!data) {
data = generateWorld();
storage[key] = JSON.stringify(data);
}
this.worlds[worldName] = data;
this.currentWorld = worldName;
return data;
},
// Save current world data to storage
saveWorld: function saveWorld(worldName, data) {
var key = "pixelWorld_" + worldName;
storage[key] = JSON.stringify(data);
this.worlds[worldName] = data;
},
// Switch to a different world
switchWorld: function switchWorld(worldName) {
worldData = this.loadWorld(worldName);
this.currentWorld = worldName;
initializeWorld();
// Move character to main door entrance (bottom center) on world switch
if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") {
setCharacterToMainDoor();
}
setupUI();
},
// List all saved worlds (by checking storage keys)
listWorlds: function listWorlds() {
var result = [];
for (var k in storage) {
if (k.indexOf("pixelWorld_") === 0) {
result.push(k.replace("pixelWorld_", ""));
}
}
return result;
}
};
// Use pixelWorlds to load the default world at start
var worldData = pixelWorlds.loadWorld("default");
var inventory = storage.inventory || {
"grass": 0,
"stone": 0,
"wood": 0,
"water": 0
};
var currentTool = 'place'; // 'place' or 'break'
var selectedBlockType = 'grass';
var selectedInventorySlot = 0;
// Game containers
var worldContainer = new Container();
var uiContainer = new Container();
var inventoryContainer = new Container();
var toolbarContainer = new Container();
game.addChild(worldContainer);
game.addChild(uiContainer);
// Create player
var player = new Player();
player.x = storage.playerPosition ? storage.playerPosition.x : 1024;
player.y = storage.playerPosition ? storage.playerPosition.y : 1600;
worldContainer.addChild(player);
// Create character at the main door entrance (bottom center)
var character = new Character();
// Find the main door entrance (bottom center)
function setCharacterToMainDoor() {
var doorGridX = Math.floor(worldWidth / 2);
var doorGridY = worldHeight - 2;
character.x = doorGridX * blockSize + blockSize / 2;
character.y = doorGridY * blockSize + blockSize / 2;
}
setCharacterToMainDoor();
worldContainer.addChild(character);
// Initialize world blocks
function initializeWorld() {
// Clear any existing blocks
while (worldContainer.children.length > 1) {
// Keep player
worldContainer.removeChildAt(1);
}
// Load world data using pixelWorlds loader for Pixel World style loading
worldData = pixelWorlds.loadWorld(pixelWorlds.currentWorld);
// Place blocks based on worldData
for (var y = 0; y < worldData.length; y++) {
for (var x = 0; x < worldData[y].length; x++) {
if (worldData[y][x]) {
var blockType = worldData[y][x];
// If this is a main door location, use 'main_door' type
if (blockType === 'main_door') {
var block = new Block('main_door', blockSize);
} else {
var block = new Block(blockType, blockSize);
}
block.x = x * blockSize + blockSize / 2;
block.y = y * blockSize + blockSize / 2;
worldContainer.addChild(block);
}
}
}
// Fade in world for polish
worldContainer.alpha = 0;
tween(worldContainer, {
alpha: 1
}, {
duration: 600,
easing: tween.easeOutQuad
});
// Place a main door at all world enter locations if not present
// For this game, we define the main doors as blocks at the bottom center, top center, left center, and right center of the world
var entrances = [{
x: Math.floor(worldWidth / 2),
y: worldHeight - 2
},
// bottom center
{
x: Math.floor(worldWidth / 2),
y: 1
},
// top center
{
x: 1,
y: Math.floor(worldHeight / 2)
},
// left center
{
x: worldWidth - 2,
y: Math.floor(worldHeight / 2)
} // right center
];
for (var i = 0; i < entrances.length; i++) {
var ex = entrances[i].x;
var ey = entrances[i].y;
if (!worldData[ey]) worldData[ey] = [];
if (worldData[ey][ex] !== 'main_door') {
worldData[ey][ex] = 'main_door';
var mainDoorBlock = new Block('main_door', blockSize);
mainDoorBlock.x = ex * blockSize + blockSize / 2;
mainDoorBlock.y = ey * blockSize + blockSize / 2;
worldContainer.addChild(mainDoorBlock);
}
}
// Create platforms if this is a new world
if (!storage.platformsCreated) {
createPlatforms();
storage.platformsCreated = true;
}
}
// Generate a new world
function generateWorld() {
var worldGen = new WorldGenerator();
var data = worldGen.generate(worldWidth, worldHeight, blockSize);
// Place a main door at the bottom center of the world
var doorGridX = Math.floor(worldWidth / 2);
var doorGridY = worldHeight - 2;
if (!data[doorGridY]) data[doorGridY] = [];
data[doorGridY][doorGridX] = 'main_door';
// Place main doors at all four world entrances (bottom center, top center, left center, right center)
var entrances = [{
x: Math.floor(worldWidth / 2),
y: worldHeight - 2
},
// bottom center
{
x: Math.floor(worldWidth / 2),
y: 1
},
// top center
{
x: 1,
y: Math.floor(worldHeight / 2)
},
// left center
{
x: worldWidth - 2,
y: Math.floor(worldHeight / 2)
} // right center
];
for (var i = 0; i < entrances.length; i++) {
var ex = entrances[i].x;
var ey = entrances[i].y;
if (!data[ey]) data[ey] = [];
data[ey][ex] = 'main_door';
}
// Save the generated world
pixelWorlds.saveWorld(pixelWorlds.currentWorld, data);
// Give player some starter blocks
inventory = {
"grass": 10,
"stone": 10,
"wood": 10,
"water": 5
};
storage.inventory = inventory;
return data;
}
// Setup UI
function setupUI() {
// Create inventory bar
setupInventory();
// Create tool selection buttons
setupToolbar();
// Create control buttons (left, right, jump)
setupControls();
// Create main menu button
setupMainMenuButton();
}
// Add main menu button to return to menu
function setupMainMenuButton() {
var menuButton = new Button("Menu", function () {
// Hide game elements
gameStarted = false;
// Show main menu
mainMenu.show();
});
menuButton.x = 120;
menuButton.y = 100;
LK.gui.topRight.addChild(menuButton);
// Add a Change World button for switching between 'old world' and 'new world'
var changeWorldButton = new Button("Change World", function () {
// If current world is 'old world', switch to 'new world', else switch to 'old world'
var nextWorld = pixelWorlds.currentWorld === "old world" ? "new world" : "old world";
// If the world does not exist, generate it
if (!pixelWorlds.worlds[nextWorld]) {
var newWorldData = generateWorld();
pixelWorlds.saveWorld(nextWorld, newWorldData);
}
pixelWorlds.switchWorld(nextWorld);
// Optionally, reset player position for new world
player.x = 1024;
player.y = 1600;
storage.playerPosition = {
x: player.x,
y: player.y
};
// Move character to main door entrance (bottom center) on Change World
if (typeof character !== "undefined" && typeof setCharacterToMainDoor === "function") {
setCharacterToMainDoor();
}
});
// Move the button to the top left, but not in the top 100x100 area
changeWorldButton.x = 120;
changeWorldButton.y = 200;
LK.gui.topLeft.addChild(changeWorldButton);
// Add a character control button
var characterButton = new Button("Character", function () {
// Show character info or focus on character
var targetX = -character.x + 2048 / 2;
var targetY = -character.y + 2732 / 2;
// Animate the camera movement to the character
tween(worldContainer, {
x: targetX,
y: targetY
}, {
duration: 500,
easing: tween.easeOutQuad
});
});
characterButton.x = 120;
characterButton.y = 200;
LK.gui.topRight.addChild(characterButton);
}
function setupInventory() {
inventoryContainer = new Container();
var blockTypes = ['grass', 'stone', 'wood', 'water'];
var slotSpacing = 100;
// Create the background for selected slot
var selectedSlotBg = LK.getAsset('selectedSlot', {
anchorX: 0.5,
anchorY: 0.5
});
inventoryContainer.addChild(selectedSlotBg);
// Create inventory slots
for (var i = 0; i < blockTypes.length; i++) {
var slot = new InventorySlot(i, blockTypes[i]);
slot.x = i * slotSpacing;
slot.countText.setText((inventory[blockTypes[i]] || 0).toString());
inventoryContainer.addChild(slot);
}
// Position the inventory at the top center
inventoryContainer.x = 2048 / 2 - (blockTypes.length - 1) * slotSpacing / 2;
inventoryContainer.y = 100;
// Create inventory title
var inventoryTitle = new Text2("Inventory", {
size: 40,
fill: 0xFFFFFF
});
inventoryTitle.anchor.set(0.5, 1);
inventoryTitle.x = (blockTypes.length - 1) * slotSpacing / 2;
inventoryTitle.y = -20;
inventoryContainer.addChild(inventoryTitle);
// Update selected slot visual
updateSelectedSlot();
LK.gui.top.addChild(inventoryContainer);
}
function setupToolbar() {
toolbarContainer = new Container();
// Place tool
var placeToolBtn = new Button("Place", function () {
currentTool = 'place';
updateToolbarSelection();
});
placeToolBtn.x = 0;
toolbarContainer.addChild(placeToolBtn);
// Break tool
var breakToolBtn = new Button("Break", function () {
currentTool = 'break';
updateToolbarSelection();
});
breakToolBtn.x = 250;
toolbarContainer.addChild(breakToolBtn);
// Position toolbar at bottom right
toolbarContainer.x = 2048 - 400;
toolbarContainer.y = 2732 - 100;
LK.gui.bottomRight.addChild(toolbarContainer);
// Update the initial tool selection
updateToolbarSelection();
}
function setupControls() {
// Container for player movement controls (bottom left)
var moveControls = new Container();
// Player Up
var upBtn = new ControlButton("↑", function () {
player.jump();
});
upBtn.x = 100;
upBtn.y = -100;
moveControls.addChild(upBtn);
// Player Left
var leftBtn = new ControlButton("←", function () {
player.move('left');
});
leftBtn.x = 0;
leftBtn.y = 0;
moveControls.addChild(leftBtn);
// Player Stop
var stopBtn = new ControlButton("■", function () {
player.stopMove();
});
stopBtn.x = 100;
stopBtn.y = 0;
moveControls.addChild(stopBtn);
// Player Right
var rightBtn = new ControlButton("→", function () {
player.move('right');
});
rightBtn.x = 200;
rightBtn.y = 0;
moveControls.addChild(rightBtn);
// Player Down (optional, not used)
var downBtn = new ControlButton("↓", function () {
// Could be used for crouch or drop
});
downBtn.x = 100;
downBtn.y = 100;
moveControls.addChild(downBtn);
// Position at bottom left, but move to the left down corner
moveControls.x = 250;
moveControls.y = 2732 - 250;
LK.gui.bottomLeft.addChild(moveControls);
// Container for character movement controls (bottom left, above player controls)
var charControls = new Container();
// Character Up Button
var charUpBtn = new ControlButton("↑", function () {
character.jump();
}, 120, 120);
charUpBtn.x = 100;
charUpBtn.y = -100;
charControls.addChild(charUpBtn);
// Character Left Button
var charLeftBtn = new ControlButton("←", function () {
character.startMoving(-1);
}, 120, 120);
charLeftBtn.x = 0;
charLeftBtn.y = 0;
charControls.addChild(charLeftBtn);
// Character Stop Button
var charStopBtn = new ControlButton("■", function () {
character.stopMoving();
}, 120, 120);
charStopBtn.x = 100;
charStopBtn.y = 0;
charControls.addChild(charStopBtn);
// Character Right Button
var charRightBtn = new ControlButton("→", function () {
character.startMoving(1);
}, 120, 120);
charRightBtn.x = 200;
charRightBtn.y = 0;
charControls.addChild(charRightBtn);
// Position at bottom left, just above player controls (left down)
charControls.x = 50;
charControls.y = 2732 - 450;
LK.gui.bottomLeft.addChild(charControls);
}
function updateSelectedSlot() {
var selectedSlotBg = inventoryContainer.getChildAt(0);
var slot = inventoryContainer.getChildAt(selectedInventorySlot + 1);
if (slot) {
selectedSlotBg.x = slot.x;
selectedSlotBg.y = slot.y;
selectedBlockType = slot.type;
}
}
function updateToolbarSelection() {
var placeToolBtn = toolbarContainer.getChildAt(0);
var breakToolBtn = toolbarContainer.getChildAt(1);
if (currentTool === 'place') {
if (placeToolBtn && placeToolBtn.buttonText) {
placeToolBtn.buttonText.style = {
fill: 0xFFFF00
};
}
if (breakToolBtn && breakToolBtn.buttonText) {
breakToolBtn.buttonText.style = {
fill: 0xFFFFFF
};
}
} else {
if (placeToolBtn && placeToolBtn.buttonText) {
placeToolBtn.buttonText.style = {
fill: 0xFFFFFF
};
}
if (breakToolBtn && breakToolBtn.buttonText) {
breakToolBtn.buttonText.style = {
fill: 0xFFFF00
};
}
}
}
function selectInventorySlot(index) {
selectedInventorySlot = index;
updateSelectedSlot();
// Play select sound
LK.getSound('select').play();
}
function updateInventoryDisplay() {
for (var i = 1; i < inventoryContainer.children.length; i++) {
var slot = inventoryContainer.getChildAt(i);
if (slot && typeof slot.updateCount === 'function') {
slot.updateCount(inventory[slot.type] || 0);
} else if (slot && slot.countText) {
slot.countText.setText((inventory[slot.type] || 0).toString());
}
}
}
function updateCamera() {
// Calculate how much to offset the world to center the player
var targetX = -player.x + 2048 / 2;
var targetY = -player.y + 2732 / 2;
// Add bounds to prevent seeing outside the world
targetX = Math.min(0, Math.max(targetX, -(worldWidth * blockSize - 2048)));
targetY = Math.min(0, Math.max(targetY, -(worldHeight * blockSize - 2732)));
// Smooth camera movement
worldContainer.x = targetX;
worldContainer.y = targetY;
}
function placeBlock(x, y) {
// Convert screen position to world position
var worldX = x - worldContainer.x;
var worldY = y - worldContainer.y;
// Convert world position to grid position
var gridX = Math.floor(worldX / blockSize);
var gridY = Math.floor(worldY / blockSize);
// Check if we have this block type in inventory
if (inventory[selectedBlockType] <= 0) {
return;
}
// Check if position is valid
if (gridX < 0 || gridX >= worldWidth || gridY < 0 || gridY >= worldHeight) {
return;
}
// Check if space is empty
if (!worldData[gridY] || gridX >= worldData[gridY].length || gridX < 0 || gridY < 0 || worldData[gridY][gridX] !== null) {
return;
}
// Don't allow placing blocks where the player is standing
var playerGridX = Math.floor(player.x / blockSize);
var playerGridY = Math.floor(player.y / blockSize);
var playerGridYBottom = Math.floor((player.y - 60) / blockSize); // Check player's head position too
if (gridX === playerGridX && (gridY === playerGridY || gridY === playerGridYBottom)) {
return; // Can't place block where player is standing
}
// Place the block
worldData[gridY][gridX] = selectedBlockType;
// Remove from inventory
inventory[selectedBlockType]--;
updateInventoryDisplay();
// Create the block visually
var block = new Block(selectedBlockType, blockSize);
block.x = gridX * blockSize + blockSize / 2;
block.y = gridY * blockSize + blockSize / 2;
worldContainer.addChild(block);
// Play place sound
LK.getSound('place').play();
// Save world data
// Save the data directly to storage
pixelWorlds.saveWorld(pixelWorlds.currentWorld, worldData);
storage.inventory = inventory;
}
// Game event handlers
game.down = function (x, y, obj) {
if (!gameStarted) return; // Don't handle interactions if game hasn't started
if (currentTool === 'place') {
placeBlock(x, y);
}
// Check if we clicked on the character
if (obj && (obj === character || obj.parent === character)) {
character.startDrag();
dragTarget = character;
// Prevent placing blocks when clicking character
return;
}
};
// Add move handler for dragging character
game.move = function (x, y, obj) {
if (dragTarget === character) {
// Convert global coordinates to character's local space
var localX = x - worldContainer.x;
var localY = y - worldContainer.y;
// Determine movement direction based on touch position relative to character
if (localX > character.x + 10) {
character.startMoving(1); // Move right
} else if (localX < character.x - 10) {
character.startMoving(-1); // Move left
} else {
character.stopMoving(); // Stop if touch is directly on character
}
}
};
// Add up handler to stop dragging
game.up = function (x, y, obj) {
if (dragTarget === character) {
character.stopDrag();
character.stopMoving(); // Make sure character stops moving when touch is released
dragTarget = null;
}
};
// Initialize drag target variable
var dragTarget = null;
// Track control state for continuous button presses
var controlState = {
left: false,
right: false,
jump: false
};
// Create platforms in the world
function createPlatforms() {
// Create several platforms at different positions
var platforms = [{
x: 10,
y: 20,
width: 8,
height: 1
}, {
x: 25,
y: 18,
width: 6,
height: 1
}, {
x: 5,
y: 15,
width: 4,
height: 1
}, {
x: 15,
y: 12,
width: 5,
height: 1
}, {
x: 30,
y: 10,
width: 7,
height: 1
}];
for (var i = 0; i < platforms.length; i++) {
var p = platforms[i];
var platform = new Platform(p.width, p.height);
platform.x = p.x * blockSize;
platform.y = p.y * blockSize;
worldContainer.addChild(platform);
// Add platform blocks to worldData
for (var x = 0; x < p.width; x++) {
for (var y = 0; y < p.height; y++) {
var gridX = p.x + x;
var gridY = p.y + y;
if (gridY < worldHeight && gridX < worldWidth) {
if (!worldData[gridY]) worldData[gridY] = [];
worldData[gridY][gridX] = 'stone';
}
}
}
}
// Save world data after creating platforms
pixelWorlds.saveWorld(pixelWorlds.currentWorld, worldData);
}
// --- Day-Night Cycle Variables ---
var dayNightCycle = {
time: 0,
// 0 to 1, where 0 is midnight, 0.5 is noon, 1 is midnight again
speed: 1 / (60 * 60 * 4),
// 4 minutes per full cycle (slower sun and moon movement)
sun: null,
moon: null,
skyColors: [{
t: 0.00,
color: 0x0a0a2a
},
// Midnight
{
t: 0.20,
color: 0x2a3a7a
},
// Early dawn
{
t: 0.30,
color: 0x87CEEB
},
// Morning
{
t: 0.50,
color: 0x87CEEB
},
// Noon
{
t: 0.70,
color: 0x2a3a7a
},
// Dusk
{
t: 0.85,
color: 0x0a0a2a
},
// Night
{
t: 1.00,
color: 0x0a0a2a
} // Midnight
]
};
// Add sun and moon visuals to the game background
if (!dayNightCycle.sun) {
dayNightCycle.sun = LK.getAsset('sun', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0.85
});
dayNightCycle.sun.y = 600;
dayNightCycle.sun.x = 2048 / 2;
dayNightCycle.sun.zIndex = -100;
game.addChild(dayNightCycle.sun);
}
if (!dayNightCycle.moon) {
dayNightCycle.moon = LK.getAsset('moon', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5,
x: 2048 / 2,
y: 2732 / 2,
alpha: 0.55
});
dayNightCycle.moon.y = 600;
dayNightCycle.moon.x = 2048 / 2;
dayNightCycle.moon.zIndex = -99;
game.addChild(dayNightCycle.moon);
}
// Helper to interpolate between two colors
function lerpColor(a, b, t) {
var ar = a >> 16 & 0xff,
ag = a >> 8 & 0xff,
ab = a & 0xff;
var br = b >> 16 & 0xff,
bg = b >> 8 & 0xff,
bb = b & 0xff;
var rr = Math.round(ar + (br - ar) * t);
var rg = Math.round(ag + (bg - ag) * t);
var rb = Math.round(ab + (bb - ab) * t);
return rr << 16 | rg << 8 | rb;
}
// Helper to get sky color for current time
function getSkyColor(t) {
var colors = dayNightCycle.skyColors;
for (var i = 0; i < colors.length - 1; i++) {
if (t >= colors[i].t && t <= colors[i + 1].t) {
var localT = (t - colors[i].t) / (colors[i + 1].t - colors[i].t);
return lerpColor(colors[i].color, colors[i + 1].color, localT);
}
}
return colors[colors.length - 1].color;
}
// --- Butterfly Spawner ---
var butterflies = [];
function spawnButterflies() {
// Only spawn during the day
var t = dayNightCycle.time;
var isDay = t > 0.18 && t < 0.82;
if (isDay && butterflies.length < 8) {
if (Math.random() < 0.03) {
var b = new Butterfly();
game.addChild(b);
butterflies.push(b);
}
}
// Remove excess or faded butterflies
for (var i = butterflies.length - 1; i >= 0; i--) {
if (butterflies[i].alpha < 0.01) {
butterflies[i].destroy();
butterflies.splice(i, 1);
}
}
}
// --- Parallax Cloud Layer ---
var clouds = [];
function createCloud(x, y, scale, speed, alpha) {
var c = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale * (1.2 + Math.random() * 0.5),
scaleY: scale * (0.7 + Math.random() * 0.3),
x: x,
y: y,
alpha: alpha
});
c._cloudSpeed = speed;
c._cloudScale = scale;
c._cloudAlpha = alpha;
c.zIndex = -50;
game.addChild(c);
return c;
}
function spawnClouds() {
if (clouds.length < 7 && Math.random() < 0.02) {
var y = 400 + Math.random() * 400;
var scale = 2.5 + Math.random() * 2.5;
var speed = 0.2 + Math.random() * 0.3;
var alpha = 0.18 + Math.random() * 0.12;
var x = -200;
var c = createCloud(x, y, scale, speed, alpha);
clouds.push(c);
}
for (var i = clouds.length - 1; i >= 0; i--) {
var c = clouds[i];
c.x += c._cloudSpeed;
// Fade out at night
var t = dayNightCycle.time;
var isDay = t > 0.18 && t < 0.82;
c.alpha = isDay ? c._cloudAlpha : c._cloudAlpha * 0.3;
if (c.x > 2048 + 300) {
c.destroy();
clouds.splice(i, 1);
}
}
}
// --- Shooting Stars ---
var shootingStars = [];
function spawnShootingStar() {
// Only at night
var t = dayNightCycle.time;
var isNight = t < 0.12 || t > 0.88;
if (isNight && Math.random() < 0.012) {
var star = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5 + Math.random() * 0.7,
scaleY: 0.18 + Math.random() * 0.12,
x: 200 + Math.random() * 1600,
y: 300 + Math.random() * 400,
tint: 0xffffff,
alpha: 0.7
});
star._vx = 12 + Math.random() * 8;
star._vy = 3 + Math.random() * 2;
star._life = 0;
game.addChild(star);
shootingStars.push(star);
}
for (var i = shootingStars.length - 1; i >= 0; i--) {
var s = shootingStars[i];
s.x += s._vx;
s.y += s._vy;
s._life += 1;
s.alpha *= 0.97;
if (s.x > 2200 || s.y > 900 || s._life > 60 || s.alpha < 0.05) {
s.destroy();
shootingStars.splice(i, 1);
}
}
}
// Main game loop
game.update = function () {
// --- Day-Night Cycle Update ---
dayNightCycle.time += dayNightCycle.speed;
if (dayNightCycle.time > 1) dayNightCycle.time -= 1;
var skyColor = getSkyColor(dayNightCycle.time);
game.setBackgroundColor(skyColor);
// Sun and moon movement (circular path)
var sunAngle = Math.PI * 2 * (dayNightCycle.time - 0.25); // Noon at top
var moonAngle = sunAngle + Math.PI;
var sunRadius = 900;
var moonRadius = 900;
dayNightCycle.sun.x = 2048 / 2 + Math.cos(sunAngle) * sunRadius;
dayNightCycle.sun.y = 1200 + Math.sin(sunAngle) * sunRadius * 0.5;
dayNightCycle.sun.alpha = Math.max(0, Math.sin(sunAngle) * 0.9);
dayNightCycle.moon.x = 2048 / 2 + Math.cos(moonAngle) * moonRadius;
dayNightCycle.moon.y = 1200 + Math.sin(moonAngle) * moonRadius * 0.5;
dayNightCycle.moon.alpha = Math.max(0, Math.sin(moonAngle) * 0.7);
// --- Parallax Clouds Update ---
spawnClouds();
// --- Shooting Stars Update ---
spawnShootingStar();
// --- Butterflies Update ---
spawnButterflies();
for (var i = 0; i < butterflies.length; i++) {
butterflies[i].update();
}
// --- Day-Night Cycle Update ---
dayNightCycle.time += dayNightCycle.speed;
if (dayNightCycle.time > 1) dayNightCycle.time -= 1;
var skyColor = getSkyColor(dayNightCycle.time);
game.setBackgroundColor(skyColor);
// Sun and moon movement (circular path)
var sunAngle = Math.PI * 2 * (dayNightCycle.time - 0.25); // Noon at top
var moonAngle = sunAngle + Math.PI;
var sunRadius = 900;
var moonRadius = 900;
dayNightCycle.sun.x = 2048 / 2 + Math.cos(sunAngle) * sunRadius;
dayNightCycle.sun.y = 1200 + Math.sin(sunAngle) * sunRadius * 0.5;
dayNightCycle.sun.alpha = Math.max(0, Math.sin(sunAngle) * 0.9);
dayNightCycle.moon.x = 2048 / 2 + Math.cos(moonAngle) * moonRadius;
dayNightCycle.moon.y = 1200 + Math.sin(moonAngle) * moonRadius * 0.5;
dayNightCycle.moon.alpha = Math.max(0, Math.sin(moonAngle) * 0.7);
// Only update game elements if the game has started
if (gameStarted) {
// --- Main Door Logic Start ---
// Find all main door blocks in the world and check if player is at any of them
if (typeof player.lastWasAtDoor === "undefined") player.lastWasAtDoor = false;
var atDoor = false;
for (var y = 0; y < worldData.length; y++) {
for (var x = 0; x < worldData[y].length; x++) {
if (worldData[y][x] === 'main_door') {
var doorX = x * blockSize + blockSize / 2;
var doorY = y * blockSize + blockSize / 2;
var dx = Math.abs(player.x - doorX);
var dy = Math.abs(player.y - doorY);
if (dx < blockSize * 0.7 && dy < blockSize * 0.7) {
atDoor = true;
}
}
}
}
// Trigger world enter menu on entering any main door
if (!player.lastWasAtDoor && atDoor) {
worldEnterMenu.show();
gameStarted = false;
}
player.lastWasAtDoor = atDoor;
// --- Main Door Logic End ---
// Update player
player.update();
// Update character
// Handle character movement based on controlState
if (controlState) {
if (controlState.left) {
character.startMoving(-1);
} else if (controlState.right) {
character.startMoving(1);
} else {
character.stopMoving();
}
if (controlState.jump) {
character.jump();
controlState.jump = false; // Only jump once per press
}
}
character.update();
// Simple AI: Make character move back and forth
if (LK.ticks % 120 === 0 && !dragTarget) {
// Change direction every 2 seconds (only if not being controlled by player)
character.startMoving(character.direction * -1);
}
// Save game data periodically
if (LK.ticks % 300 === 0) {
// Convert worldData to string before storing
pixelWorlds.saveWorld(pixelWorlds.currentWorld, worldData);
storage.inventory = inventory;
}
}
};
// Create main menu
var mainMenu = new MainMenu();
mainMenu.x = 0;
mainMenu.y = 0;
game.addChild(mainMenu);
// Create world enter menu
var worldEnterMenu = new WorldEnterMenu();
worldEnterMenu.x = 0;
worldEnterMenu.y = 0;
worldEnterMenu.visible = false;
game.addChild(worldEnterMenu);
// Show main menu to start
mainMenu.show();
// Remove auto-show of worldEnterMenu. Instead, show it when player enters the main door.
// --- Vignette Overlay ---
var vignette = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 18,
scaleY: 12,
x: 2048 / 2,
y: 2732 / 2,
tint: 0x000000,
alpha: 0.18
});
vignette.zIndex = 9999;
game.addChild(vignette);
// Start background music
LK.playMusic('bgmusic');
;
;
the character has a red hair, green t shirt and blue pant. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
wooden. In-Game asset. 2d. High contrast. No shadows
rock. In-Game asset. 2d. High contrast. No shadows
grasses. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
dirt. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
make the down side is square
bedrock. In-Game asset. 2d. High contrast. No shadows
left button. In-Game asset. 2d. High contrast. No shadows
right button. In-Game asset. 2d. High contrast. No shadows
up button. In-Game asset. 2d. High contrast. No shadows
sun. In-Game asset. 2d. High contrast. No shadows
moon. In-Game asset. 2d. High contrast. No shadows
cloud. In-Game asset. 2d. High contrast. No shadows
make a butterfly (horizontal). In-Game asset. 2d. High contrast. No shadows