User prompt
oyuncu köylüye saldırırsa köylü oyuncudan kaçar veya saldırır
User prompt
köylüler zıplayabilir
User prompt
köylüler blok koyabilir ve kırabilir
User prompt
köylüleri öldürebilelim ve yerçekiminden etkilensin
User prompt
make villager to inventory
User prompt
make villager egg
User prompt
yeni biomlar = lav havuzları köyler (köylülerde var) mağralar
User prompt
başlangıç menüsü ekle
User prompt
oyuna farklı farklı başarımlar ekle
User prompt
oyuncu lav ve suyun içinden geçsin ve lavdan geçince efekt gelsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
su ve lav birleşince bir tepkime olsun
User prompt
başarımlar yerinin boyunu 2ye böl
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Achievement = Container.expand(function (achievementData) { var self = Container.call(this); // Background var bgGraphics = self.attachAsset('menuBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.3, alpha: 0.9 }); // Title text var titleText = new Text2('Achievement Unlocked!', { size: 36, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -30; self.addChild(titleText); // Achievement name var nameText = new Text2(achievementData.name, { size: 28, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0.5); nameText.x = 0; nameText.y = 0; self.addChild(nameText); // Achievement description var descText = new Text2(achievementData.description, { size: 22, fill: 0xCCCCCC }); descText.anchor.set(0.5, 0.5); descText.x = 0; descText.y = 25; self.addChild(descText); self.show = function () { self.alpha = 0; self.scaleX = 0.5; self.scaleY = 0.5; self.visible = true; tween(self, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); // Auto hide after 3 seconds LK.setTimeout(function () { tween(self, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 200, onFinish: function onFinish() { self.visible = false; } }); }, 3000); }; // Initially hidden self.visible = false; return self; }); var AchievementMenu = Container.expand(function () { var self = Container.call(this); self.isVisible = false; // Background var bgGraphics = self.attachAsset('menuBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9, scaleX: 1.2, scaleY: 3.0 }); // Title text var titleText = new Text2('Achievements', { size: 48, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -300; self.addChild(titleText); // Close button var closeButton = self.attachAsset('closeButton', { anchorX: 0.5, anchorY: 0.5 }); closeButton.x = 350; closeButton.y = -300; // Achievement list container var achievementList = new Container(); self.addChild(achievementList); // Page navigation buttons var prevPageButton = self.attachAsset('menuButton', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8, scaleX: 0.8, scaleY: 0.5 }); prevPageButton.x = -200; prevPageButton.y = 300; var prevPageText = new Text2('PREV', { size: 28, fill: 0xFFFFFF }); prevPageText.anchor.set(0.5, 0.5); prevPageText.x = -200; prevPageText.y = 300; self.addChild(prevPageText); var nextPageButton = self.attachAsset('menuButton', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8, scaleX: 0.8, scaleY: 0.5 }); nextPageButton.x = 200; nextPageButton.y = 300; var nextPageText = new Text2('NEXT', { size: 28, fill: 0xFFFFFF }); nextPageText.anchor.set(0.5, 0.5); nextPageText.x = 200; nextPageText.y = 300; self.addChild(nextPageText); // Page indicator text var pageIndicatorText = new Text2('', { size: 24, fill: 0xCCCCCC }); pageIndicatorText.anchor.set(0.5, 0.5); pageIndicatorText.x = 0; pageIndicatorText.y = 300; self.addChild(pageIndicatorText); self.updateAchievements = function () { // Clear existing achievement displays while (achievementList.children.length > 0) { achievementList.removeChild(achievementList.children[0]); } // Get all achievement IDs as array for pagination var allAchievementIds = []; for (var achievementId in achievementDefinitions) { allAchievementIds.push(achievementId); } // Calculate pagination var totalPages = Math.ceil(allAchievementIds.length / achievementsPerPage); var startIndex = currentAchievementPage * achievementsPerPage; var endIndex = Math.min(startIndex + achievementsPerPage, allAchievementIds.length); // Update page indicator pageIndicatorText.setText('Page ' + (currentAchievementPage + 1) + ' of ' + totalPages); // Update button visibility prevPageButton.alpha = currentAchievementPage > 0 ? 0.8 : 0.3; prevPageText.fill = currentAchievementPage > 0 ? 0xFFFFFF : 0x666666; nextPageButton.alpha = currentAchievementPage < totalPages - 1 ? 0.8 : 0.3; nextPageText.fill = currentAchievementPage < totalPages - 1 ? 0xFFFFFF : 0x666666; var yOffset = -200; var achievementIndex = 0; // Display achievements for current page for (var i = startIndex; i < endIndex; i++) { var achievementId = allAchievementIds[i]; var achievement = achievementDefinitions[achievementId]; var isUnlocked = achievements[achievementId] || false; // Achievement container var achievementContainer = new Container(); achievementContainer.y = yOffset + achievementIndex * 80; // Achievement background var achievementBg = achievementContainer.attachAsset('menuButton', { anchorX: 0.5, anchorY: 0.5, alpha: isUnlocked ? 0.8 : 0.3, scaleX: 2.8, scaleY: 0.6 }); // Achievement name var nameText = new Text2(achievement.name, { size: 32, fill: isUnlocked ? 0xFFD700 : 0x888888 }); nameText.anchor.set(0.5, 0.5); nameText.x = 0; nameText.y = -15; achievementContainer.addChild(nameText); // Achievement description var descText = new Text2(achievement.description, { size: 24, fill: isUnlocked ? 0xFFFFFF : 0x666666 }); descText.anchor.set(0.5, 0.5); descText.x = 0; descText.y = 10; achievementContainer.addChild(descText); // Status text var statusText = new Text2(isUnlocked ? 'UNLOCKED' : 'LOCKED', { size: 20, fill: isUnlocked ? 0x00FF00 : 0xFF4444 }); statusText.anchor.set(1, 0.5); statusText.x = 150; statusText.y = 0; achievementContainer.addChild(statusText); achievementList.addChild(achievementContainer); achievementIndex++; } }; // Page navigation interactions prevPageButton.down = function (x, y, obj) { if (currentAchievementPage > 0) { prevPageButton.alpha = 1.0; currentAchievementPage--; self.updateAchievements(); } }; prevPageButton.up = function (x, y, obj) { prevPageButton.alpha = currentAchievementPage > 0 ? 0.8 : 0.3; }; nextPageButton.down = function (x, y, obj) { var totalPages = Math.ceil(Object.keys(achievementDefinitions).length / achievementsPerPage); if (currentAchievementPage < totalPages - 1) { nextPageButton.alpha = 1.0; currentAchievementPage++; self.updateAchievements(); } }; nextPageButton.up = function (x, y, obj) { var totalPages = Math.ceil(Object.keys(achievementDefinitions).length / achievementsPerPage); nextPageButton.alpha = currentAchievementPage < totalPages - 1 ? 0.8 : 0.3; }; // Close button interaction closeButton.down = function (x, y, obj) { closeButton.alpha = 0.7; self.hide(); }; closeButton.up = function (x, y, obj) { closeButton.alpha = 1.0; }; self.show = function () { self.isVisible = true; currentAchievementPage = 0; // Reset to first page when opening self.updateAchievements(); self.alpha = 0; self.visible = true; tween(self, { alpha: 1 }, { duration: 200 }); }; self.hide = function () { self.isVisible = false; tween(self, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.visible = false; } }); }; // Initially hidden self.visible = false; return self; }); var Block = Container.expand(function (blockType) { var self = Container.call(this); self.blockType = blockType; self.gridX = 0; self.gridY = 0; var blockGraphics = self.attachAsset(blockType, { anchorX: 0.5, anchorY: 0.5 }); // Add a subtle border effect blockGraphics.alpha = 0.9; self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; }; return self; }); var BlockSelectionMenu = Container.expand(function () { var self = Container.call(this); self.isVisible = false; // Background var bgGraphics = self.attachAsset('menuBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9, scaleY: 4.0 }); // Title text var titleText = new Text2('Select Block Type', { size: 48, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -400; self.addChild(titleText); // Close button var closeButton = self.attachAsset('closeButton', { anchorX: 0.5, anchorY: 0.5 }); closeButton.x = 280; closeButton.y = -400; // Block selection buttons self.menuButtons = []; var allBlockTypes = Object.keys(inventory); var buttonsPerRow = 3; var buttonSpacing = 160; for (var i = 0; i < allBlockTypes.length; i++) { var button = new Container(); // Button background var buttonBg = button.attachAsset('menuButton', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); // Block preview var blockPreview = button.attachAsset(allBlockTypes[i], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); // Block name text var nameText = new Text2(allBlockTypes[i], { size: 26, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0); nameText.x = 0; nameText.y = 45; button.addChild(nameText); // Inventory count text var countText = new Text2(inventory[allBlockTypes[i]].toString(), { size: 24, fill: 0xFFDD44 }); countText.anchor.set(0.5, 0); countText.x = 0; countText.y = 65; button.addChild(countText); // Position button var col = i % buttonsPerRow; var row = Math.floor(i / buttonsPerRow); button.x = (col - 1) * buttonSpacing; button.y = row * buttonSpacing - 30; // Store button data button.blockType = allBlockTypes[i]; button.bgGraphics = buttonBg; button.countText = countText; // Button interaction button.down = function (x, y, obj) { this.bgGraphics.alpha = 1.0; selectedBlockType = this.blockType; updateToolbarSelection(); self.hide(); }; button.up = function (x, y, obj) { this.bgGraphics.alpha = 0.8; }; self.menuButtons.push(button); self.addChild(button); } // Close button interaction closeButton.down = function (x, y, obj) { closeButton.alpha = 0.7; self.hide(); }; closeButton.up = function (x, y, obj) { closeButton.alpha = 1.0; }; self.show = function () { self.isVisible = true; self.alpha = 0; self.visible = true; tween(self, { alpha: 1 }, { duration: 200 }); }; self.hide = function () { self.isVisible = false; tween(self, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.visible = false; } }); }; self.updateInventoryCounts = function () { for (var i = 0; i < self.menuButtons.length; i++) { var button = self.menuButtons[i]; if (button.countText && button.blockType) { button.countText.setText(inventory[button.blockType].toString()); } } }; // Initially hidden self.visible = false; return self; }); var Character = Container.expand(function () { var self = Container.call(this); var characterGraphics = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5 }); self.gridX = 0; self.gridY = 0; self.targetX = 0; self.targetY = 0; self.isMoving = false; self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.targetX = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.targetY = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; if (!self.isMoving) { self.x = self.targetX; self.y = self.targetY; } }; self.moveToGrid = function (gridX, gridY) { if (self.isMoving) return false; if (!isValidGridPosition(gridX, gridY)) return false; // Check if there's a block at the target position (only block movement if not flying) if (!isFlying && grid[gridX] && grid[gridX][gridY] !== null) return false; self.isMoving = true; self.gridX = gridX; self.gridY = gridY; self.targetX = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.targetY = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; // Reset gravity when manually moving (especially important in flying mode) if (isFlying) { characterGravity = 0; } // Adjust movement speed based on flying state var moveDuration = isFlying ? 100 : 200; tween(self, { x: self.targetX, y: self.targetY }, { duration: moveDuration, easing: isFlying ? tween.easeOut : tween.easeInOut, onFinish: function onFinish() { self.isMoving = false; // Track movement for achievements moveCount++; storage.moveCount = moveCount; // Track jumping (moving up) for boooing achievement if (gridY < self.gridY) { jumpCount++; storage.jumpCount = jumpCount; } checkAllAchievements(); } }); return true; }; self.update = function () { // Reset gravity when flying - do this first if (isFlying) { characterGravity = 0; } else { // Apply gravity if character is not moving, not supported, and not flying if (!self.isMoving) { var hasSupport = false; // Check if character has support below (block or ground) var belowY = self.gridY + 1; if (belowY >= GRID_HEIGHT) { // At bottom of world, has support hasSupport = true; } else if (grid[self.gridX] && grid[self.gridX][belowY] !== null) { // Block below, has support hasSupport = true; } if (!hasSupport) { // Character should fall characterGravity += characterGravityAcceleration; if (characterGravity > characterMaxFallSpeed) { characterGravity = characterMaxFallSpeed; } // Find where character should fall to var fallToY = self.gridY; for (var checkY = self.gridY + 1; checkY < GRID_HEIGHT; checkY++) { if (grid[self.gridX][checkY] !== null) { break; } fallToY = checkY; } // Move character down if there's a place to fall if (fallToY > self.gridY) { var targetY = Math.min(fallToY, self.gridY + Math.floor(characterGravity)); if (targetY !== self.gridY) { self.moveToGrid(self.gridX, targetY); } } } else { // Character has support, reset gravity characterGravity = 0; } } } // Check if character is touching a portal if (!self.isMoving) { for (var i = 0; i < placedBlocks.length; i++) { var block = placedBlocks[i]; if (block.blockType === 'portal' && block.gridX === self.gridX && block.gridY === self.gridY) { block.teleportCharacter(); break; } } } // Character is always on top if (self.parent) { var parent = self.parent; parent.removeChild(self); parent.addChild(self); } }; return self; }); var ControlButton = Container.expand(function (direction) { var self = Container.call(this); self.direction = direction; var bgGraphics = self.attachAsset('controlBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); var arrowGraphics = self.attachAsset('arrow' + direction.charAt(0).toUpperCase() + direction.slice(1), { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); self.down = function (x, y, obj) { bgGraphics.alpha = 1.0; var newX = character.gridX; var newY = character.gridY; if (direction === 'up') newY--;else if (direction === 'down') newY++;else if (direction === 'left') newX--;else if (direction === 'right') newX++; character.moveToGrid(newX, newY); }; self.up = function (x, y, obj) { bgGraphics.alpha = 0.7; }; return self; }); var GridCell = Container.expand(function () { var self = Container.call(this); var cellGraphics = self.attachAsset('gridCell', { anchorX: 0.5, anchorY: 0.5, alpha: 0.1 }); self.gridX = 0; self.gridY = 0; self.isEmpty = true; self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; }; self.highlight = function () { cellGraphics.alpha = 0.3; }; self.unhighlight = function () { cellGraphics.alpha = 0.1; }; return self; }); var Lava = Container.expand(function () { var self = Container.call(this); self.blockType = 'lava'; self.gridX = 0; self.gridY = 0; self.isFlowing = false; self.flowCooldown = 0; self.damageTimer = 0; var lavaGraphics = self.attachAsset('lava', { anchorX: 0.5, anchorY: 0.5 }); // Add glowing effect lavaGraphics.alpha = 0.9; self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; }; self.update = function () { // Pulsing glow effect lavaGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.15) * 0.15; lavaGraphics.tint = 0xff4500 + Math.floor(Math.sin(LK.ticks * 0.1) * 20) * 0x010000; // Handle damage to character if (character && character.gridX === self.gridX && character.gridY === self.gridY && !character.isMoving) { self.damageTimer++; if (self.damageTimer >= 60) { // 1 second of contact // Flash character red and teleport away from lava LK.effects.flashObject(character, 0xff0000, 500); LK.effects.flashScreen(0xff0000, 300); // Move character to nearest safe position self.teleportCharacterToSafety(); self.damageTimer = 0; } } else { self.damageTimer = 0; } // Lava flowing mechanics disabled - no spreading // if (self.flowCooldown > 0) { // self.flowCooldown--; // } else { // self.tryToFlow(); // self.flowCooldown = 120; // Flow every 2 seconds // } }; self.teleportCharacterToSafety = function () { // Find nearest safe position var safePositions = []; for (var radius = 1; radius <= 5; radius++) { for (var dx = -radius; dx <= radius; dx++) { for (var dy = -radius; dy <= radius; dy++) { var checkX = self.gridX + dx; var checkY = self.gridY + dy; if (isValidGridPosition(checkX, checkY) && grid[checkX][checkY] === null) { // Check if position has ground support below var hasSupport = false; if (checkY + 1 >= GRID_HEIGHT) { hasSupport = true; } else if (grid[checkX][checkY + 1] !== null && grid[checkX][checkY + 1].blockType !== 'lava') { hasSupport = true; } if (hasSupport) { safePositions.push({ x: checkX, y: checkY }); } } } } if (safePositions.length > 0) break; } if (safePositions.length > 0) { var safePos = safePositions[0]; // Take closest safe position character.setGridPosition(safePos.x, safePos.y); } }; self.tryToFlow = function () { // Lava flows down first, then horizontally var flowDirections = [{ x: 0, y: 1 }, // Down { x: -1, y: 0 }, // Left { x: 1, y: 0 }, // Right { x: -1, y: 1 }, // Down-left { x: 1, y: 1 } // Down-right ]; for (var i = 0; i < flowDirections.length; i++) { var dir = flowDirections[i]; var targetX = self.gridX + dir.x; var targetY = self.gridY + dir.y; if (isValidGridPosition(targetX, targetY) && grid[targetX][targetY] === null) { // Only flow if there are less than 3 lava blocks already var nearbyLavaCount = 0; for (var checkX = targetX - 1; checkX <= targetX + 1; checkX++) { for (var checkY = targetY - 1; checkY <= targetY + 1; checkY++) { if (isValidGridPosition(checkX, checkY) && grid[checkX][checkY] !== null && grid[checkX][checkY].blockType === 'lava') { nearbyLavaCount++; } } } if (nearbyLavaCount < 3) { // Create new lava block var newLava = new Lava(); newLava.setGridPosition(targetX, targetY); game.addChild(newLava); grid[targetX][targetY] = newLava; placedBlocks.push(newLava); LK.effects.flashObject(newLava, 0xffff00, 300); break; } } } }; return self; }); var Portal = Container.expand(function () { var self = Container.call(this); self.blockType = 'portal'; self.gridX = 0; self.gridY = 0; self.isActive = true; self.cooldownTime = 0; var portalGraphics = self.attachAsset('portal', { anchorX: 0.5, anchorY: 0.5 }); // Add glowing effect portalGraphics.alpha = 0.8; self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; }; self.teleportCharacter = function () { if (!self.isActive || self.cooldownTime > 0) return false; if (!character || character.isMoving) return false; // Find all other active portals var otherPortals = []; for (var i = 0; i < placedBlocks.length; i++) { var block = placedBlocks[i]; if (block !== self && block.blockType === 'portal' && block.isActive && block.cooldownTime <= 0) { otherPortals.push(block); } } if (otherPortals.length === 0) { // No other portals available - teleport to heaven! // Create heaven effect with golden background and clouds game.setBackgroundColor(0xFFD700); // Golden sky // Flash screen with heavenly light LK.effects.flashScreen(0xFFFFFF, 2000); // Teleport character to top of world center var heavenX = Math.floor(GRID_WIDTH / 2); var heavenY = 0; // Top of the world character.setGridPosition(heavenX, heavenY); // Add visual effects LK.effects.flashObject(self, 0xFFD700, 1000); LK.effects.flashObject(character, 0xFFFFFF, 1000); // Play portal sound LK.getSound('portal').play(); // Set cooldown self.cooldownTime = 300; // 5 seconds cooldown // Reset background after effect LK.setTimeout(function () { game.setBackgroundColor(0x87CEEB); // Back to sky blue }, 3000); return true; } // Choose random portal to teleport to var targetPortal = otherPortals[Math.floor(Math.random() * otherPortals.length)]; // Teleport character character.setGridPosition(targetPortal.gridX, targetPortal.gridY); // Add visual effects LK.effects.flashObject(self, 0x9400d3, 500); LK.effects.flashObject(targetPortal, 0x9400d3, 500); LK.effects.flashObject(character, 0x00ffff, 300); // Play portal sound LK.getSound('portal').play(); // Set cooldown for both portals self.cooldownTime = 180; // 3 seconds at 60fps targetPortal.cooldownTime = 180; return true; }; self.update = function () { // Handle cooldown if (self.cooldownTime > 0) { self.cooldownTime--; portalGraphics.alpha = 0.3 + self.cooldownTime / 180 * 0.5; } else { portalGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.1) * 0.2; // Pulsing effect } // Check if character is on portal if (character && character.gridX === self.gridX && character.gridY === self.gridY && !character.isMoving) { self.teleportCharacter(); } }; return self; }); var Sand = Container.expand(function () { var self = Container.call(this); self.blockType = 'sand'; self.gridX = 0; self.gridY = 0; self.isFalling = false; self.fallSpeed = 0; self.maxFallSpeed = 8; self.fallAcceleration = 0.8; var sandGraphics = self.attachAsset('sand', { anchorX: 0.5, anchorY: 0.5 }); self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; }; self.update = function () { // Check if sand should fall if (!self.isFalling) { var hasSupport = false; var belowY = self.gridY + 1; // Check if at bottom or has block below if (belowY >= GRID_HEIGHT) { hasSupport = true; } else if (grid[self.gridX] && grid[self.gridX][belowY] !== null) { hasSupport = true; } // Start falling if no support if (!hasSupport) { self.isFalling = true; self.fallSpeed = 0; } } // Handle falling physics if (self.isFalling) { self.fallSpeed += self.fallAcceleration; if (self.fallSpeed > self.maxFallSpeed) { self.fallSpeed = self.maxFallSpeed; } // Calculate new grid position var newGridY = self.gridY + Math.floor(self.fallSpeed / 4); // Check if we can fall to new position if (newGridY < GRID_HEIGHT && (grid[self.gridX][newGridY] === null || grid[self.gridX][newGridY] === self)) { // Update grid if (grid[self.gridX][self.gridY] === self) { grid[self.gridX][self.gridY] = null; } self.gridY = newGridY; grid[self.gridX][self.gridY] = self; self.setGridPosition(self.gridX, self.gridY); } else { // Stop falling - hit something or reached bottom self.isFalling = false; self.fallSpeed = 0; } } }; return self; }); var ToolbarButton = Container.expand(function (blockType) { var self = Container.call(this); self.blockType = blockType; self.isSelected = false; var bgGraphics = self.attachAsset('toolbarBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); var blockGraphics = self.attachAsset(blockType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); var selectedBorder = self.attachAsset('selectedBorder', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.setSelected = function (selected) { self.isSelected = selected; selectedBorder.alpha = selected ? 0.8 : 0; bgGraphics.alpha = selected ? 1.0 : 0.8; }; self.down = function (x, y, obj) { selectedBlockType = self.blockType; updateToolbarSelection(); }; return self; }); var Water = Container.expand(function () { var self = Container.call(this); self.blockType = 'water'; self.gridX = 0; self.gridY = 0; self.isFlowing = false; self.flowCooldown = 0; var waterGraphics = self.attachAsset('water', { anchorX: 0.5, anchorY: 0.5 }); // Add water effects waterGraphics.alpha = 0.7; // Semi-transparent water self.setGridPosition = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X; self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y; }; self.update = function () { // Gentle wave effect waterGraphics.alpha = 0.6 + Math.sin(LK.ticks * 0.08) * 0.1; waterGraphics.tint = 0x4169e1 + Math.floor(Math.sin(LK.ticks * 0.05) * 10) * 0x000100; // Water flowing mechanics disabled - no spreading // if (self.flowCooldown > 0) { // self.flowCooldown--; // } else { // self.tryToFlow(); // self.flowCooldown = 90; // Flow every 1.5 seconds // } }; self.tryToFlow = function () { // Water flows down first, then horizontally var flowDirections = [{ x: 0, y: 1 }, // Down { x: -1, y: 0 }, // Left { x: 1, y: 0 }, // Right { x: -1, y: 1 }, // Down-left { x: 1, y: 1 } // Down-right ]; for (var i = 0; i < flowDirections.length; i++) { var dir = flowDirections[i]; var targetX = self.gridX + dir.x; var targetY = self.gridY + dir.y; if (isValidGridPosition(targetX, targetY) && grid[targetX][targetY] === null) { // Only flow if there are less than 4 water blocks nearby var nearbyWaterCount = 0; for (var checkX = targetX - 1; checkX <= targetX + 1; checkX++) { for (var checkY = targetY - 1; checkY <= targetY + 1; checkY++) { if (isValidGridPosition(checkX, checkY) && grid[checkX][checkY] !== null && grid[checkX][checkY].blockType === 'water') { nearbyWaterCount++; } } } if (nearbyWaterCount < 4) { // Create new water block var newWater = new Water(); newWater.setGridPosition(targetX, targetY); game.addChild(newWater); grid[targetX][targetY] = newWater; placedBlocks.push(newWater); LK.effects.flashObject(newWater, 0x87ceeb, 300); break; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ var GRID_SIZE = 80; var GRID_WIDTH = 20; var GRID_HEIGHT = 20; var GRID_OFFSET_X = (2048 - GRID_WIDTH * GRID_SIZE) / 2; var GRID_OFFSET_Y = 200; var selectedBlockType = 'dirt'; var blockTypes = ['dirt', 'stone', 'wood', 'grass', 'grassDark', 'grassLight', 'sand', 'diamond', 'gold', 'portal', 'lava', 'water']; var inventory = { dirt: 50, stone: 30, wood: 25, grass: 20, grassDark: 15, grassLight: 15, sand: 25, diamond: 5, gold: 8, portal: 3, lava: 10, water: 15 }; // Achievement system var achievements = storage.achievements || {}; var currentAchievementPage = 0; var achievementsPerPage = 8; var achievementDefinitions = { firstBlock: { name: "First Builder", description: "Place your first block", unlocked: false }, destroyer: { name: "Destroyer", description: "Break 10 blocks", unlocked: false }, architect: { name: "Architect", description: "Place 50 blocks", unlocked: false }, miner: { name: "Miner", description: "Break 50 blocks", unlocked: false }, collector: { name: "Collector", description: "Collect all block types", unlocked: false }, explorer: { name: "Explorer", description: "Move 100 times", unlocked: false }, masterBuilder: { name: "Master Builder", description: "Place 200 blocks", unlocked: false }, demolitionExpert: { name: "Demolition Expert", description: "Break 200 blocks", unlocked: false }, goldRush: { name: "Gold Rush", description: "Collect 20 gold blocks", unlocked: false }, diamondCollector: { name: "Diamond Collector", description: "Collect 15 diamond blocks", unlocked: false }, woodsman: { name: "Woodsman", description: "Collect 100 wood blocks", unlocked: false }, stoneAge: { name: "Stone Age", description: "Collect 150 stone blocks", unlocked: false }, adventurer: { name: "Adventurer", description: "Move 500 times", unlocked: false }, nomad: { name: "Nomad", description: "Move 1000 times", unlocked: false }, flightTime: { name: "Taking Flight", description: "Use flying mode 10 times", unlocked: false }, skyExplorer: { name: "Sky Explorer", description: "Spend 60 seconds in flying mode", unlocked: false }, areCrazy: { name: "are you crazy?", description: "Break each block type 10 times", unlocked: false }, boooing: { name: "boooing", description: "Jump 100 times", unlocked: false }, lavaLord: { name: "Lava Lord", description: "Place 25 lava blocks", unlocked: false }, waterMaster: { name: "Water Master", description: "Place 30 water blocks", unlocked: false } }; // Achievement counters var blocksPlaced = storage.blocksPlaced || 0; var blocksBroken = storage.blocksBroken || 0; var moveCount = storage.moveCount || 0; var goldCollected = storage.goldCollected || 0; var diamondCollected = storage.diamondCollected || 0; var woodCollected = storage.woodCollected || 0; var stoneCollected = storage.stoneCollected || 0; var flyingActivations = storage.flyingActivations || 0; var flyingStartTime = 0; var totalFlyingTime = storage.totalFlyingTime || 0; var blockTypeBrokenCount = storage.blockTypeBrokenCount || {}; var jumpCount = storage.jumpCount || 0; var lavaPlaced = storage.lavaPlaced || 0; var waterPlaced = storage.waterPlaced || 0; var grid = []; var placedBlocks = []; var toolbarButtons = []; var controlButtons = []; var pressTimer = null; var isLongPress = false; var pressStartTime = 0; var character = null; var blockSelectionMenu = null; var menuButton = null; var achievementNotification = null; var achievementMenu = null; var characterGravity = 0; // Current falling speed var characterMaxFallSpeed = 8; // Maximum falling speed var characterGravityAcceleration = 0.5; // How fast gravity accelerates var isFlying = false; // Flying mode toggle var flyingSpeed = 4; // Speed when flying var flyingVerticalSpeed = 3; // Vertical movement speed when flying function checkAchievement(achievementId) { if (achievements[achievementId]) return; var achieved = false; switch (achievementId) { case 'firstBlock': achieved = blocksPlaced >= 1; break; case 'destroyer': achieved = blocksBroken >= 10; break; case 'architect': achieved = blocksPlaced >= 50; break; case 'miner': achieved = blocksBroken >= 50; break; case 'collector': // Check if player has collected all block types var hasAllTypes = true; for (var blockType in inventory) { if (inventory[blockType] <= 0) { hasAllTypes = false; break; } } achieved = hasAllTypes; break; case 'explorer': achieved = moveCount >= 100; break; case 'masterBuilder': achieved = blocksPlaced >= 200; break; case 'demolitionExpert': achieved = blocksBroken >= 200; break; case 'goldRush': achieved = goldCollected >= 20; break; case 'diamondCollector': achieved = diamondCollected >= 15; break; case 'woodsman': achieved = woodCollected >= 100; break; case 'stoneAge': achieved = stoneCollected >= 150; break; case 'adventurer': achieved = moveCount >= 500; break; case 'nomad': achieved = moveCount >= 1000; break; case 'flightTime': achieved = flyingActivations >= 10; break; case 'skyExplorer': achieved = totalFlyingTime >= 60000; // 60 seconds in milliseconds break; case 'areCrazy': // Check if each block type has been broken at least 10 times achieved = true; for (var i = 0; i < blockTypes.length; i++) { var blockType = blockTypes[i]; var brokenCount = blockTypeBrokenCount[blockType] || 0; if (brokenCount < 10) { achieved = false; break; } } break; case 'boooing': achieved = jumpCount >= 100; break; case 'lavaLord': achieved = lavaPlaced >= 25; break; case 'waterMaster': achieved = waterPlaced >= 30; break; } if (achieved) { unlockAchievement(achievementId); } } function unlockAchievement(achievementId) { if (achievements[achievementId]) return; achievements[achievementId] = true; storage.achievements = achievements; // Update the achievement menu to show the newly unlocked achievement if (achievementMenu) { achievementMenu.updateAchievements(); // Show the achievement menu temporarily if not already visible if (!achievementMenu.isVisible) { achievementMenu.show(); // Auto-hide after a few seconds LK.setTimeout(function () { if (achievementMenu && achievementMenu.isVisible) { achievementMenu.hide(); } }, 3000); } } } function checkAllAchievements() { for (var achievementId in achievementDefinitions) { checkAchievement(achievementId); } } // Initialize grid for (var x = 0; x < GRID_WIDTH; x++) { grid[x] = []; for (var y = 0; y < GRID_HEIGHT; y++) { grid[x][y] = null; var gridCell = new GridCell(); gridCell.setGridPosition(x, y); game.addChild(gridCell); } } // Initialize character at top of world character = new Character(); var randomX = Math.floor(Math.random() * GRID_WIDTH); var topY = 0; // Always start at the very top character.setGridPosition(randomX, topY); game.addChild(character); // Create block selection menu blockSelectionMenu = new BlockSelectionMenu(); blockSelectionMenu.x = 1024; blockSelectionMenu.y = 800; LK.gui.addChild(blockSelectionMenu); // Create achievement menu at top layer achievementMenu = new AchievementMenu(); achievementMenu.x = 1024; achievementMenu.y = 400; LK.gui.addChild(achievementMenu); // Create menu button menuButton = new Container(); var menuBg = menuButton.attachAsset('toolbarBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); var menuText = new Text2('MENU', { size: 36, fill: 0xFFFFFF }); menuText.anchor.set(0.5, 0.5); menuText.x = 0; menuText.y = 0; menuButton.addChild(menuText); menuButton.x = 2048 - 80; menuButton.y = 150; menuButton.down = function (x, y, obj) { menuBg.alpha = 1.0; if (!blockSelectionMenu.isVisible) { blockSelectionMenu.show(); } }; menuButton.up = function (x, y, obj) { menuBg.alpha = 0.8; }; game.addChild(menuButton); // Create achievements button at the top var achievementsButton = new Container(); var achievementsBg = achievementsButton.attachAsset('toolbarBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); var achievementsText = new Text2('ACHIEVEMENTS', { size: 28, fill: 0xFFFFFF }); achievementsText.anchor.set(0.5, 0.5); achievementsText.x = 0; achievementsText.y = 0; achievementsButton.addChild(achievementsText); achievementsButton.x = 1024; achievementsButton.y = 50; achievementsButton.down = function (x, y, obj) { achievementsBg.alpha = 1.0; if (!achievementMenu.isVisible) { achievementMenu.show(); } }; achievementsButton.up = function (x, y, obj) { achievementsBg.alpha = 0.8; }; game.addChild(achievementsButton); // Create flying toggle button var flyingButton = new Container(); var flyingBg = flyingButton.attachAsset('toolbarBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); var flyingText = new Text2('FLY', { size: 32, fill: 0xFFFFFF }); flyingText.anchor.set(0.5, 0.5); flyingText.x = 0; flyingText.y = 0; flyingButton.addChild(flyingText); flyingButton.x = 2048 - 80; flyingButton.y = 350; flyingButton.down = function (x, y, obj) { flyingBg.alpha = 1.0; // Toggle flying mode var wasFlying = isFlying; isFlying = !isFlying; // Track flying time and activations if (isFlying && !wasFlying) { // Started flying flyingActivations++; storage.flyingActivations = flyingActivations; flyingStartTime = LK.ticks; checkAllAchievements(); } else if (!isFlying && wasFlying) { // Stopped flying if (flyingStartTime > 0) { var flyingDuration = (LK.ticks - flyingStartTime) * (1000 / 60); // Convert ticks to milliseconds totalFlyingTime += flyingDuration; storage.totalFlyingTime = totalFlyingTime; flyingStartTime = 0; checkAllAchievements(); } } // Update button appearance - recreate the background with new color flyingButton.removeChild(flyingBg); flyingBg = flyingButton.attachAsset('toolbarBg', { anchorX: 0.5, anchorY: 0.5, alpha: 1.0 }); if (isFlying) { flyingBg.tint = 0x00FF00; } else { flyingBg.tint = 0xFFFFFF; } flyingText.setText(isFlying ? 'LAND' : 'FLY'); // Visual feedback if (isFlying) { LK.effects.flashObject(character, 0x00FFFF, 500); } }; flyingButton.up = function (x, y, obj) { flyingBg.alpha = 0.8; }; game.addChild(flyingButton); // Create flying status display var flyingStatusText = new Text2('', { size: 28, fill: 0x00FFFF }); flyingStatusText.anchor.set(0.5, 0); flyingStatusText.x = 1024; flyingStatusText.y = 100; LK.gui.addChild(flyingStatusText); // Create toolbar var toolbarY = 2732 - 120; var toolbarStartX = (2048 - blockTypes.length * 120) / 2; for (var i = 0; i < blockTypes.length; i++) { var button = new ToolbarButton(blockTypes[i]); button.x = toolbarStartX + i * 120 + 60; button.y = toolbarY; toolbarButtons.push(button); LK.gui.addChild(button); } // Create inventory display var inventoryTexts = {}; for (var i = 0; i < blockTypes.length; i++) { var blockType = blockTypes[i]; var inventoryText = new Text2(inventory[blockType].toString(), { size: 36, fill: 0xFFFFFF }); inventoryText.anchor.set(0.5, 0); inventoryText.x = toolbarStartX + i * 120 + 60; inventoryText.y = toolbarY + 60; inventoryTexts[blockType] = inventoryText; LK.gui.addChild(inventoryText); } // Create control buttons var controlButtonSize = 100; var controlPadding = 20; var controlCenterX = 150; var controlCenterY = GRID_OFFSET_Y + GRID_HEIGHT * GRID_SIZE + 150; var directions = [{ dir: 'up', x: 0, y: -1 }, { dir: 'down', x: 0, y: 1 }, { dir: 'left', x: -1, y: 0 }, { dir: 'right', x: 1, y: 0 }]; for (var i = 0; i < directions.length; i++) { var dirInfo = directions[i]; var button = new ControlButton(dirInfo.dir); button.x = controlCenterX + dirInfo.x * (controlButtonSize + controlPadding); button.y = controlCenterY + dirInfo.y * (controlButtonSize + controlPadding); controlButtons.push(button); game.addChild(button); } function updateToolbarSelection() { for (var i = 0; i < toolbarButtons.length; i++) { toolbarButtons[i].setSelected(toolbarButtons[i].blockType === selectedBlockType); } } function updateInventoryDisplay() { for (var blockType in inventoryTexts) { inventoryTexts[blockType].setText(inventory[blockType].toString()); } // Update block selection menu inventory counts if (blockSelectionMenu) { blockSelectionMenu.updateInventoryCounts(); } } function getGridPosition(worldX, worldY) { var gridX = Math.floor((worldX - GRID_OFFSET_X) / GRID_SIZE); var gridY = Math.floor((worldY - GRID_OFFSET_Y) / GRID_SIZE); return { x: gridX, y: gridY }; } function isValidGridPosition(gridX, gridY) { return gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT; } function placeBlock(gridX, gridY) { if (!isValidGridPosition(gridX, gridY)) return false; if (grid[gridX][gridY] !== null) return false; if (inventory[selectedBlockType] <= 0) return false; // Don't place block on character position if (character && gridX === character.gridX && gridY === character.gridY) return false; var block; if (selectedBlockType === 'sand') { block = new Sand(); } else if (selectedBlockType === 'portal') { block = new Portal(); } else if (selectedBlockType === 'lava') { block = new Lava(); } else if (selectedBlockType === 'water') { block = new Water(); } else { block = new Block(selectedBlockType); } block.setGridPosition(gridX, gridY); game.addChild(block); grid[gridX][gridY] = block; placedBlocks.push(block); inventory[selectedBlockType]--; updateInventoryDisplay(); LK.getSound('place').play(); // Track achievement progress blocksPlaced++; storage.blocksPlaced = blocksPlaced; // Track lava placement for achievement if (selectedBlockType === 'lava') { lavaPlaced++; storage.lavaPlaced = lavaPlaced; } // Track water placement for achievement if (selectedBlockType === 'water') { waterPlaced++; storage.waterPlaced = waterPlaced; } checkAllAchievements(); return true; } function removeBlock(gridX, gridY) { if (!isValidGridPosition(gridX, gridY)) return false; if (grid[gridX][gridY] === null) return false; var block = grid[gridX][gridY]; var blockType = block.blockType; // If breaking a portal, teleport character to heaven biome if (blockType === 'portal') { // Transition to heaven biome permanently game.setBackgroundColor(0xFFD700); // Golden heaven sky // Flash screen with heavenly light LK.effects.flashScreen(0xFFFFFF, 2000); // Teleport character to top of world center var heavenX = Math.floor(GRID_WIDTH / 2); var heavenY = 0; // Top of the world character.setGridPosition(heavenX, heavenY); // Add visual effects LK.effects.flashObject(block, 0xFFD700, 1000); LK.effects.flashObject(character, 0xFFFFFF, 1000); // Play portal sound LK.getSound('portal').play(); // Clear all existing blocks to create clean heaven environment for (var x = 0; x < GRID_WIDTH; x++) { for (var y = 0; y < GRID_HEIGHT; y++) { if (grid[x][y] !== null && grid[x][y] !== block) { var existingBlock = grid[x][y]; existingBlock.destroy(); grid[x][y] = null; // Remove from placedBlocks array for (var j = placedBlocks.length - 1; j >= 0; j--) { if (placedBlocks[j] === existingBlock) { placedBlocks.splice(j, 1); break; } } } } } // Generate heaven environment with golden blocks and clouds selectedBlockType = 'gold'; // Create golden platform in heaven var heavenPlatformY = Math.floor(GRID_HEIGHT * 0.8); for (var x = 0; x < GRID_WIDTH; x++) { placeBlock(x, heavenPlatformY); if (Math.random() < 0.3) { placeBlock(x, heavenPlatformY - 1); // Add some elevation } } // Add diamond clouds scattered around selectedBlockType = 'diamond'; for (var i = 0; i < 15; i++) { var cloudX = Math.floor(Math.random() * GRID_WIDTH); var cloudY = Math.floor(Math.random() * heavenPlatformY * 0.7); if (isValidGridPosition(cloudX, cloudY) && grid[cloudX][cloudY] === null) { placeBlock(cloudX, cloudY); } } // Reset selection selectedBlockType = 'dirt'; updateToolbarSelection(); } // Create particle effects - multiple small pieces flying out for (var p = 0; p < 6; p++) { var particle = new Container(); var particleGraphics = particle.attachAsset(blockType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); particle.x = block.x; particle.y = block.y; game.addChild(particle); // Random direction for each particle var angle = p / 6 * Math.PI * 2 + (Math.random() - 0.5) * 0.5; var speed = 100 + Math.random() * 80; var targetX = particle.x + Math.cos(angle) * speed; var targetY = particle.y + Math.sin(angle) * speed; // Animate particle flying out and fading tween(particle, { x: targetX, y: targetY, scaleX: 0.1, scaleY: 0.1, alpha: 0, rotation: Math.random() * Math.PI * 2 }, { duration: 300 + Math.random() * 200, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } // Add breaking animation with flash effect and rotation LK.effects.flashObject(block, 0xFFFFFF, 200); // Add screen shake effect for impact var originalX = game.x; var originalY = game.y; var shakeIntensity = 8; tween(game, { x: originalX + shakeIntensity }, { duration: 30, easing: tween.easeOut }); LK.setTimeout(function () { tween(game, { x: originalX - shakeIntensity }, { duration: 30, easing: tween.easeOut }); }, 30); LK.setTimeout(function () { tween(game, { x: originalX, y: originalY }, { duration: 40, easing: tween.easeOut }); }, 60); // Remove from placedBlocks array for (var i = placedBlocks.length - 1; i >= 0; i--) { if (placedBlocks[i] === block) { placedBlocks.splice(i, 1); break; } } // Animate block destruction with scaling and rotation tween(block, { scaleX: 0, scaleY: 0, alpha: 0, rotation: Math.PI * 0.5 }, { duration: 150, easing: tween.easeIn, onFinish: function onFinish() { block.destroy(); } }); grid[gridX][gridY] = null; inventory[blockType]++; updateInventoryDisplay(); LK.getSound('break').play(); // Track achievement progress blocksBroken++; storage.blocksBroken = blocksBroken; // Track specific resource collection if (blockType === 'gold') { goldCollected++; storage.goldCollected = goldCollected; } else if (blockType === 'diamond') { diamondCollected++; storage.diamondCollected = diamondCollected; } else if (blockType === 'wood') { woodCollected++; storage.woodCollected = woodCollected; } else if (blockType === 'stone') { stoneCollected++; storage.stoneCollected = stoneCollected; } // Track block type breaking for 'are you crazy?' achievement if (!blockTypeBrokenCount[blockType]) { blockTypeBrokenCount[blockType] = 0; } blockTypeBrokenCount[blockType]++; storage.blockTypeBrokenCount = blockTypeBrokenCount; checkAllAchievements(); return true; } // Initialize toolbar selection updateToolbarSelection(); updateInventoryDisplay(); game.down = function (x, y, obj) { var gridPos = getGridPosition(x, y); pressStartTime = LK.ticks; isLongPress = false; // Start long press timer for breaking blocks if (pressTimer) { LK.clearTimeout(pressTimer); } pressTimer = LK.setTimeout(function () { isLongPress = true; // Break only the specific block at clicked position if (isValidGridPosition(gridPos.x, gridPos.y) && grid[gridPos.x][gridPos.y] !== null) { removeBlock(gridPos.x, gridPos.y); } }, 500); // 500ms long press }; game.up = function (x, y, obj) { // Clear the long press timer if (pressTimer) { LK.clearTimeout(pressTimer); pressTimer = null; } // If it wasn't a long press, place a block if (!isLongPress) { var gridPos = getGridPosition(x, y); if (isValidGridPosition(gridPos.x, gridPos.y)) { placeBlock(gridPos.x, gridPos.y); } } }; // Generate procedural world with biomes function generateWorld() { // Reset selected block type to dirt for generation selectedBlockType = 'dirt'; updateToolbarSelection(); // Create height map for smooth terrain var heightMap = []; var baseHeightLevel = Math.floor(GRID_HEIGHT * 0.7); // Base terrain level for (var x = 0; x < GRID_WIDTH; x++) { // Generate smooth terrain using sine waves var primary = Math.sin(x / GRID_WIDTH * Math.PI * 2) * 3; var secondary = Math.sin(x / GRID_WIDTH * Math.PI * 6) * 1.5; var noise = (Math.random() - 0.5) * 2; var height = baseHeightLevel + primary + secondary + noise; heightMap[x] = Math.floor(Math.max(5, Math.min(height, GRID_HEIGHT - 3))); } // Smooth the height map to reduce abrupt changes for (var pass = 0; pass < 2; pass++) { for (var x = 1; x < GRID_WIDTH - 1; x++) { var avg = (heightMap[x - 1] + heightMap[x] + heightMap[x + 1]) / 3; heightMap[x] = Math.floor(avg); } } // Define biome regions var biome1 = Math.floor(GRID_WIDTH * 0.33); var biome2 = Math.floor(GRID_WIDTH * 0.66); var biomes = ['plains', 'forest', 'mountains']; var currentBiomes = [biomes[Math.floor(Math.random() * biomes.length)], biomes[Math.floor(Math.random() * biomes.length)], biomes[Math.floor(Math.random() * biomes.length)]]; // Generate terrain layers for (var x = 0; x < GRID_WIDTH; x++) { var currentBiome; if (x < biome1) { currentBiome = currentBiomes[0]; } else if (x < biome2) { currentBiome = currentBiomes[1]; } else { currentBiome = currentBiomes[2]; } var baseHeight = heightMap[x]; var surfaceBlock = 'grass'; var treeChance = 0.15; var stoneChance = 0.1; // Biome-specific properties if (currentBiome === 'plains') { surfaceBlock = 'grass'; treeChance = 0.08; stoneChance = 0.05; } else if (currentBiome === 'forest') { surfaceBlock = 'grass'; treeChance = 0.25; // More trees stoneChance = 0.08; } else if (currentBiome === 'mountains') { surfaceBlock = 'stone'; // Rocky surface treeChance = 0.05; // Few trees at high altitude stoneChance = 0.25; // Very rocky // Adjust height for mountains baseHeight = Math.max(baseHeight - 3, Math.floor(GRID_HEIGHT * 0.5)); } // Place surface layer selectedBlockType = surfaceBlock; placeBlock(x, baseHeight); // Place sub-surface layers consistently selectedBlockType = 'dirt'; var dirtLayers = currentBiome === 'mountains' ? 2 : 3; for (var y = baseHeight + 1; y < Math.min(baseHeight + dirtLayers + 1, GRID_HEIGHT); y++) { placeBlock(x, y); } // Place stone in deeper layers consistently selectedBlockType = 'stone'; for (var y = baseHeight + dirtLayers + 1; y < GRID_HEIGHT; y++) { var stoneProb = currentBiome === 'mountains' ? 0.9 : 0.8; if (Math.random() < stoneProb) { placeBlock(x, y); } } // Add trees in groups for natural look if (Math.random() < treeChance && baseHeight > 2) { selectedBlockType = 'wood'; var treeHeight; if (currentBiome === 'forest') { treeHeight = Math.floor(Math.random() * 3) + 3; // Tall trees } else { treeHeight = Math.floor(Math.random() * 2) + 2; // Normal trees } for (var i = 0; i < treeHeight; i++) { var treeY = baseHeight - i - 1; if (treeY >= 0) { placeBlock(x, treeY); } } // Add tree leaves/branches if (currentBiome === 'forest' && treeHeight >= 3) { selectedBlockType = 'grassLight'; if (x > 0 && baseHeight - treeHeight >= 0) { placeBlock(x - 1, baseHeight - treeHeight + 1); } if (x < GRID_WIDTH - 1 && baseHeight - treeHeight >= 0) { placeBlock(x + 1, baseHeight - treeHeight + 1); } } } // Add surface features sparingly if (Math.random() < stoneChance && baseHeight > 0) { selectedBlockType = 'stone'; placeBlock(x, baseHeight - 1); } // Add biome-specific features occasionally if (currentBiome === 'mountains' && Math.random() < 0.1) { selectedBlockType = 'stone'; if (baseHeight > 1) { placeBlock(x, baseHeight - 1); } } } // Add minimal scattered resources in logical places for (var i = 0; i < 15; i++) { var randX = Math.floor(Math.random() * GRID_WIDTH); var surfaceY = heightMap[randX]; // Place resources near surface or in underground areas var randY = Math.random() < 0.6 ? surfaceY + Math.floor(Math.random() * 3) + 1 : Math.floor(Math.random() * (GRID_HEIGHT - surfaceY - 5)) + surfaceY + 3; if (isValidGridPosition(randX, randY) && grid[randX][randY] === null) { var resourceType; if (randY > surfaceY + 2) { // Underground resources resourceType = Math.random() < 0.6 ? 'stone' : 'dirt'; } else { // Surface resources resourceType = blockTypes[Math.floor(Math.random() * blockTypes.length)]; } selectedBlockType = resourceType; placeBlock(randX, randY); } } // Add rare precious blocks deep underground for (var i = 0; i < 3; i++) { var randX = Math.floor(Math.random() * GRID_WIDTH); var surfaceY = heightMap[randX]; // Place deep underground only var randY = Math.floor(Math.random() * 3) + GRID_HEIGHT - 5; if (isValidGridPosition(randX, randY) && grid[randX][randY] === null) { var preciousType = Math.random() < 0.4 ? 'diamond' : 'gold'; selectedBlockType = preciousType; placeBlock(randX, randY); } } // Add portals with 10% chance - place 1-2 portals randomly if (Math.random() < 0.1) { var numPortals = Math.floor(Math.random() * 2) + 1; // 1 or 2 portals for (var p = 0; p < numPortals; p++) { var attempts = 0; var portalPlaced = false; while (attempts < 20 && !portalPlaced) { var randX = Math.floor(Math.random() * GRID_WIDTH); var surfaceY = heightMap[randX]; var randY = surfaceY - Math.floor(Math.random() * 3) - 1; // Place above surface if (isValidGridPosition(randX, randY) && grid[randX][randY] === null) { selectedBlockType = 'portal'; if (placeBlock(randX, randY)) { portalPlaced = true; } } attempts++; } } } // Create a few small, deliberate caves for (var caveCount = 0; caveCount < 2; caveCount++) { var caveX = Math.floor(Math.random() * (GRID_WIDTH - 4)) + 2; var surfaceHeight = heightMap[caveX]; var caveY = surfaceHeight + Math.floor(Math.random() * 4) + 3; var caveSize = 1; // Create small hollow areas for (var cx = caveX - caveSize; cx <= caveX + caveSize; cx++) { for (var cy = caveY - caveSize; cy <= caveY + caveSize; cy++) { if (isValidGridPosition(cx, cy) && grid[cx][cy] !== null) { var block = grid[cx][cy]; if (block) { block.destroy(); grid[cx][cy] = null; // Remove from placedBlocks array for (var j = placedBlocks.length - 1; j >= 0; j--) { if (placedBlocks[j] === block) { placedBlocks.splice(j, 1); break; } } } } } } } // Reset to dirt selection selectedBlockType = 'dirt'; updateToolbarSelection(); } // Generate the world after a short delay LK.setTimeout(function () { generateWorld(); }, 500); function applyGravity() { var blocksToMove = []; // Check all blocks from bottom to top for (var y = GRID_HEIGHT - 2; y >= 0; y--) { for (var x = 0; x < GRID_WIDTH; x++) { var block = grid[x][y]; if (block !== null) { // Check if block has support below var hasSupport = false; for (var checkY = y + 1; checkY < GRID_HEIGHT; checkY++) { if (grid[x][checkY] !== null) { hasSupport = true; break; } } // If no support, mark for falling if (!hasSupport) { var fallY = y; // Find where it should fall to for (var targetY = y + 1; targetY < GRID_HEIGHT; targetY++) { if (grid[x][targetY] !== null) { break; } fallY = targetY; } if (fallY !== y) { blocksToMove.push({ block: block, fromX: x, fromY: y, toX: x, toY: fallY }); } } } } } // Move blocks that need to fall for (var i = 0; i < blocksToMove.length; i++) { var moveData = blocksToMove[i]; var block = moveData.block; // Clear old position grid[moveData.fromX][moveData.fromY] = null; // Set new position grid[moveData.toX][moveData.toY] = block; block.setGridPosition(moveData.toX, moveData.toY); } } game.update = function () { // Apply gravity every few ticks to make blocks fall if (LK.ticks % 10 === 0) { applyGravity(); } // Update flying status display if (isFlying) { flyingStatusText.setText('FLYING MODE'); flyingStatusText.visible = true; // Track flying time for achievements if (flyingStartTime > 0) { var currentFlyingTime = (LK.ticks - flyingStartTime) * (1000 / 60); if (currentFlyingTime >= 1000) { // Check every second totalFlyingTime += 1000; storage.totalFlyingTime = totalFlyingTime; flyingStartTime = LK.ticks; checkAllAchievements(); } } } else { flyingStatusText.visible = false; } // Replenish inventory slowly over time if (LK.ticks % 600 === 0) { // Every 10 seconds for (var blockType in inventory) { if (inventory[blockType] < 50) { inventory[blockType]++; } } updateInventoryDisplay(); checkAllAchievements(); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Achievement = Container.expand(function (achievementData) {
var self = Container.call(this);
// Background
var bgGraphics = self.attachAsset('menuBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.3,
alpha: 0.9
});
// Title text
var titleText = new Text2('Achievement Unlocked!', {
size: 36,
fill: 0xFFD700
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -30;
self.addChild(titleText);
// Achievement name
var nameText = new Text2(achievementData.name, {
size: 28,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = 0;
self.addChild(nameText);
// Achievement description
var descText = new Text2(achievementData.description, {
size: 22,
fill: 0xCCCCCC
});
descText.anchor.set(0.5, 0.5);
descText.x = 0;
descText.y = 25;
self.addChild(descText);
self.show = function () {
self.alpha = 0;
self.scaleX = 0.5;
self.scaleY = 0.5;
self.visible = true;
tween(self, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
// Auto hide after 3 seconds
LK.setTimeout(function () {
tween(self, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 200,
onFinish: function onFinish() {
self.visible = false;
}
});
}, 3000);
};
// Initially hidden
self.visible = false;
return self;
});
var AchievementMenu = Container.expand(function () {
var self = Container.call(this);
self.isVisible = false;
// Background
var bgGraphics = self.attachAsset('menuBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9,
scaleX: 1.2,
scaleY: 3.0
});
// Title text
var titleText = new Text2('Achievements', {
size: 48,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -300;
self.addChild(titleText);
// Close button
var closeButton = self.attachAsset('closeButton', {
anchorX: 0.5,
anchorY: 0.5
});
closeButton.x = 350;
closeButton.y = -300;
// Achievement list container
var achievementList = new Container();
self.addChild(achievementList);
// Page navigation buttons
var prevPageButton = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
scaleX: 0.8,
scaleY: 0.5
});
prevPageButton.x = -200;
prevPageButton.y = 300;
var prevPageText = new Text2('PREV', {
size: 28,
fill: 0xFFFFFF
});
prevPageText.anchor.set(0.5, 0.5);
prevPageText.x = -200;
prevPageText.y = 300;
self.addChild(prevPageText);
var nextPageButton = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
scaleX: 0.8,
scaleY: 0.5
});
nextPageButton.x = 200;
nextPageButton.y = 300;
var nextPageText = new Text2('NEXT', {
size: 28,
fill: 0xFFFFFF
});
nextPageText.anchor.set(0.5, 0.5);
nextPageText.x = 200;
nextPageText.y = 300;
self.addChild(nextPageText);
// Page indicator text
var pageIndicatorText = new Text2('', {
size: 24,
fill: 0xCCCCCC
});
pageIndicatorText.anchor.set(0.5, 0.5);
pageIndicatorText.x = 0;
pageIndicatorText.y = 300;
self.addChild(pageIndicatorText);
self.updateAchievements = function () {
// Clear existing achievement displays
while (achievementList.children.length > 0) {
achievementList.removeChild(achievementList.children[0]);
}
// Get all achievement IDs as array for pagination
var allAchievementIds = [];
for (var achievementId in achievementDefinitions) {
allAchievementIds.push(achievementId);
}
// Calculate pagination
var totalPages = Math.ceil(allAchievementIds.length / achievementsPerPage);
var startIndex = currentAchievementPage * achievementsPerPage;
var endIndex = Math.min(startIndex + achievementsPerPage, allAchievementIds.length);
// Update page indicator
pageIndicatorText.setText('Page ' + (currentAchievementPage + 1) + ' of ' + totalPages);
// Update button visibility
prevPageButton.alpha = currentAchievementPage > 0 ? 0.8 : 0.3;
prevPageText.fill = currentAchievementPage > 0 ? 0xFFFFFF : 0x666666;
nextPageButton.alpha = currentAchievementPage < totalPages - 1 ? 0.8 : 0.3;
nextPageText.fill = currentAchievementPage < totalPages - 1 ? 0xFFFFFF : 0x666666;
var yOffset = -200;
var achievementIndex = 0;
// Display achievements for current page
for (var i = startIndex; i < endIndex; i++) {
var achievementId = allAchievementIds[i];
var achievement = achievementDefinitions[achievementId];
var isUnlocked = achievements[achievementId] || false;
// Achievement container
var achievementContainer = new Container();
achievementContainer.y = yOffset + achievementIndex * 80;
// Achievement background
var achievementBg = achievementContainer.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: isUnlocked ? 0.8 : 0.3,
scaleX: 2.8,
scaleY: 0.6
});
// Achievement name
var nameText = new Text2(achievement.name, {
size: 32,
fill: isUnlocked ? 0xFFD700 : 0x888888
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 0;
nameText.y = -15;
achievementContainer.addChild(nameText);
// Achievement description
var descText = new Text2(achievement.description, {
size: 24,
fill: isUnlocked ? 0xFFFFFF : 0x666666
});
descText.anchor.set(0.5, 0.5);
descText.x = 0;
descText.y = 10;
achievementContainer.addChild(descText);
// Status text
var statusText = new Text2(isUnlocked ? 'UNLOCKED' : 'LOCKED', {
size: 20,
fill: isUnlocked ? 0x00FF00 : 0xFF4444
});
statusText.anchor.set(1, 0.5);
statusText.x = 150;
statusText.y = 0;
achievementContainer.addChild(statusText);
achievementList.addChild(achievementContainer);
achievementIndex++;
}
};
// Page navigation interactions
prevPageButton.down = function (x, y, obj) {
if (currentAchievementPage > 0) {
prevPageButton.alpha = 1.0;
currentAchievementPage--;
self.updateAchievements();
}
};
prevPageButton.up = function (x, y, obj) {
prevPageButton.alpha = currentAchievementPage > 0 ? 0.8 : 0.3;
};
nextPageButton.down = function (x, y, obj) {
var totalPages = Math.ceil(Object.keys(achievementDefinitions).length / achievementsPerPage);
if (currentAchievementPage < totalPages - 1) {
nextPageButton.alpha = 1.0;
currentAchievementPage++;
self.updateAchievements();
}
};
nextPageButton.up = function (x, y, obj) {
var totalPages = Math.ceil(Object.keys(achievementDefinitions).length / achievementsPerPage);
nextPageButton.alpha = currentAchievementPage < totalPages - 1 ? 0.8 : 0.3;
};
// Close button interaction
closeButton.down = function (x, y, obj) {
closeButton.alpha = 0.7;
self.hide();
};
closeButton.up = function (x, y, obj) {
closeButton.alpha = 1.0;
};
self.show = function () {
self.isVisible = true;
currentAchievementPage = 0; // Reset to first page when opening
self.updateAchievements();
self.alpha = 0;
self.visible = true;
tween(self, {
alpha: 1
}, {
duration: 200
});
};
self.hide = function () {
self.isVisible = false;
tween(self, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.visible = false;
}
});
};
// Initially hidden
self.visible = false;
return self;
});
var Block = Container.expand(function (blockType) {
var self = Container.call(this);
self.blockType = blockType;
self.gridX = 0;
self.gridY = 0;
var blockGraphics = self.attachAsset(blockType, {
anchorX: 0.5,
anchorY: 0.5
});
// Add a subtle border effect
blockGraphics.alpha = 0.9;
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
};
return self;
});
var BlockSelectionMenu = Container.expand(function () {
var self = Container.call(this);
self.isVisible = false;
// Background
var bgGraphics = self.attachAsset('menuBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9,
scaleY: 4.0
});
// Title text
var titleText = new Text2('Select Block Type', {
size: 48,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -400;
self.addChild(titleText);
// Close button
var closeButton = self.attachAsset('closeButton', {
anchorX: 0.5,
anchorY: 0.5
});
closeButton.x = 280;
closeButton.y = -400;
// Block selection buttons
self.menuButtons = [];
var allBlockTypes = Object.keys(inventory);
var buttonsPerRow = 3;
var buttonSpacing = 160;
for (var i = 0; i < allBlockTypes.length; i++) {
var button = new Container();
// Button background
var buttonBg = button.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
// Block preview
var blockPreview = button.attachAsset(allBlockTypes[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
// Block name text
var nameText = new Text2(allBlockTypes[i], {
size: 26,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.x = 0;
nameText.y = 45;
button.addChild(nameText);
// Inventory count text
var countText = new Text2(inventory[allBlockTypes[i]].toString(), {
size: 24,
fill: 0xFFDD44
});
countText.anchor.set(0.5, 0);
countText.x = 0;
countText.y = 65;
button.addChild(countText);
// Position button
var col = i % buttonsPerRow;
var row = Math.floor(i / buttonsPerRow);
button.x = (col - 1) * buttonSpacing;
button.y = row * buttonSpacing - 30;
// Store button data
button.blockType = allBlockTypes[i];
button.bgGraphics = buttonBg;
button.countText = countText;
// Button interaction
button.down = function (x, y, obj) {
this.bgGraphics.alpha = 1.0;
selectedBlockType = this.blockType;
updateToolbarSelection();
self.hide();
};
button.up = function (x, y, obj) {
this.bgGraphics.alpha = 0.8;
};
self.menuButtons.push(button);
self.addChild(button);
}
// Close button interaction
closeButton.down = function (x, y, obj) {
closeButton.alpha = 0.7;
self.hide();
};
closeButton.up = function (x, y, obj) {
closeButton.alpha = 1.0;
};
self.show = function () {
self.isVisible = true;
self.alpha = 0;
self.visible = true;
tween(self, {
alpha: 1
}, {
duration: 200
});
};
self.hide = function () {
self.isVisible = false;
tween(self, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.visible = false;
}
});
};
self.updateInventoryCounts = function () {
for (var i = 0; i < self.menuButtons.length; i++) {
var button = self.menuButtons[i];
if (button.countText && button.blockType) {
button.countText.setText(inventory[button.blockType].toString());
}
}
};
// Initially hidden
self.visible = false;
return self;
});
var Character = Container.expand(function () {
var self = Container.call(this);
var characterGraphics = self.attachAsset('character', {
anchorX: 0.5,
anchorY: 0.5
});
self.gridX = 0;
self.gridY = 0;
self.targetX = 0;
self.targetY = 0;
self.isMoving = false;
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.targetX = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.targetY = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
if (!self.isMoving) {
self.x = self.targetX;
self.y = self.targetY;
}
};
self.moveToGrid = function (gridX, gridY) {
if (self.isMoving) return false;
if (!isValidGridPosition(gridX, gridY)) return false;
// Check if there's a block at the target position (only block movement if not flying)
if (!isFlying && grid[gridX] && grid[gridX][gridY] !== null) return false;
self.isMoving = true;
self.gridX = gridX;
self.gridY = gridY;
self.targetX = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.targetY = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
// Reset gravity when manually moving (especially important in flying mode)
if (isFlying) {
characterGravity = 0;
}
// Adjust movement speed based on flying state
var moveDuration = isFlying ? 100 : 200;
tween(self, {
x: self.targetX,
y: self.targetY
}, {
duration: moveDuration,
easing: isFlying ? tween.easeOut : tween.easeInOut,
onFinish: function onFinish() {
self.isMoving = false;
// Track movement for achievements
moveCount++;
storage.moveCount = moveCount;
// Track jumping (moving up) for boooing achievement
if (gridY < self.gridY) {
jumpCount++;
storage.jumpCount = jumpCount;
}
checkAllAchievements();
}
});
return true;
};
self.update = function () {
// Reset gravity when flying - do this first
if (isFlying) {
characterGravity = 0;
} else {
// Apply gravity if character is not moving, not supported, and not flying
if (!self.isMoving) {
var hasSupport = false;
// Check if character has support below (block or ground)
var belowY = self.gridY + 1;
if (belowY >= GRID_HEIGHT) {
// At bottom of world, has support
hasSupport = true;
} else if (grid[self.gridX] && grid[self.gridX][belowY] !== null) {
// Block below, has support
hasSupport = true;
}
if (!hasSupport) {
// Character should fall
characterGravity += characterGravityAcceleration;
if (characterGravity > characterMaxFallSpeed) {
characterGravity = characterMaxFallSpeed;
}
// Find where character should fall to
var fallToY = self.gridY;
for (var checkY = self.gridY + 1; checkY < GRID_HEIGHT; checkY++) {
if (grid[self.gridX][checkY] !== null) {
break;
}
fallToY = checkY;
}
// Move character down if there's a place to fall
if (fallToY > self.gridY) {
var targetY = Math.min(fallToY, self.gridY + Math.floor(characterGravity));
if (targetY !== self.gridY) {
self.moveToGrid(self.gridX, targetY);
}
}
} else {
// Character has support, reset gravity
characterGravity = 0;
}
}
}
// Check if character is touching a portal
if (!self.isMoving) {
for (var i = 0; i < placedBlocks.length; i++) {
var block = placedBlocks[i];
if (block.blockType === 'portal' && block.gridX === self.gridX && block.gridY === self.gridY) {
block.teleportCharacter();
break;
}
}
}
// Character is always on top
if (self.parent) {
var parent = self.parent;
parent.removeChild(self);
parent.addChild(self);
}
};
return self;
});
var ControlButton = Container.expand(function (direction) {
var self = Container.call(this);
self.direction = direction;
var bgGraphics = self.attachAsset('controlBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
var arrowGraphics = self.attachAsset('arrow' + direction.charAt(0).toUpperCase() + direction.slice(1), {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
self.down = function (x, y, obj) {
bgGraphics.alpha = 1.0;
var newX = character.gridX;
var newY = character.gridY;
if (direction === 'up') newY--;else if (direction === 'down') newY++;else if (direction === 'left') newX--;else if (direction === 'right') newX++;
character.moveToGrid(newX, newY);
};
self.up = function (x, y, obj) {
bgGraphics.alpha = 0.7;
};
return self;
});
var GridCell = Container.expand(function () {
var self = Container.call(this);
var cellGraphics = self.attachAsset('gridCell', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1
});
self.gridX = 0;
self.gridY = 0;
self.isEmpty = true;
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
};
self.highlight = function () {
cellGraphics.alpha = 0.3;
};
self.unhighlight = function () {
cellGraphics.alpha = 0.1;
};
return self;
});
var Lava = Container.expand(function () {
var self = Container.call(this);
self.blockType = 'lava';
self.gridX = 0;
self.gridY = 0;
self.isFlowing = false;
self.flowCooldown = 0;
self.damageTimer = 0;
var lavaGraphics = self.attachAsset('lava', {
anchorX: 0.5,
anchorY: 0.5
});
// Add glowing effect
lavaGraphics.alpha = 0.9;
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
};
self.update = function () {
// Pulsing glow effect
lavaGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.15) * 0.15;
lavaGraphics.tint = 0xff4500 + Math.floor(Math.sin(LK.ticks * 0.1) * 20) * 0x010000;
// Handle damage to character
if (character && character.gridX === self.gridX && character.gridY === self.gridY && !character.isMoving) {
self.damageTimer++;
if (self.damageTimer >= 60) {
// 1 second of contact
// Flash character red and teleport away from lava
LK.effects.flashObject(character, 0xff0000, 500);
LK.effects.flashScreen(0xff0000, 300);
// Move character to nearest safe position
self.teleportCharacterToSafety();
self.damageTimer = 0;
}
} else {
self.damageTimer = 0;
}
// Lava flowing mechanics disabled - no spreading
// if (self.flowCooldown > 0) {
// self.flowCooldown--;
// } else {
// self.tryToFlow();
// self.flowCooldown = 120; // Flow every 2 seconds
// }
};
self.teleportCharacterToSafety = function () {
// Find nearest safe position
var safePositions = [];
for (var radius = 1; radius <= 5; radius++) {
for (var dx = -radius; dx <= radius; dx++) {
for (var dy = -radius; dy <= radius; dy++) {
var checkX = self.gridX + dx;
var checkY = self.gridY + dy;
if (isValidGridPosition(checkX, checkY) && grid[checkX][checkY] === null) {
// Check if position has ground support below
var hasSupport = false;
if (checkY + 1 >= GRID_HEIGHT) {
hasSupport = true;
} else if (grid[checkX][checkY + 1] !== null && grid[checkX][checkY + 1].blockType !== 'lava') {
hasSupport = true;
}
if (hasSupport) {
safePositions.push({
x: checkX,
y: checkY
});
}
}
}
}
if (safePositions.length > 0) break;
}
if (safePositions.length > 0) {
var safePos = safePositions[0]; // Take closest safe position
character.setGridPosition(safePos.x, safePos.y);
}
};
self.tryToFlow = function () {
// Lava flows down first, then horizontally
var flowDirections = [{
x: 0,
y: 1
},
// Down
{
x: -1,
y: 0
},
// Left
{
x: 1,
y: 0
},
// Right
{
x: -1,
y: 1
},
// Down-left
{
x: 1,
y: 1
} // Down-right
];
for (var i = 0; i < flowDirections.length; i++) {
var dir = flowDirections[i];
var targetX = self.gridX + dir.x;
var targetY = self.gridY + dir.y;
if (isValidGridPosition(targetX, targetY) && grid[targetX][targetY] === null) {
// Only flow if there are less than 3 lava blocks already
var nearbyLavaCount = 0;
for (var checkX = targetX - 1; checkX <= targetX + 1; checkX++) {
for (var checkY = targetY - 1; checkY <= targetY + 1; checkY++) {
if (isValidGridPosition(checkX, checkY) && grid[checkX][checkY] !== null && grid[checkX][checkY].blockType === 'lava') {
nearbyLavaCount++;
}
}
}
if (nearbyLavaCount < 3) {
// Create new lava block
var newLava = new Lava();
newLava.setGridPosition(targetX, targetY);
game.addChild(newLava);
grid[targetX][targetY] = newLava;
placedBlocks.push(newLava);
LK.effects.flashObject(newLava, 0xffff00, 300);
break;
}
}
}
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
self.blockType = 'portal';
self.gridX = 0;
self.gridY = 0;
self.isActive = true;
self.cooldownTime = 0;
var portalGraphics = self.attachAsset('portal', {
anchorX: 0.5,
anchorY: 0.5
});
// Add glowing effect
portalGraphics.alpha = 0.8;
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
};
self.teleportCharacter = function () {
if (!self.isActive || self.cooldownTime > 0) return false;
if (!character || character.isMoving) return false;
// Find all other active portals
var otherPortals = [];
for (var i = 0; i < placedBlocks.length; i++) {
var block = placedBlocks[i];
if (block !== self && block.blockType === 'portal' && block.isActive && block.cooldownTime <= 0) {
otherPortals.push(block);
}
}
if (otherPortals.length === 0) {
// No other portals available - teleport to heaven!
// Create heaven effect with golden background and clouds
game.setBackgroundColor(0xFFD700); // Golden sky
// Flash screen with heavenly light
LK.effects.flashScreen(0xFFFFFF, 2000);
// Teleport character to top of world center
var heavenX = Math.floor(GRID_WIDTH / 2);
var heavenY = 0; // Top of the world
character.setGridPosition(heavenX, heavenY);
// Add visual effects
LK.effects.flashObject(self, 0xFFD700, 1000);
LK.effects.flashObject(character, 0xFFFFFF, 1000);
// Play portal sound
LK.getSound('portal').play();
// Set cooldown
self.cooldownTime = 300; // 5 seconds cooldown
// Reset background after effect
LK.setTimeout(function () {
game.setBackgroundColor(0x87CEEB); // Back to sky blue
}, 3000);
return true;
}
// Choose random portal to teleport to
var targetPortal = otherPortals[Math.floor(Math.random() * otherPortals.length)];
// Teleport character
character.setGridPosition(targetPortal.gridX, targetPortal.gridY);
// Add visual effects
LK.effects.flashObject(self, 0x9400d3, 500);
LK.effects.flashObject(targetPortal, 0x9400d3, 500);
LK.effects.flashObject(character, 0x00ffff, 300);
// Play portal sound
LK.getSound('portal').play();
// Set cooldown for both portals
self.cooldownTime = 180; // 3 seconds at 60fps
targetPortal.cooldownTime = 180;
return true;
};
self.update = function () {
// Handle cooldown
if (self.cooldownTime > 0) {
self.cooldownTime--;
portalGraphics.alpha = 0.3 + self.cooldownTime / 180 * 0.5;
} else {
portalGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.1) * 0.2; // Pulsing effect
}
// Check if character is on portal
if (character && character.gridX === self.gridX && character.gridY === self.gridY && !character.isMoving) {
self.teleportCharacter();
}
};
return self;
});
var Sand = Container.expand(function () {
var self = Container.call(this);
self.blockType = 'sand';
self.gridX = 0;
self.gridY = 0;
self.isFalling = false;
self.fallSpeed = 0;
self.maxFallSpeed = 8;
self.fallAcceleration = 0.8;
var sandGraphics = self.attachAsset('sand', {
anchorX: 0.5,
anchorY: 0.5
});
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
};
self.update = function () {
// Check if sand should fall
if (!self.isFalling) {
var hasSupport = false;
var belowY = self.gridY + 1;
// Check if at bottom or has block below
if (belowY >= GRID_HEIGHT) {
hasSupport = true;
} else if (grid[self.gridX] && grid[self.gridX][belowY] !== null) {
hasSupport = true;
}
// Start falling if no support
if (!hasSupport) {
self.isFalling = true;
self.fallSpeed = 0;
}
}
// Handle falling physics
if (self.isFalling) {
self.fallSpeed += self.fallAcceleration;
if (self.fallSpeed > self.maxFallSpeed) {
self.fallSpeed = self.maxFallSpeed;
}
// Calculate new grid position
var newGridY = self.gridY + Math.floor(self.fallSpeed / 4);
// Check if we can fall to new position
if (newGridY < GRID_HEIGHT && (grid[self.gridX][newGridY] === null || grid[self.gridX][newGridY] === self)) {
// Update grid
if (grid[self.gridX][self.gridY] === self) {
grid[self.gridX][self.gridY] = null;
}
self.gridY = newGridY;
grid[self.gridX][self.gridY] = self;
self.setGridPosition(self.gridX, self.gridY);
} else {
// Stop falling - hit something or reached bottom
self.isFalling = false;
self.fallSpeed = 0;
}
}
};
return self;
});
var ToolbarButton = Container.expand(function (blockType) {
var self = Container.call(this);
self.blockType = blockType;
self.isSelected = false;
var bgGraphics = self.attachAsset('toolbarBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
var blockGraphics = self.attachAsset(blockType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
var selectedBorder = self.attachAsset('selectedBorder', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.setSelected = function (selected) {
self.isSelected = selected;
selectedBorder.alpha = selected ? 0.8 : 0;
bgGraphics.alpha = selected ? 1.0 : 0.8;
};
self.down = function (x, y, obj) {
selectedBlockType = self.blockType;
updateToolbarSelection();
};
return self;
});
var Water = Container.expand(function () {
var self = Container.call(this);
self.blockType = 'water';
self.gridX = 0;
self.gridY = 0;
self.isFlowing = false;
self.flowCooldown = 0;
var waterGraphics = self.attachAsset('water', {
anchorX: 0.5,
anchorY: 0.5
});
// Add water effects
waterGraphics.alpha = 0.7; // Semi-transparent water
self.setGridPosition = function (gridX, gridY) {
self.gridX = gridX;
self.gridY = gridY;
self.x = gridX * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_X;
self.y = gridY * GRID_SIZE + GRID_SIZE / 2 + GRID_OFFSET_Y;
};
self.update = function () {
// Gentle wave effect
waterGraphics.alpha = 0.6 + Math.sin(LK.ticks * 0.08) * 0.1;
waterGraphics.tint = 0x4169e1 + Math.floor(Math.sin(LK.ticks * 0.05) * 10) * 0x000100;
// Water flowing mechanics disabled - no spreading
// if (self.flowCooldown > 0) {
// self.flowCooldown--;
// } else {
// self.tryToFlow();
// self.flowCooldown = 90; // Flow every 1.5 seconds
// }
};
self.tryToFlow = function () {
// Water flows down first, then horizontally
var flowDirections = [{
x: 0,
y: 1
},
// Down
{
x: -1,
y: 0
},
// Left
{
x: 1,
y: 0
},
// Right
{
x: -1,
y: 1
},
// Down-left
{
x: 1,
y: 1
} // Down-right
];
for (var i = 0; i < flowDirections.length; i++) {
var dir = flowDirections[i];
var targetX = self.gridX + dir.x;
var targetY = self.gridY + dir.y;
if (isValidGridPosition(targetX, targetY) && grid[targetX][targetY] === null) {
// Only flow if there are less than 4 water blocks nearby
var nearbyWaterCount = 0;
for (var checkX = targetX - 1; checkX <= targetX + 1; checkX++) {
for (var checkY = targetY - 1; checkY <= targetY + 1; checkY++) {
if (isValidGridPosition(checkX, checkY) && grid[checkX][checkY] !== null && grid[checkX][checkY].blockType === 'water') {
nearbyWaterCount++;
}
}
}
if (nearbyWaterCount < 4) {
// Create new water block
var newWater = new Water();
newWater.setGridPosition(targetX, targetY);
game.addChild(newWater);
grid[targetX][targetY] = newWater;
placedBlocks.push(newWater);
LK.effects.flashObject(newWater, 0x87ceeb, 300);
break;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var GRID_SIZE = 80;
var GRID_WIDTH = 20;
var GRID_HEIGHT = 20;
var GRID_OFFSET_X = (2048 - GRID_WIDTH * GRID_SIZE) / 2;
var GRID_OFFSET_Y = 200;
var selectedBlockType = 'dirt';
var blockTypes = ['dirt', 'stone', 'wood', 'grass', 'grassDark', 'grassLight', 'sand', 'diamond', 'gold', 'portal', 'lava', 'water'];
var inventory = {
dirt: 50,
stone: 30,
wood: 25,
grass: 20,
grassDark: 15,
grassLight: 15,
sand: 25,
diamond: 5,
gold: 8,
portal: 3,
lava: 10,
water: 15
};
// Achievement system
var achievements = storage.achievements || {};
var currentAchievementPage = 0;
var achievementsPerPage = 8;
var achievementDefinitions = {
firstBlock: {
name: "First Builder",
description: "Place your first block",
unlocked: false
},
destroyer: {
name: "Destroyer",
description: "Break 10 blocks",
unlocked: false
},
architect: {
name: "Architect",
description: "Place 50 blocks",
unlocked: false
},
miner: {
name: "Miner",
description: "Break 50 blocks",
unlocked: false
},
collector: {
name: "Collector",
description: "Collect all block types",
unlocked: false
},
explorer: {
name: "Explorer",
description: "Move 100 times",
unlocked: false
},
masterBuilder: {
name: "Master Builder",
description: "Place 200 blocks",
unlocked: false
},
demolitionExpert: {
name: "Demolition Expert",
description: "Break 200 blocks",
unlocked: false
},
goldRush: {
name: "Gold Rush",
description: "Collect 20 gold blocks",
unlocked: false
},
diamondCollector: {
name: "Diamond Collector",
description: "Collect 15 diamond blocks",
unlocked: false
},
woodsman: {
name: "Woodsman",
description: "Collect 100 wood blocks",
unlocked: false
},
stoneAge: {
name: "Stone Age",
description: "Collect 150 stone blocks",
unlocked: false
},
adventurer: {
name: "Adventurer",
description: "Move 500 times",
unlocked: false
},
nomad: {
name: "Nomad",
description: "Move 1000 times",
unlocked: false
},
flightTime: {
name: "Taking Flight",
description: "Use flying mode 10 times",
unlocked: false
},
skyExplorer: {
name: "Sky Explorer",
description: "Spend 60 seconds in flying mode",
unlocked: false
},
areCrazy: {
name: "are you crazy?",
description: "Break each block type 10 times",
unlocked: false
},
boooing: {
name: "boooing",
description: "Jump 100 times",
unlocked: false
},
lavaLord: {
name: "Lava Lord",
description: "Place 25 lava blocks",
unlocked: false
},
waterMaster: {
name: "Water Master",
description: "Place 30 water blocks",
unlocked: false
}
};
// Achievement counters
var blocksPlaced = storage.blocksPlaced || 0;
var blocksBroken = storage.blocksBroken || 0;
var moveCount = storage.moveCount || 0;
var goldCollected = storage.goldCollected || 0;
var diamondCollected = storage.diamondCollected || 0;
var woodCollected = storage.woodCollected || 0;
var stoneCollected = storage.stoneCollected || 0;
var flyingActivations = storage.flyingActivations || 0;
var flyingStartTime = 0;
var totalFlyingTime = storage.totalFlyingTime || 0;
var blockTypeBrokenCount = storage.blockTypeBrokenCount || {};
var jumpCount = storage.jumpCount || 0;
var lavaPlaced = storage.lavaPlaced || 0;
var waterPlaced = storage.waterPlaced || 0;
var grid = [];
var placedBlocks = [];
var toolbarButtons = [];
var controlButtons = [];
var pressTimer = null;
var isLongPress = false;
var pressStartTime = 0;
var character = null;
var blockSelectionMenu = null;
var menuButton = null;
var achievementNotification = null;
var achievementMenu = null;
var characterGravity = 0; // Current falling speed
var characterMaxFallSpeed = 8; // Maximum falling speed
var characterGravityAcceleration = 0.5; // How fast gravity accelerates
var isFlying = false; // Flying mode toggle
var flyingSpeed = 4; // Speed when flying
var flyingVerticalSpeed = 3; // Vertical movement speed when flying
function checkAchievement(achievementId) {
if (achievements[achievementId]) return;
var achieved = false;
switch (achievementId) {
case 'firstBlock':
achieved = blocksPlaced >= 1;
break;
case 'destroyer':
achieved = blocksBroken >= 10;
break;
case 'architect':
achieved = blocksPlaced >= 50;
break;
case 'miner':
achieved = blocksBroken >= 50;
break;
case 'collector':
// Check if player has collected all block types
var hasAllTypes = true;
for (var blockType in inventory) {
if (inventory[blockType] <= 0) {
hasAllTypes = false;
break;
}
}
achieved = hasAllTypes;
break;
case 'explorer':
achieved = moveCount >= 100;
break;
case 'masterBuilder':
achieved = blocksPlaced >= 200;
break;
case 'demolitionExpert':
achieved = blocksBroken >= 200;
break;
case 'goldRush':
achieved = goldCollected >= 20;
break;
case 'diamondCollector':
achieved = diamondCollected >= 15;
break;
case 'woodsman':
achieved = woodCollected >= 100;
break;
case 'stoneAge':
achieved = stoneCollected >= 150;
break;
case 'adventurer':
achieved = moveCount >= 500;
break;
case 'nomad':
achieved = moveCount >= 1000;
break;
case 'flightTime':
achieved = flyingActivations >= 10;
break;
case 'skyExplorer':
achieved = totalFlyingTime >= 60000; // 60 seconds in milliseconds
break;
case 'areCrazy':
// Check if each block type has been broken at least 10 times
achieved = true;
for (var i = 0; i < blockTypes.length; i++) {
var blockType = blockTypes[i];
var brokenCount = blockTypeBrokenCount[blockType] || 0;
if (brokenCount < 10) {
achieved = false;
break;
}
}
break;
case 'boooing':
achieved = jumpCount >= 100;
break;
case 'lavaLord':
achieved = lavaPlaced >= 25;
break;
case 'waterMaster':
achieved = waterPlaced >= 30;
break;
}
if (achieved) {
unlockAchievement(achievementId);
}
}
function unlockAchievement(achievementId) {
if (achievements[achievementId]) return;
achievements[achievementId] = true;
storage.achievements = achievements;
// Update the achievement menu to show the newly unlocked achievement
if (achievementMenu) {
achievementMenu.updateAchievements();
// Show the achievement menu temporarily if not already visible
if (!achievementMenu.isVisible) {
achievementMenu.show();
// Auto-hide after a few seconds
LK.setTimeout(function () {
if (achievementMenu && achievementMenu.isVisible) {
achievementMenu.hide();
}
}, 3000);
}
}
}
function checkAllAchievements() {
for (var achievementId in achievementDefinitions) {
checkAchievement(achievementId);
}
}
// Initialize grid
for (var x = 0; x < GRID_WIDTH; x++) {
grid[x] = [];
for (var y = 0; y < GRID_HEIGHT; y++) {
grid[x][y] = null;
var gridCell = new GridCell();
gridCell.setGridPosition(x, y);
game.addChild(gridCell);
}
}
// Initialize character at top of world
character = new Character();
var randomX = Math.floor(Math.random() * GRID_WIDTH);
var topY = 0; // Always start at the very top
character.setGridPosition(randomX, topY);
game.addChild(character);
// Create block selection menu
blockSelectionMenu = new BlockSelectionMenu();
blockSelectionMenu.x = 1024;
blockSelectionMenu.y = 800;
LK.gui.addChild(blockSelectionMenu);
// Create achievement menu at top layer
achievementMenu = new AchievementMenu();
achievementMenu.x = 1024;
achievementMenu.y = 400;
LK.gui.addChild(achievementMenu);
// Create menu button
menuButton = new Container();
var menuBg = menuButton.attachAsset('toolbarBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
var menuText = new Text2('MENU', {
size: 36,
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuText.x = 0;
menuText.y = 0;
menuButton.addChild(menuText);
menuButton.x = 2048 - 80;
menuButton.y = 150;
menuButton.down = function (x, y, obj) {
menuBg.alpha = 1.0;
if (!blockSelectionMenu.isVisible) {
blockSelectionMenu.show();
}
};
menuButton.up = function (x, y, obj) {
menuBg.alpha = 0.8;
};
game.addChild(menuButton);
// Create achievements button at the top
var achievementsButton = new Container();
var achievementsBg = achievementsButton.attachAsset('toolbarBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
var achievementsText = new Text2('ACHIEVEMENTS', {
size: 28,
fill: 0xFFFFFF
});
achievementsText.anchor.set(0.5, 0.5);
achievementsText.x = 0;
achievementsText.y = 0;
achievementsButton.addChild(achievementsText);
achievementsButton.x = 1024;
achievementsButton.y = 50;
achievementsButton.down = function (x, y, obj) {
achievementsBg.alpha = 1.0;
if (!achievementMenu.isVisible) {
achievementMenu.show();
}
};
achievementsButton.up = function (x, y, obj) {
achievementsBg.alpha = 0.8;
};
game.addChild(achievementsButton);
// Create flying toggle button
var flyingButton = new Container();
var flyingBg = flyingButton.attachAsset('toolbarBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
var flyingText = new Text2('FLY', {
size: 32,
fill: 0xFFFFFF
});
flyingText.anchor.set(0.5, 0.5);
flyingText.x = 0;
flyingText.y = 0;
flyingButton.addChild(flyingText);
flyingButton.x = 2048 - 80;
flyingButton.y = 350;
flyingButton.down = function (x, y, obj) {
flyingBg.alpha = 1.0;
// Toggle flying mode
var wasFlying = isFlying;
isFlying = !isFlying;
// Track flying time and activations
if (isFlying && !wasFlying) {
// Started flying
flyingActivations++;
storage.flyingActivations = flyingActivations;
flyingStartTime = LK.ticks;
checkAllAchievements();
} else if (!isFlying && wasFlying) {
// Stopped flying
if (flyingStartTime > 0) {
var flyingDuration = (LK.ticks - flyingStartTime) * (1000 / 60); // Convert ticks to milliseconds
totalFlyingTime += flyingDuration;
storage.totalFlyingTime = totalFlyingTime;
flyingStartTime = 0;
checkAllAchievements();
}
}
// Update button appearance - recreate the background with new color
flyingButton.removeChild(flyingBg);
flyingBg = flyingButton.attachAsset('toolbarBg', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1.0
});
if (isFlying) {
flyingBg.tint = 0x00FF00;
} else {
flyingBg.tint = 0xFFFFFF;
}
flyingText.setText(isFlying ? 'LAND' : 'FLY');
// Visual feedback
if (isFlying) {
LK.effects.flashObject(character, 0x00FFFF, 500);
}
};
flyingButton.up = function (x, y, obj) {
flyingBg.alpha = 0.8;
};
game.addChild(flyingButton);
// Create flying status display
var flyingStatusText = new Text2('', {
size: 28,
fill: 0x00FFFF
});
flyingStatusText.anchor.set(0.5, 0);
flyingStatusText.x = 1024;
flyingStatusText.y = 100;
LK.gui.addChild(flyingStatusText);
// Create toolbar
var toolbarY = 2732 - 120;
var toolbarStartX = (2048 - blockTypes.length * 120) / 2;
for (var i = 0; i < blockTypes.length; i++) {
var button = new ToolbarButton(blockTypes[i]);
button.x = toolbarStartX + i * 120 + 60;
button.y = toolbarY;
toolbarButtons.push(button);
LK.gui.addChild(button);
}
// Create inventory display
var inventoryTexts = {};
for (var i = 0; i < blockTypes.length; i++) {
var blockType = blockTypes[i];
var inventoryText = new Text2(inventory[blockType].toString(), {
size: 36,
fill: 0xFFFFFF
});
inventoryText.anchor.set(0.5, 0);
inventoryText.x = toolbarStartX + i * 120 + 60;
inventoryText.y = toolbarY + 60;
inventoryTexts[blockType] = inventoryText;
LK.gui.addChild(inventoryText);
}
// Create control buttons
var controlButtonSize = 100;
var controlPadding = 20;
var controlCenterX = 150;
var controlCenterY = GRID_OFFSET_Y + GRID_HEIGHT * GRID_SIZE + 150;
var directions = [{
dir: 'up',
x: 0,
y: -1
}, {
dir: 'down',
x: 0,
y: 1
}, {
dir: 'left',
x: -1,
y: 0
}, {
dir: 'right',
x: 1,
y: 0
}];
for (var i = 0; i < directions.length; i++) {
var dirInfo = directions[i];
var button = new ControlButton(dirInfo.dir);
button.x = controlCenterX + dirInfo.x * (controlButtonSize + controlPadding);
button.y = controlCenterY + dirInfo.y * (controlButtonSize + controlPadding);
controlButtons.push(button);
game.addChild(button);
}
function updateToolbarSelection() {
for (var i = 0; i < toolbarButtons.length; i++) {
toolbarButtons[i].setSelected(toolbarButtons[i].blockType === selectedBlockType);
}
}
function updateInventoryDisplay() {
for (var blockType in inventoryTexts) {
inventoryTexts[blockType].setText(inventory[blockType].toString());
}
// Update block selection menu inventory counts
if (blockSelectionMenu) {
blockSelectionMenu.updateInventoryCounts();
}
}
function getGridPosition(worldX, worldY) {
var gridX = Math.floor((worldX - GRID_OFFSET_X) / GRID_SIZE);
var gridY = Math.floor((worldY - GRID_OFFSET_Y) / GRID_SIZE);
return {
x: gridX,
y: gridY
};
}
function isValidGridPosition(gridX, gridY) {
return gridX >= 0 && gridX < GRID_WIDTH && gridY >= 0 && gridY < GRID_HEIGHT;
}
function placeBlock(gridX, gridY) {
if (!isValidGridPosition(gridX, gridY)) return false;
if (grid[gridX][gridY] !== null) return false;
if (inventory[selectedBlockType] <= 0) return false;
// Don't place block on character position
if (character && gridX === character.gridX && gridY === character.gridY) return false;
var block;
if (selectedBlockType === 'sand') {
block = new Sand();
} else if (selectedBlockType === 'portal') {
block = new Portal();
} else if (selectedBlockType === 'lava') {
block = new Lava();
} else if (selectedBlockType === 'water') {
block = new Water();
} else {
block = new Block(selectedBlockType);
}
block.setGridPosition(gridX, gridY);
game.addChild(block);
grid[gridX][gridY] = block;
placedBlocks.push(block);
inventory[selectedBlockType]--;
updateInventoryDisplay();
LK.getSound('place').play();
// Track achievement progress
blocksPlaced++;
storage.blocksPlaced = blocksPlaced;
// Track lava placement for achievement
if (selectedBlockType === 'lava') {
lavaPlaced++;
storage.lavaPlaced = lavaPlaced;
}
// Track water placement for achievement
if (selectedBlockType === 'water') {
waterPlaced++;
storage.waterPlaced = waterPlaced;
}
checkAllAchievements();
return true;
}
function removeBlock(gridX, gridY) {
if (!isValidGridPosition(gridX, gridY)) return false;
if (grid[gridX][gridY] === null) return false;
var block = grid[gridX][gridY];
var blockType = block.blockType;
// If breaking a portal, teleport character to heaven biome
if (blockType === 'portal') {
// Transition to heaven biome permanently
game.setBackgroundColor(0xFFD700); // Golden heaven sky
// Flash screen with heavenly light
LK.effects.flashScreen(0xFFFFFF, 2000);
// Teleport character to top of world center
var heavenX = Math.floor(GRID_WIDTH / 2);
var heavenY = 0; // Top of the world
character.setGridPosition(heavenX, heavenY);
// Add visual effects
LK.effects.flashObject(block, 0xFFD700, 1000);
LK.effects.flashObject(character, 0xFFFFFF, 1000);
// Play portal sound
LK.getSound('portal').play();
// Clear all existing blocks to create clean heaven environment
for (var x = 0; x < GRID_WIDTH; x++) {
for (var y = 0; y < GRID_HEIGHT; y++) {
if (grid[x][y] !== null && grid[x][y] !== block) {
var existingBlock = grid[x][y];
existingBlock.destroy();
grid[x][y] = null;
// Remove from placedBlocks array
for (var j = placedBlocks.length - 1; j >= 0; j--) {
if (placedBlocks[j] === existingBlock) {
placedBlocks.splice(j, 1);
break;
}
}
}
}
}
// Generate heaven environment with golden blocks and clouds
selectedBlockType = 'gold';
// Create golden platform in heaven
var heavenPlatformY = Math.floor(GRID_HEIGHT * 0.8);
for (var x = 0; x < GRID_WIDTH; x++) {
placeBlock(x, heavenPlatformY);
if (Math.random() < 0.3) {
placeBlock(x, heavenPlatformY - 1); // Add some elevation
}
}
// Add diamond clouds scattered around
selectedBlockType = 'diamond';
for (var i = 0; i < 15; i++) {
var cloudX = Math.floor(Math.random() * GRID_WIDTH);
var cloudY = Math.floor(Math.random() * heavenPlatformY * 0.7);
if (isValidGridPosition(cloudX, cloudY) && grid[cloudX][cloudY] === null) {
placeBlock(cloudX, cloudY);
}
}
// Reset selection
selectedBlockType = 'dirt';
updateToolbarSelection();
}
// Create particle effects - multiple small pieces flying out
for (var p = 0; p < 6; p++) {
var particle = new Container();
var particleGraphics = particle.attachAsset(blockType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
particle.x = block.x;
particle.y = block.y;
game.addChild(particle);
// Random direction for each particle
var angle = p / 6 * Math.PI * 2 + (Math.random() - 0.5) * 0.5;
var speed = 100 + Math.random() * 80;
var targetX = particle.x + Math.cos(angle) * speed;
var targetY = particle.y + Math.sin(angle) * speed;
// Animate particle flying out and fading
tween(particle, {
x: targetX,
y: targetY,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0,
rotation: Math.random() * Math.PI * 2
}, {
duration: 300 + Math.random() * 200,
easing: tween.easeOut,
onFinish: function onFinish() {
particle.destroy();
}
});
}
// Add breaking animation with flash effect and rotation
LK.effects.flashObject(block, 0xFFFFFF, 200);
// Add screen shake effect for impact
var originalX = game.x;
var originalY = game.y;
var shakeIntensity = 8;
tween(game, {
x: originalX + shakeIntensity
}, {
duration: 30,
easing: tween.easeOut
});
LK.setTimeout(function () {
tween(game, {
x: originalX - shakeIntensity
}, {
duration: 30,
easing: tween.easeOut
});
}, 30);
LK.setTimeout(function () {
tween(game, {
x: originalX,
y: originalY
}, {
duration: 40,
easing: tween.easeOut
});
}, 60);
// Remove from placedBlocks array
for (var i = placedBlocks.length - 1; i >= 0; i--) {
if (placedBlocks[i] === block) {
placedBlocks.splice(i, 1);
break;
}
}
// Animate block destruction with scaling and rotation
tween(block, {
scaleX: 0,
scaleY: 0,
alpha: 0,
rotation: Math.PI * 0.5
}, {
duration: 150,
easing: tween.easeIn,
onFinish: function onFinish() {
block.destroy();
}
});
grid[gridX][gridY] = null;
inventory[blockType]++;
updateInventoryDisplay();
LK.getSound('break').play();
// Track achievement progress
blocksBroken++;
storage.blocksBroken = blocksBroken;
// Track specific resource collection
if (blockType === 'gold') {
goldCollected++;
storage.goldCollected = goldCollected;
} else if (blockType === 'diamond') {
diamondCollected++;
storage.diamondCollected = diamondCollected;
} else if (blockType === 'wood') {
woodCollected++;
storage.woodCollected = woodCollected;
} else if (blockType === 'stone') {
stoneCollected++;
storage.stoneCollected = stoneCollected;
}
// Track block type breaking for 'are you crazy?' achievement
if (!blockTypeBrokenCount[blockType]) {
blockTypeBrokenCount[blockType] = 0;
}
blockTypeBrokenCount[blockType]++;
storage.blockTypeBrokenCount = blockTypeBrokenCount;
checkAllAchievements();
return true;
}
// Initialize toolbar selection
updateToolbarSelection();
updateInventoryDisplay();
game.down = function (x, y, obj) {
var gridPos = getGridPosition(x, y);
pressStartTime = LK.ticks;
isLongPress = false;
// Start long press timer for breaking blocks
if (pressTimer) {
LK.clearTimeout(pressTimer);
}
pressTimer = LK.setTimeout(function () {
isLongPress = true;
// Break only the specific block at clicked position
if (isValidGridPosition(gridPos.x, gridPos.y) && grid[gridPos.x][gridPos.y] !== null) {
removeBlock(gridPos.x, gridPos.y);
}
}, 500); // 500ms long press
};
game.up = function (x, y, obj) {
// Clear the long press timer
if (pressTimer) {
LK.clearTimeout(pressTimer);
pressTimer = null;
}
// If it wasn't a long press, place a block
if (!isLongPress) {
var gridPos = getGridPosition(x, y);
if (isValidGridPosition(gridPos.x, gridPos.y)) {
placeBlock(gridPos.x, gridPos.y);
}
}
};
// Generate procedural world with biomes
function generateWorld() {
// Reset selected block type to dirt for generation
selectedBlockType = 'dirt';
updateToolbarSelection();
// Create height map for smooth terrain
var heightMap = [];
var baseHeightLevel = Math.floor(GRID_HEIGHT * 0.7); // Base terrain level
for (var x = 0; x < GRID_WIDTH; x++) {
// Generate smooth terrain using sine waves
var primary = Math.sin(x / GRID_WIDTH * Math.PI * 2) * 3;
var secondary = Math.sin(x / GRID_WIDTH * Math.PI * 6) * 1.5;
var noise = (Math.random() - 0.5) * 2;
var height = baseHeightLevel + primary + secondary + noise;
heightMap[x] = Math.floor(Math.max(5, Math.min(height, GRID_HEIGHT - 3)));
}
// Smooth the height map to reduce abrupt changes
for (var pass = 0; pass < 2; pass++) {
for (var x = 1; x < GRID_WIDTH - 1; x++) {
var avg = (heightMap[x - 1] + heightMap[x] + heightMap[x + 1]) / 3;
heightMap[x] = Math.floor(avg);
}
}
// Define biome regions
var biome1 = Math.floor(GRID_WIDTH * 0.33);
var biome2 = Math.floor(GRID_WIDTH * 0.66);
var biomes = ['plains', 'forest', 'mountains'];
var currentBiomes = [biomes[Math.floor(Math.random() * biomes.length)], biomes[Math.floor(Math.random() * biomes.length)], biomes[Math.floor(Math.random() * biomes.length)]];
// Generate terrain layers
for (var x = 0; x < GRID_WIDTH; x++) {
var currentBiome;
if (x < biome1) {
currentBiome = currentBiomes[0];
} else if (x < biome2) {
currentBiome = currentBiomes[1];
} else {
currentBiome = currentBiomes[2];
}
var baseHeight = heightMap[x];
var surfaceBlock = 'grass';
var treeChance = 0.15;
var stoneChance = 0.1;
// Biome-specific properties
if (currentBiome === 'plains') {
surfaceBlock = 'grass';
treeChance = 0.08;
stoneChance = 0.05;
} else if (currentBiome === 'forest') {
surfaceBlock = 'grass';
treeChance = 0.25; // More trees
stoneChance = 0.08;
} else if (currentBiome === 'mountains') {
surfaceBlock = 'stone'; // Rocky surface
treeChance = 0.05; // Few trees at high altitude
stoneChance = 0.25; // Very rocky
// Adjust height for mountains
baseHeight = Math.max(baseHeight - 3, Math.floor(GRID_HEIGHT * 0.5));
}
// Place surface layer
selectedBlockType = surfaceBlock;
placeBlock(x, baseHeight);
// Place sub-surface layers consistently
selectedBlockType = 'dirt';
var dirtLayers = currentBiome === 'mountains' ? 2 : 3;
for (var y = baseHeight + 1; y < Math.min(baseHeight + dirtLayers + 1, GRID_HEIGHT); y++) {
placeBlock(x, y);
}
// Place stone in deeper layers consistently
selectedBlockType = 'stone';
for (var y = baseHeight + dirtLayers + 1; y < GRID_HEIGHT; y++) {
var stoneProb = currentBiome === 'mountains' ? 0.9 : 0.8;
if (Math.random() < stoneProb) {
placeBlock(x, y);
}
}
// Add trees in groups for natural look
if (Math.random() < treeChance && baseHeight > 2) {
selectedBlockType = 'wood';
var treeHeight;
if (currentBiome === 'forest') {
treeHeight = Math.floor(Math.random() * 3) + 3; // Tall trees
} else {
treeHeight = Math.floor(Math.random() * 2) + 2; // Normal trees
}
for (var i = 0; i < treeHeight; i++) {
var treeY = baseHeight - i - 1;
if (treeY >= 0) {
placeBlock(x, treeY);
}
}
// Add tree leaves/branches
if (currentBiome === 'forest' && treeHeight >= 3) {
selectedBlockType = 'grassLight';
if (x > 0 && baseHeight - treeHeight >= 0) {
placeBlock(x - 1, baseHeight - treeHeight + 1);
}
if (x < GRID_WIDTH - 1 && baseHeight - treeHeight >= 0) {
placeBlock(x + 1, baseHeight - treeHeight + 1);
}
}
}
// Add surface features sparingly
if (Math.random() < stoneChance && baseHeight > 0) {
selectedBlockType = 'stone';
placeBlock(x, baseHeight - 1);
}
// Add biome-specific features occasionally
if (currentBiome === 'mountains' && Math.random() < 0.1) {
selectedBlockType = 'stone';
if (baseHeight > 1) {
placeBlock(x, baseHeight - 1);
}
}
}
// Add minimal scattered resources in logical places
for (var i = 0; i < 15; i++) {
var randX = Math.floor(Math.random() * GRID_WIDTH);
var surfaceY = heightMap[randX];
// Place resources near surface or in underground areas
var randY = Math.random() < 0.6 ? surfaceY + Math.floor(Math.random() * 3) + 1 : Math.floor(Math.random() * (GRID_HEIGHT - surfaceY - 5)) + surfaceY + 3;
if (isValidGridPosition(randX, randY) && grid[randX][randY] === null) {
var resourceType;
if (randY > surfaceY + 2) {
// Underground resources
resourceType = Math.random() < 0.6 ? 'stone' : 'dirt';
} else {
// Surface resources
resourceType = blockTypes[Math.floor(Math.random() * blockTypes.length)];
}
selectedBlockType = resourceType;
placeBlock(randX, randY);
}
}
// Add rare precious blocks deep underground
for (var i = 0; i < 3; i++) {
var randX = Math.floor(Math.random() * GRID_WIDTH);
var surfaceY = heightMap[randX];
// Place deep underground only
var randY = Math.floor(Math.random() * 3) + GRID_HEIGHT - 5;
if (isValidGridPosition(randX, randY) && grid[randX][randY] === null) {
var preciousType = Math.random() < 0.4 ? 'diamond' : 'gold';
selectedBlockType = preciousType;
placeBlock(randX, randY);
}
}
// Add portals with 10% chance - place 1-2 portals randomly
if (Math.random() < 0.1) {
var numPortals = Math.floor(Math.random() * 2) + 1; // 1 or 2 portals
for (var p = 0; p < numPortals; p++) {
var attempts = 0;
var portalPlaced = false;
while (attempts < 20 && !portalPlaced) {
var randX = Math.floor(Math.random() * GRID_WIDTH);
var surfaceY = heightMap[randX];
var randY = surfaceY - Math.floor(Math.random() * 3) - 1; // Place above surface
if (isValidGridPosition(randX, randY) && grid[randX][randY] === null) {
selectedBlockType = 'portal';
if (placeBlock(randX, randY)) {
portalPlaced = true;
}
}
attempts++;
}
}
}
// Create a few small, deliberate caves
for (var caveCount = 0; caveCount < 2; caveCount++) {
var caveX = Math.floor(Math.random() * (GRID_WIDTH - 4)) + 2;
var surfaceHeight = heightMap[caveX];
var caveY = surfaceHeight + Math.floor(Math.random() * 4) + 3;
var caveSize = 1;
// Create small hollow areas
for (var cx = caveX - caveSize; cx <= caveX + caveSize; cx++) {
for (var cy = caveY - caveSize; cy <= caveY + caveSize; cy++) {
if (isValidGridPosition(cx, cy) && grid[cx][cy] !== null) {
var block = grid[cx][cy];
if (block) {
block.destroy();
grid[cx][cy] = null;
// Remove from placedBlocks array
for (var j = placedBlocks.length - 1; j >= 0; j--) {
if (placedBlocks[j] === block) {
placedBlocks.splice(j, 1);
break;
}
}
}
}
}
}
}
// Reset to dirt selection
selectedBlockType = 'dirt';
updateToolbarSelection();
}
// Generate the world after a short delay
LK.setTimeout(function () {
generateWorld();
}, 500);
function applyGravity() {
var blocksToMove = [];
// Check all blocks from bottom to top
for (var y = GRID_HEIGHT - 2; y >= 0; y--) {
for (var x = 0; x < GRID_WIDTH; x++) {
var block = grid[x][y];
if (block !== null) {
// Check if block has support below
var hasSupport = false;
for (var checkY = y + 1; checkY < GRID_HEIGHT; checkY++) {
if (grid[x][checkY] !== null) {
hasSupport = true;
break;
}
}
// If no support, mark for falling
if (!hasSupport) {
var fallY = y;
// Find where it should fall to
for (var targetY = y + 1; targetY < GRID_HEIGHT; targetY++) {
if (grid[x][targetY] !== null) {
break;
}
fallY = targetY;
}
if (fallY !== y) {
blocksToMove.push({
block: block,
fromX: x,
fromY: y,
toX: x,
toY: fallY
});
}
}
}
}
}
// Move blocks that need to fall
for (var i = 0; i < blocksToMove.length; i++) {
var moveData = blocksToMove[i];
var block = moveData.block;
// Clear old position
grid[moveData.fromX][moveData.fromY] = null;
// Set new position
grid[moveData.toX][moveData.toY] = block;
block.setGridPosition(moveData.toX, moveData.toY);
}
}
game.update = function () {
// Apply gravity every few ticks to make blocks fall
if (LK.ticks % 10 === 0) {
applyGravity();
}
// Update flying status display
if (isFlying) {
flyingStatusText.setText('FLYING MODE');
flyingStatusText.visible = true;
// Track flying time for achievements
if (flyingStartTime > 0) {
var currentFlyingTime = (LK.ticks - flyingStartTime) * (1000 / 60);
if (currentFlyingTime >= 1000) {
// Check every second
totalFlyingTime += 1000;
storage.totalFlyingTime = totalFlyingTime;
flyingStartTime = LK.ticks;
checkAllAchievements();
}
}
} else {
flyingStatusText.visible = false;
}
// Replenish inventory slowly over time
if (LK.ticks % 600 === 0) {
// Every 10 seconds
for (var blockType in inventory) {
if (inventory[blockType] < 50) {
inventory[blockType]++;
}
}
updateInventoryDisplay();
checkAllAchievements();
}
};