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