User prompt
Prevent passing the stone lines that are on the sides by player or enemies!
User prompt
when game is on pause enemies must not move too they killed player when he is doing shopping!
User prompt
when click shop pause the game
User prompt
make it can be continuously not just one time make it can be click and show at anytime
User prompt
when click shopsign show th shopbackground
User prompt
when close shopbackground place shopsign on the top right of the screen and exactly below crystal score text
User prompt
lower the shopsign to be aligned with top edge of shopbackground
Code edit (1 edits merged)
Please save this source code
User prompt
center the shopbackground on the screen
Code edit (1 edits merged)
Please save this source code
User prompt
fit the intro background to the screen
Code edit (1 edits merged)
Please save this source code
User prompt
remove it from the intro let it in the start of the game only
User prompt
Add the shopbackground and its shopsign when game start not in the intro! Add close button for it on the right top corner.
User prompt
Change the areas from 2000 miles to 1000 miles
User prompt
add shopbackground to the game align its top right corner with top right corner of the screen. add shopsign outside the top left edge of the shopbackground.
User prompt
fit the Introbackground to the screen
Code edit (2 edits merged)
Please save this source code
User prompt
scale the intro background as the screen scale
Code edit (3 edits merged)
Please save this source code
User prompt
// Add intro background - since we're adding to GUI, we need different scaling approach var introBackground = introContainer.attachAsset('IntroBackground', { anchorX: 0.5, anchorY: 0.5, x: 1000, y: 1000 });
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Fit the intro background to the screen same scale
Code edit (6 edits merged)
Please save this source code
/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1"); var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Enemy = Container.expand(function () { var self = Container.call(this); self.enemyTypeString = null; self.config = null; self.graphics = null; self.movementSpeed = 4; // Slightly slower than player's base dig speed self.targetOreBlock = null; self.shootCooldown = 0; self.maxShootCooldown = 240; // 4 seconds at 60FPS self.associatedZoneIndex = -1; self.fiftyMileZoneId = -1; // To track which 50-mile depth zone this enemy belongs to self.initEnemy = function (enemyTypeString, initialX, initialY, zoneIndex, fiftyMileZoneId) { self.fiftyMileZoneId = fiftyMileZoneId; self.enemyTypeString = enemyTypeString; self.config = ENEMY_CONFIG[enemyTypeString]; // ENEMY_CONFIG must be global if (!self.config) { console.log("Error: Invalid enemyTypeString for Enemy: " + enemyTypeString); self.shouldBeDestroyed = true; return; } self.graphics = self.attachAsset(self.config.asset, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.8, // Enemies are 100x100, scale up to be more visible (like 180x180) scaleY: 1.8 }); self.x = initialX; self.y = initialY; self.associatedZoneIndex = zoneIndex; self.shootCooldown = Math.random() * self.maxShootCooldown; self.isAttackingPlayer = false; // Initialize new state property self.hasCollectedTreasure = false; // Track if enemy has collected treasure for shooting self.lastX = self.x; // Initialize last positions self.lastY = self.y; }; self.findTargetOre = function () { // Don't interrupt ore finding during scrolling - enemies should continue seeking targets var closestOre = null; var minDistanceSq = Infinity; for (var r = 0; r < terrain.length; r++) { for (var c = 0; c < terrain[r].length; c++) { var block = terrain[r][c]; if (block && block.blockType === 'terrain' && block.visible && block.hasTreasure && block.treasureType === self.config.ore) { if (typeof block.originalDepth === 'undefined') { continue; } var oreDepthInMiles = Math.floor(block.originalDepth / (blockSize / 2)); var oreTreasureZone = Math.floor(oreDepthInMiles / 1000) % 5; if (oreTreasureZone !== self.associatedZoneIndex) { continue; } // No vertical restrictions - enemies can target any visible ore var dx = block.x + blockSize / 2 - self.x; var dy = block.y + blockSize / 2 - self.y; var distSq = dx * dx + dy * dy; if (distSq < minDistanceSq) { minDistanceSq = distSq; closestOre = block; } } } } self.targetOreBlock = closestOre; }; self.shoot = function () { if (!player || player.destroyed || !gameStarted) { return; } var newShot = new EnemyShot(); // Ensure newShot is properly initialized before adding to game scene game.addChild(newShot); // Add to scene first so assets can be loaded if attachAsset is deferred // Pass the enemy's configured ore type for the shot's appearance newShot.initShot(self.x, self.y, player.x, player.y, self.config.ore); enemyShots.push(newShot); // enemyShots must be global self.shootCooldown = self.maxShootCooldown + (Math.random() * 60 - 30); // Add some variance // No longer consuming treasure for shooting }; self.update = function () { if (self.shouldBeDestroyed || !self.graphics) { return; } // Don't update enemy position if game is paused if (gamePaused) { return; } self.lastX = self.x; self.lastY = self.y; // If enemy scrolled off top by main scroll, mark for destroy // (graphics height check in game.update is a fallback) if (self.y < -(self.graphics.height * self.graphics.scale.y) && !isScrolling) { self.shouldBeDestroyed = true; return; } if (self.isAttackingPlayer) { if (player && !player.destroyed && gameStarted) { var playerTargetX = player.x; var playerTargetY = player.y; var dxToPlayer = playerTargetX - self.x; var dyToPlayer = playerTargetY - self.y; var distToPlayer = Math.sqrt(dxToPlayer * dxToPlayer + dyToPlayer * dyToPlayer); var effectiveSpeedAttack = self.movementSpeed; if (isScrolling) { effectiveSpeedAttack /= 2; } if (distToPlayer < self.graphics.width * self.graphics.scale.x / 2 + player.playerGraphics.width / 2 && distToPlayer < self.graphics.height * self.graphics.scale.y / 2 + player.playerGraphics.height / 2) { // Check for intersection / close proximity self.x = playerTargetX; // Snap to player for effect self.y = playerTargetY; handlePlayerDeath(); // Call new death handler return; // Stop further updates for this enemy } else if (distToPlayer > 0) { // Check distToPlayer > 0 to prevent division by zero // Allow free diagonal movement towards player var moveX = dxToPlayer / distToPlayer * effectiveSpeedAttack; var moveY = dyToPlayer / distToPlayer * effectiveSpeedAttack; // Compensate for scrolling - if screen is scrolling up, enemy needs to move down additionally if (isScrolling) { // During scroll, everything moves up by blockSize over scrollAnimationDuration // Calculate the per-frame scroll compensation var scrollCompensation = blockSize / (scrollAnimationDuration / (1000 / 60)); // pixels per frame moveY += scrollCompensation; // Add downward movement to compensate for upward scroll } // Calculate mineable area boundaries var leftBoundary = mineableStartX * blockSize + blockSize / 2; var rightBoundary = (mineableEndX - 1) * blockSize + blockSize / 2; // Apply movement but clamp to mineable area var newX = self.x + moveX; self.x = Math.max(leftBoundary, Math.min(newX, rightBoundary)); self.y += moveY; mineBlock(self.x, self.y, false); // Enemy action, dig while moving } } else { // Player doesn't exist or is destroyed, revert to ore-seeking self.isAttackingPlayer = false; } } else { // Not attacking player, try to find/mine ore if (!self.targetOreBlock || !self.targetOreBlock.visible || !self.targetOreBlock.hasTreasure || self.targetOreBlock.treasureType !== self.config.ore) { self.findTargetOre(); if (!self.targetOreBlock) { // No ore found, switch to attacking player self.isAttackingPlayer = true; // Optionally, immediately try to move towards player this frame (or wait for next update cycle) // For now, state change is enough, next update cycle will handle movement. } } if (!self.isAttackingPlayer && self.targetOreBlock) { // Check again in case state just changed var targetX = self.targetOreBlock.x + blockSize / 2; var targetY = self.targetOreBlock.y + blockSize / 2; var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var effectiveSpeed = self.movementSpeed; if (isScrolling) { effectiveSpeed /= 2; } if (distance < effectiveSpeed) { self.x = targetX; self.y = targetY; // Let enemy dig any terrain block (not just ore) if (self.targetOreBlock && self.targetOreBlock.visible) { var minedCorrectOre = false; // Check if this is the enemy's specific treasure before actual mining action if (self.targetOreBlock.hasTreasure && self.targetOreBlock.treasureType === self.config.ore) { minedCorrectOre = true; } // Mine the block at enemy position (200x200 area like player) // This action "uncovers" the treasure. mineBlock(self.x, self.y, false); // false = not player action if (minedCorrectOre) { self.hasCollectedTreasure = true; // Confirm treasure collection // No longer immediately shooting when collecting treasure } } self.targetOreBlock = null; } else if (distance > 0) { // Check distance > 0 to prevent division by zero // Allow free diagonal movement - move in both X and Y simultaneously var moveX = dx / distance * effectiveSpeed; var moveY = dy / distance * effectiveSpeed; // Compensate for scrolling when moving if (isScrolling) { var scrollCompensation = blockSize / (scrollAnimationDuration / (1000 / 60)); moveY += scrollCompensation; } // Calculate mineable area boundaries var leftBoundary = mineableStartX * blockSize + blockSize / 2; var rightBoundary = (mineableEndX - 1) * blockSize + blockSize / 2; // Apply movement but clamp to mineable area var newX = self.x + moveX; self.x = Math.max(leftBoundary, Math.min(newX, rightBoundary)); self.y += moveY; mineBlock(self.x, self.y, false); // Enemy action, dig while moving } } } // Enemy sprite flipping logic (after position update) if (self.graphics) { var baseScaleX = 1.8; // Defined in attachAsset for enemies // Game width is 2048, center is 1024 if (self.x < 1024) { // Facing left self.graphics.scale.x = -baseScaleX; } else { // Facing right or center self.graphics.scale.x = baseScaleX; } } self.shootCooldown--; if (self.shootCooldown <= 0 && player && !player.destroyed && gameStarted) { // Free shooting when facing player, no treasure collection required self.shoot(); } }; return self; }); var EnemyShot = Container.expand(function () { var self = Container.call(this); self.graphics = null; self.speed = 7; // pixels per frame - Reduced from 15 to slow down shots self.maxDistance = 600; // Distance limit of 600px for enemy shots self.distanceTraveled = 0; self.velocity = { x: 0, y: 0 }; // Normalized direction vector // Added shotTypeAssetId parameter self.initShot = function (startX, startY, targetX, targetY, shotTypeAssetId) { self.x = startX; self.y = startY; if (!self.graphics) { if (shotTypeAssetId) { // Treasure assets are 200x200, shots are 50x50. Scale factor = 50/200 = 0.25 var scaleFactor = 0.25; self.graphics = self.attachAsset(shotTypeAssetId, { anchorX: 0.5, anchorY: 0.5, scaleX: scaleFactor, scaleY: scaleFactor }); } else { // Fallback to the generic 'EnemyShot' asset if no specific type is provided // This case should ideally not be hit if Enemies always specify an ore type. self.graphics = self.attachAsset('EnemyShot', { anchorX: 0.5, anchorY: 0.5 }); } } var dx = targetX - startX; var dy = targetY - startY; var magnitude = Math.sqrt(dx * dx + dy * dy); if (magnitude > 0) { self.velocity.x = dx / magnitude; self.velocity.y = dy / magnitude; } else { self.velocity.x = 0; self.velocity.y = 1; // Default to shooting downwards if target is same position } self.lastX = self.x; // Initialize last positions self.lastY = self.y; }; self.update = function () { if (self.shouldBeDestroyed || !self.graphics) { return; } // Don't update shot position if game is paused if (gamePaused) { return; } var moveX = self.velocity.x * self.speed; var moveY = self.velocity.y * self.speed; self.lastX = self.x; // Store previous position for checks self.lastY = self.y; self.x += moveX; self.y += moveY; self.distanceTraveled += Math.sqrt(moveX * moveX + moveY * moveY); // Check for max distance or off-screen // Ensure graphics exist before checking width/height var graphicWidth = self.graphics ? self.graphics.width : 50; var graphicHeight = self.graphics ? self.graphics.height : 50; if (self.distanceTraveled >= self.maxDistance || self.y < -graphicHeight || self.y > 2732 + graphicHeight || self.x < -graphicWidth || self.x > 2048 + graphicWidth) { self.shouldBeDestroyed = true; } // Collision with player (player is global) if (player && !player.destroyed && self.intersects(player) && !self.shouldBeDestroyed) { handlePlayerDeath(); // Call new death handler self.shouldBeDestroyed = true; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); // Store playerGraphics on self to allow access from game code for flipping self.playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.playerGraphics.alpha = 1; return self; }); var ScoreBackground = Container.expand(function () { var self = Container.call(this); self.panel = null; self.titleText = null; self.scoresListText = null; self.closeButton = null; self.closeButtonText = null; self.init = function () { // Panel background self.panel = self.attachAsset('ScoreBackgroundPanel', { anchorX: 0.5, anchorY: 0.5, x: 0, // Centered because ScoreBackground instance will be added to LK.gui.center y: 0 }); self.panel.alpha = 0.95; // Slightly transparent self.panel.interactive = true; self.panel.down = function () { if (activeScoreBackground === self) { activeScoreBackground = null; // Clear the global reference } self.destroy(); }; // Title Text self.titleText = new Text2('High Scores', { size: 120, fill: 0xFFFFFF // White text }); self.titleText.anchor.set(0.5, 0.5); self.titleText.x = 0; self.titleText.y = -self.panel.height / 2 + 120; // Position near top of panel self.addChild(self.titleText); // Retrieve player's Upit name and ID from storage, with defaults // The 'storage' variable is globally available if imported. var currentPlayerName = "You"; // Default name if (typeof storage !== 'undefined' && storage.playerName) { currentPlayerName = storage.playerName; } var currentPlayerID = "YOUR_ID"; // Default ID if (typeof storage !== 'undefined' && storage.playerUpitID) { currentPlayerID = storage.playerUpitID; } var currentScore = LK.getScore(); // Current session score var leaderboardData = []; // Initialize an empty list for leaderboard entries // Attempt to load leaderboard from storage (assuming Upit populates these) // storage.leaderboardNames, storage.leaderboardScores, storage.leaderboardIds are expected to be arrays of literals. if (typeof storage !== 'undefined' && Array.isArray(storage.leaderboardNames) && Array.isArray(storage.leaderboardScores) && Array.isArray(storage.leaderboardIds) && storage.leaderboardNames.length === storage.leaderboardScores.length && storage.leaderboardNames.length === storage.leaderboardIds.length) { for (var i = 0; i < storage.leaderboardNames.length; i++) { // Ensure scores are numbers, default to 0 if not. var scoreValue = parseInt(storage.leaderboardScores[i], 10); if (isNaN(scoreValue)) { scoreValue = 0; } leaderboardData.push({ name: String(storage.leaderboardNames[i]), // Ensure name is a string id: String(storage.leaderboardIds[i]), // Ensure ID is a string score: scoreValue }); } } // Prepare current player's entry var playerEntry = { name: currentPlayerName, // Already retrieved from storage or defaulted id: currentPlayerID, // Already retrieved from storage or defaulted score: currentScore // From LK.getScore() }; // Merge current player's score: update if exists and current is higher, or add if new. var playerInStoredLeaderboard = false; for (var i = 0; i < leaderboardData.length; i++) { if (leaderboardData[i].id === playerEntry.id) { playerInStoredLeaderboard = true; if (playerEntry.score > leaderboardData[i].score) { leaderboardData[i].score = playerEntry.score; // Optionally update name if it could change, though ID is key leaderboardData[i].name = playerEntry.name; } break; } } if (!playerInStoredLeaderboard) { leaderboardData.push(playerEntry); } // Sort by score descending leaderboardData.sort(function (a, b) { return b.score - a.score; }); // Create a unique list by ID, preferring the first encountered (highest score due to sort) var uniqueLeaderboardData = []; var idsOnLeaderboard = {}; // Use an object as a set for quick ID lookups for (var i = 0; i < leaderboardData.length; i++) { var entry = leaderboardData[i]; if (!idsOnLeaderboard[entry.id]) { uniqueLeaderboardData.push(entry); idsOnLeaderboard[entry.id] = true; } } // Limit to top N, e.g., top 5 var topScores = uniqueLeaderboardData.slice(0, 5); var scoresString = ""; if (topScores.length > 0) { for (var i = 0; i < topScores.length; i++) { var entry = topScores[i]; var rank = i + 1; var displayName = entry.name; // Highlight the current player if (entry.name === currentPlayerName && entry.id === currentPlayerID && entry.score === currentScore) { displayName = "* " + displayName + " *"; } scoresString += rank + ". " + displayName + " (" + entry.id + "): " + entry.score + "\n"; } } else if (currentScore > 0) { // If leaderboard empty but player has score scoresString = "1. * " + currentPlayerName + " * (" + currentPlayerID + "): " + currentScore + "\n"; } else { scoresString = "No scores yet. Be the first!"; } self.scoresListText = new Text2('', { // Initialize with empty string size: 40, // Adjusted size for better fit fill: 0xFFFFFF // White text }); self.scoresListText.setText(scoresString.trim()); // Set the actual text content self.scoresListText.anchor.set(0.5, 0); // Anchor top-center for the text block self.scoresListText.x = 0; // Position below the title, provide enough space for title and some padding self.scoresListText.y = -self.panel.height / 2 + self.titleText.height + 120; // e.g. -1000 + 120 (title size) + 120 (padding) = -760 self.addChild(self.scoresListText); // Close button graphic self.closeButton = self.attachAsset('CloseButtonAsset', { anchorX: 0.5, anchorY: 0.5, x: self.panel.width / 2 - 80, // Top-right corner of the panel y: -self.panel.height / 2 + 80 }); self.closeButton.interactive = true; // Text "X" for the close button self.closeButtonText = new Text2('X', { size: 70, fill: 0xFFFFFF // White "X" }); self.closeButtonText.anchor.set(0.5, 0.5); self.closeButtonText.x = self.closeButton.x; self.closeButtonText.y = self.closeButton.y; self.addChild(self.closeButtonText); // Close button functionality self.closeButton.down = function () { if (activeScoreBackground === self) { activeScoreBackground = null; // Clear the global reference } self.destroy(); }; }; // Call init when a new ScoreBackground is created, if preferred // Or call it manually after creating an instance, e.g., new ScoreBackground().init(); return self; }); var StoneBlock = Container.expand(function () { var self = Container.call(this); self.blockType = 'stone'; var stoneGraphics = self.attachAsset('stone', { anchorX: 0, anchorY: 0 }); self.mine = function () { return false; // Stone blocks cannot be mined }; return self; }); var TerrainBlock = Container.expand(function () { var self = Container.call(this); self.blockType = 'terrain'; self.hasTreasure = false; self.treasureType = null; self.treasureGraphics = null; var blockGraphics = self.attachAsset('terrain', { anchorX: 0, anchorY: 0 }); self.graphics = blockGraphics; self.setTreasure = function (type, blockDepth) { // Added blockDepth argument self.hasTreasure = true; self.treasureType = type; self.originalDepth = blockDepth; // Store the depth at which this treasure was generated // Create and display the actual treasure image if (type) { self.treasureGraphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5, x: blockSize / 2, y: blockSize / 2, scaleX: 0.8, scaleY: 0.8 }); } }; self.mine = function () { if (self.blockType === 'stone') { return false; } self.visible = false; if (self.treasureGraphics) { self.treasureGraphics.visible = false; } return true; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // A flat red for the button // A nice dark blue/grey game.setBackgroundColor(0x000000); var blockSize = 200; var gridWidth = Math.floor(2048 / blockSize); var gridHeight = Math.floor(2732 / blockSize); var stoneWallWidth = 1; var mineableStartX = stoneWallWidth; var mineableEndX = gridWidth - stoneWallWidth; var terrain = []; var player; var depth = 0; var score = 0; var isDragging = false; var targetX = null; var targetY = null; var isScrolling = false; // Flag to indicate if a scroll animation is in progress var playerSpeed = 40; // Pixels per frame var scrollAnimationDuration = 500; // Duration for scroll animation in milliseconds var treasureCounts = { bronze: 0, silver: 0, gold: 0, diamond: 0, crystal: 0 }; var INITIAL_PLAYER_LIVES = 5; var playerLives = INITIAL_PLAYER_LIVES; var enemies = []; var enemyShots = []; var FIFTY_MILE_DEPTH_PIXELS = 50 * (blockSize / 2); // 50 miles * 100 pixels/mile = 5000 pixels var ENEMY_SPAWN_CHANCE = 0.03; // 3% chance per eligible block in a new row var MAX_ENEMIES_PER_ZONE_ONSCREEN = 2; // Limit enemies per zone currently visible var ENEMY_CONFIG = { 'bronze': { asset: 'BronzeEnemy', ore: 'bronze', zone: 0 }, 'silver': { asset: 'SilverEnemy', ore: 'silver', zone: 1 }, 'gold': { asset: 'GoldEnemy', ore: 'gold', zone: 2 }, 'diamond': { asset: 'DiamondEnemy', ore: 'diamond', zone: 3 }, 'crystal': { asset: 'CrystalEnemy', ore: 'crystal', zone: 4 } }; var ZONE_TO_ENEMY_TYPE = ['bronze', 'silver', 'gold', 'diamond', 'crystal']; // Each 1000 miles (depth units) has a specific treasure type var gameStarted = false; var introContainer = null; var activeScoreBackground = null; // To manage the score panel instance var lastFiftyMileSpawnCheckDepth = 0; // Track last depth where 50-mile enemy spawn check occurred var gamePaused = false; // Track if game is paused function movePlayerTowardsTarget() { if (targetX === null || targetY === null || !player) { return; } // Calculate mineable area boundaries var leftBoundary = mineableStartX * blockSize + blockSize / 2; var rightBoundary = (mineableEndX - 1) * blockSize + blockSize / 2; // Clamp target position to stay within mineable area targetX = Math.max(leftBoundary, Math.min(targetX, rightBoundary)); // Move player towards target position, restricting to X or Y axis per update var dx = targetX - player.x; var dy = targetY - player.y; if (dx === 0 && dy === 0) { // Player is already at the target, no movement calculation needed. // The mineBlock() call after this block will handle mining at the current position. } else if (Math.abs(dx) > Math.abs(dy)) { // Prioritize horizontal movement if (Math.abs(dx) > playerSpeed) { var newX = player.x + Math.sign(dx) * playerSpeed; // Clamp player position to stay within mineable area player.x = Math.max(leftBoundary, Math.min(newX, rightBoundary)); } else { player.x = targetX; // Snap to target X } } else { // Prioritize vertical movement if |dy| >= |dx| (this includes dx === 0, or |dx| === |dy|) if (Math.abs(dy) > playerSpeed) { player.y += Math.sign(dy) * playerSpeed; } else { player.y = targetY; // Snap to target Y } } // Mine at player position (true = player action) mineBlock(player.x, player.y, true); // Player sprite flipping logic if (player && player.playerGraphics) { // Game width is 2048, center is 1024 if (player.x < 1024) { // Facing left player.playerGraphics.scale.x = -1; // Default scale is 1 } else { // Facing right or center player.playerGraphics.scale.x = 1; // Default scale is 1 } } } // Create score text first (will be at the back) var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.y = 90; scoreText.visible = false; // Hide initially LK.gui.top.addChild(scoreText); // Create depth text second (will be in the middle) var depthText = new Text2('Depth: 0m', { size: 60, fill: 0xFFFFFF }); // Create lives text var livesText = new Text2('Lives: ' + playerLives, { size: 60, // Same size as depth text fill: 0xFFFFFF }); var UI_TEXT_SPACING = 20; // Space between lives and depth text livesText.anchor.set(1, 0); // Anchor right, top livesText.y = 30; livesText.x = -UI_TEXT_SPACING / 2; // Position to the left of center livesText.visible = false; // Hide initially LK.gui.top.addChild(livesText); depthText.anchor.set(0, 0); // Anchor left, top depthText.y = 30; depthText.x = UI_TEXT_SPACING / 2; // Position to the right of center depthText.visible = false; // Hide initially LK.gui.top.addChild(depthText); // Create treasure count displays var treasureTexts = {}; var treasureTypes = ['bronze', 'silver', 'gold', 'diamond', 'crystal']; var treasureColors = { bronze: 0xCD7F32, silver: 0xC0C0C0, gold: 0xFFD700, diamond: 0x00FFFF, crystal: 0xFF69B4 }; // Create container for treasure counts var treasureContainer = new Container(); treasureContainer.y = -20; // Move to the very top edge treasureContainer.visible = false; // Hide initially // Create text for each treasure type var xOffset = -500; for (var i = 0; i < treasureTypes.length; i++) { var type = treasureTypes[i]; var text = new Text2(type.charAt(0).toUpperCase() + type.slice(1) + ': 0', { size: 50, fill: treasureColors[type] }); text.anchor.set(0.5, 0); text.x = xOffset + i * 250; treasureContainer.addChild(text); treasureTexts[type] = text; } // Add container to GUI after all texts are created to ensure it appears on top LK.gui.top.addChild(treasureContainer); function getTreasureType(currentDepth) { // Each 5000 miles (250000 pixels) has a specific treasure type var depthInMiles = Math.floor(currentDepth / (blockSize / 2)); // Convert pixels to abstract depth units (e.g., "miles" in description). 1 unit = blockSize / 2 pixels. var treasureZone = Math.floor(depthInMiles / 1000); // Which 1000-mile zone var rand = Math.random(); var spawnChance = 0.1; // 10% chance to spawn treasure if (rand > spawnChance) { return null; // No treasure } // Make treasure zones cycle every 5 zones (10000 miles) var cycledZone = treasureZone % 5; // Return specific treasure based on cycled zone switch (cycledZone) { case 0: // Bronze zone return 'bronze'; case 1: // Silver zone return 'silver'; case 2: // Gold zone return 'gold'; case 3: // Diamond zone return 'diamond'; case 4: // Crystal zone return 'crystal'; } } function generateRow(y_rowIndex_on_screen) { var row = []; // Calculate the actual depth of this row being generated var currentBlockDepth = depth + y_rowIndex_on_screen * blockSize; for (var x = 0; x < gridWidth; x++) { var block; if (x < mineableStartX || x >= mineableEndX) { block = new StoneBlock(); } else { block = new TerrainBlock(); var treasureTypeForBlock = getTreasureType(currentBlockDepth); if (treasureTypeForBlock) { block.setTreasure(treasureTypeForBlock, currentBlockDepth); // Pass currentBlockDepth } // Enemy Spawning Logic (only for new rows at the bottom, y_rowIndex_on_screen === gridHeight) var fiftyMileZoneIdForBlock = Math.floor(currentBlockDepth / FIFTY_MILE_DEPTH_PIXELS); if (treasureTypeForBlock && y_rowIndex_on_screen === gridHeight && Math.random() < ENEMY_SPAWN_CHANCE) { var depthInMiles = Math.floor(currentBlockDepth / (blockSize / 2)); var treasureZoneIndex = Math.floor(depthInMiles / 1000) % 5; var enemyTypeString = ZONE_TO_ENEMY_TYPE[treasureZoneIndex]; if (enemyTypeString) { // Check 1: Is the 50-mile slot for this specific block's depth and type empty? var enemiesInThisFiftyMileSlot = 0; for (var k_idx = 0; k_idx < enemies.length; k_idx++) { if (!enemies[k_idx].shouldBeDestroyed && enemies[k_idx].fiftyMileZoneId === fiftyMileZoneIdForBlock && enemies[k_idx].associatedZoneIndex === treasureZoneIndex) { enemiesInThisFiftyMileSlot++; } } if (enemiesInThisFiftyMileSlot === 0) { // Check 2: Is the overall cap for the treasure zone not reached? var enemiesInThisTreasureZone = 0; for (var k_check = 0; k_check < enemies.length; k_check++) { if (!enemies[k_check].shouldBeDestroyed && enemies[k_check].associatedZoneIndex === treasureZoneIndex) { enemiesInThisTreasureZone++; } } if (enemiesInThisTreasureZone < MAX_ENEMIES_PER_ZONE_ONSCREEN) { if (!row.enemySpawnData) { row.enemySpawnData = []; } row.enemySpawnData.push({ enemyTypeString: enemyTypeString, spawnX: x * blockSize + blockSize / 2, spawnY: y_rowIndex_on_screen * blockSize + blockSize / 2, // This Y is relative to screen top (0) treasureZoneIndex: treasureZoneIndex, fiftyMileZoneId: fiftyMileZoneIdForBlock }); } } } } } block.x = x * blockSize; block.y = y_rowIndex_on_screen * blockSize; // Position block based on its screen row index game.addChild(block); row.push(block); } // Create enemies after all terrain blocks are added to ensure they render on top if (row.enemySpawnData) { for (var i = 0; i < row.enemySpawnData.length; i++) { var spawnData = row.enemySpawnData[i]; var newEnemy = new Enemy(); game.addChild(newEnemy); // Add to scene after terrain blocks newEnemy.initEnemy(spawnData.enemyTypeString, spawnData.spawnX, spawnData.spawnY, spawnData.treasureZoneIndex, spawnData.fiftyMileZoneId); if (!newEnemy.shouldBeDestroyed) { enemies.push(newEnemy); } else { newEnemy.destroy(); } } delete row.enemySpawnData; // Clean up temporary data } return row; } function initializeTerrain() { terrain = []; for (var y = 0; y < gridHeight + 1; y++) { terrain.push(generateRow(y)); } } function completeScrollCleanup() { // The blocks in terrain[0] are the ones that moved off-screen and whose tweens just finished. var topRowToDestroy = terrain[0]; for (var x = 0; x < topRowToDestroy.length; x++) { var block = topRowToDestroy[x]; // Destroy treasure graphics if they exist if (block.treasureGraphics) { block.treasureGraphics.destroy(); } block.destroy(); // This removes the container and its children from the stage } terrain.shift(); // Now remove the row from our logical terrain array depth += blockSize; // Add new row at the bottom. generateRow expects the screen row index. For the new bottom row, this is gridHeight. terrain.push(generateRow(gridHeight)); depthText.setText('Depth: ' + Math.floor(depth / (blockSize / 2)) + 'm'); isScrolling = false; // Allow new scrolls } function scrollTerrain() { if (isScrolling) { return; // Don't start a new scroll if one is already in progress } isScrolling = true; var tweensCompleted = 0; var totalTweens = 0; // Calculate total number of blocks to be tweened for (var y_coord = 0; y_coord < terrain.length; y_coord++) { totalTweens += terrain[y_coord].length; } var itemsToScroll = []; // Add terrain blocks for (var r = 0; r < terrain.length; r++) { for (var c = 0; c < terrain[r].length; c++) { itemsToScroll.push(terrain[r][c]); } } // Add active enemies for (var i = 0; i < enemies.length; i++) { if (!enemies[i].shouldBeDestroyed) { itemsToScroll.push(enemies[i]); } } // Add active enemy shots for (var i = 0; i < enemyShots.length; i++) { if (!enemyShots[i].shouldBeDestroyed) { itemsToScroll.push(enemyShots[i]); } } totalTweens = itemsToScroll.length; if (totalTweens === 0) { isScrolling = false; // It's possible no terrain exists but other items do, but cleanup is tied to terrain. // If itemsToScroll is empty, nothing to do. // If only enemies/shots, they scroll, but no new terrain row generation. if (terrain.length === 0 || terrain[0].length === 0) { // If no terrain to manage, but scroll was initiated (e.g. by player action) // and there might be other things to scroll, depth might still conceptually increase. // However, completeScrollCleanup is the one that advances depth and adds rows. // This path implies no terrain related cleanup. } return; } // Animate each item var terrainBlocksWereScrolled = false; for (var i = 0; i < itemsToScroll.length; i++) { var itemToAnimate = itemsToScroll[i]; if (itemToAnimate instanceof TerrainBlock || itemToAnimate instanceof StoneBlock) { terrainBlocksWereScrolled = true; } tween(itemToAnimate, { y: itemToAnimate.y - blockSize }, { duration: scrollAnimationDuration, easing: tween.linear, onFinish: function onScrollItemFinish() { tweensCompleted++; if (tweensCompleted === totalTweens) { // All items done tweening if (terrainBlocksWereScrolled && terrain.length > 0 && terrain[0].length > 0) { completeScrollCleanup(); // Call cleanup if terrain was involved } else { // No terrain was scrolled, or terrain is now empty after scrolling. isScrolling = false; // If terrain was scrolled and became empty, still update depth. if (terrainBlocksWereScrolled && (terrain.length === 0 || terrain.length > 0 && terrain[0].length === 0)) { depth += blockSize; depthText.setText('Depth: ' + Math.floor(depth / (blockSize / 2)) + 'm'); } } } } }); } } function mineBlock(worldX, worldY, isPlayerAction) { // Default isPlayerAction to true if not specified for backward compatibility if (typeof isPlayerAction === 'undefined') { isPlayerAction = true; } var gridX = Math.floor(worldX / blockSize); var gridY = Math.floor(worldY / blockSize); // Corrected boundary check for gridY: should be against terrain.length if (gridX < 0 || gridX >= gridWidth || gridY < 0 || gridY >= terrain.length) { return; } // Check if terrain row exists before accessing if (!terrain[gridY]) { return; } var block = terrain[gridY][gridX]; if (!block || !block.visible) { return; } if (block.mine()) { // Play digging sound based on who performed the action if (isPlayerAction) { var playerDigSound = LK.getSound('Playerdiggingsound1'); if (playerDigSound) { // Defensive check playerDigSound.play(); } } else { // Enemy action successfully mined a block var enemyDigSound = LK.getSound('Enemydiggingsound1'); if (enemyDigSound) { // Defensive check enemyDigSound.play(); } } if (block.hasTreasure) { var treasureValue = 0; switch (block.treasureType) { case 'bronze': treasureValue = 10; break; case 'silver': treasureValue = 25; break; case 'gold': treasureValue = 50; break; case 'diamond': treasureValue = 100; break; case 'crystal': treasureValue = 150; break; } // Only update score and treasure counts for player actions if (isPlayerAction) { score += treasureValue; LK.setScore(score); scoreText.setText('Score: ' + score); treasureCounts[block.treasureType]++; treasureTexts[block.treasureType].setText(block.treasureType.charAt(0).toUpperCase() + block.treasureType.slice(1) + ': ' + treasureCounts[block.treasureType]); // Play treasure collected sound var treasureSound = LK.getSound('CollectedTreasuresound1'); if (treasureSound) { treasureSound.play(); } // Persist player's new high score to storage if (typeof storage !== 'undefined') { // Ensure currentPlayerUpitID and currentPlayerName are strings for consistent handling var currentPlayerUpitID = String(storage.playerUpitID || "YOUR_ID"); var currentPlayerName = String(storage.playerName || "You"); // Retrieve leaderboard arrays from storage. If they don't exist or aren't arrays, initialize as new empty arrays. // Using [].concat() creates a shallow copy, ensuring we're working with a mutable array. var storedLeaderboardIds = Array.isArray(storage.leaderboardIds) ? [].concat(storage.leaderboardIds) : []; var storedLeaderboardNames = Array.isArray(storage.leaderboardNames) ? [].concat(storage.leaderboardNames) : []; var storedLeaderboardScores = Array.isArray(storage.leaderboardScores) ? [].concat(storage.leaderboardScores) : []; var playerIndex = -1; for (var i = 0; i < storedLeaderboardIds.length; i++) { // Compare IDs as strings for consistency if (String(storedLeaderboardIds[i]) === currentPlayerUpitID) { playerIndex = i; break; } } if (playerIndex !== -1) { // Player exists in the stored leaderboard var currentStoredScore = parseInt(storedLeaderboardScores[playerIndex], 10); // Default to 0 if the stored score is not a valid number if (isNaN(currentStoredScore)) { currentStoredScore = 0; } // Update the stored score if the current game score (variable 'score') is higher if (score > currentStoredScore) { storedLeaderboardScores[playerIndex] = score; // Optionally, update the player's name if it might have changed in Upit // storedLeaderboardNames[playerIndex] = currentPlayerName; } } else { // Player is not in the stored leaderboard, so add them as a new entry storedLeaderboardIds.push(currentPlayerUpitID); storedLeaderboardNames.push(currentPlayerName); storedLeaderboardScores.push(score); // 'score' is the current game score } // Save the updated arrays back to storage. // The storage plugin will handle persisting these changes. storage.leaderboardIds = storedLeaderboardIds; storage.leaderboardNames = storedLeaderboardNames; storage.leaderboardScores = storedLeaderboardScores; } } } // Only trigger scrolling for player actions if (isPlayerAction && gridY > gridHeight / 2) { scrollTerrain(); } } } function handlePlayerDeath() { playerLives--; if (livesText) { livesText.setText('Lives: ' + playerLives); } LK.effects.flashScreen(0xff0000, 300); // Flash screen red for losing a life if (playerLives > 0) { // Respawn player if (player && !player.destroyed) { player.x = 1024; // Respawn X (initial position) player.y = 200; // Respawn Y (initial position) targetX = player.x; // Reset drag target to prevent moving back to death spot targetY = player.y; // Optional: Make enemies briefly re-evaluate targets for (var i = 0; i < enemies.length; i++) { if (enemies[i] && !enemies[i].shouldBeDestroyed) { enemies[i].isAttackingPlayer = false; // Force re-evaluation of player attack enemies[i].targetOreBlock = null; // Force find new ore target if not attacking } } } } else { LK.showGameOver(); } } // Create intro screen function createIntroScreen() { introContainer = new Container(); // Add intro background - since we're adding to GUI, we need different scaling approach var introBackground = introContainer.attachAsset('IntroBackground', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); // Since we're adding to GUI.center, we need to scale based on the actual GUI dimensions // The GUI system automatically scales, so we need to set the background to fit the screen // by making it large enough to fit within the viewport var assetWidth = introBackground.width; var assetHeight = introBackground.height; // Defensively check for valid, positive asset dimensions before calculating scale if (assetWidth > 0 && assetHeight > 0) { // For GUI elements, we want to scale the image to fit the entire screen while maintaining aspect ratio. // This means the entire image will be visible within the 2048x2732 viewport, potentially leaving empty space if aspect ratios differ. var scaleX = 2048 / assetWidth; var scaleY = 2732 / assetHeight; // Use the smaller scale to ensure the entire image fits within screen boundaries var scale = Math.min(scaleX, scaleY); introBackground.scale.x = scale; introBackground.scale.y = scale; // Position is already centered: introContainer is in LK.gui.center, // and introBackground is at (0,0) within introContainer with anchor 0.5,0.5. } else { // If asset dimensions are invalid (e.g., zero or negative), // set scale to zero to effectively hide it and log a warning. introBackground.scale.x = 0; introBackground.scale.y = 0; console.log("Warning: IntroBackground asset has invalid dimensions (width: " + assetWidth + ", height: " + assetHeight + "). Cannot scale appropriately."); } // Add start button var startButton = introContainer.attachAsset('Startbutton', { anchorX: 0.5, anchorY: 0.5, x: -200, y: 800, // Lowered by 200px (was 200) scaleX: 4, scaleY: 1.5 }); // Make button interactive startButton.interactive = true; // Add button press handler startButton.down = function () { var startSound = LK.getSound('Startsound1'); if (startSound) { startSound.play(); } startGame(); }; // Add HighScore button to the right of start button var highScoreButton = introContainer.attachAsset('HighScoreButton', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 800, scaleX: 4, scaleY: 1.5 }); // Make high score button interactive highScoreButton.interactive = true; // Add high score button press handler highScoreButton.down = function () { console.log("High score button pressed - displaying custom ScoreBackground."); // Prevent opening multiple score panels if (activeScoreBackground && activeScoreBackground.parent) { return; } var scoreScreen = new ScoreBackground(); scoreScreen.init(); // Initialize the panel's contents LK.gui.center.addChild(scoreScreen); // Add to the center of the GUI layer activeScoreBackground = scoreScreen; // Store a reference }; // Add continuous blinking animation to the start button function startButtonBlinkLoop() { tween(startButton, { alpha: 0.5 }, { duration: 800, easing: tween.linear, onFinish: function onFinish() { tween(startButton, { alpha: 1.0 }, { duration: 800, easing: tween.linear, onFinish: startButtonBlinkLoop // Loop by calling itself }); } }); } startButtonBlinkLoop(); // Start the blinking loop for the start button // Add continuous blinking animation to the high score button (with offset timing) function highScoreButtonBlinkLoop() { tween(highScoreButton, { alpha: 0.5 }, { duration: 800, easing: tween.linear, onFinish: function onFinish() { tween(highScoreButton, { alpha: 1.0 }, { duration: 800, easing: tween.linear, onFinish: highScoreButtonBlinkLoop // Loop by calling itself }); } }); } // Start the blinking loop for the high score button with an initial delay LK.setTimeout(highScoreButtonBlinkLoop, 400); // Add to GUI center instead of game LK.gui.center.addChild(introContainer); // Play intro music LK.playMusic('Intromusic1'); } function startGame() { if (gameStarted) { return; } // Set gameStarted immediately to prevent multiple calls gameStarted = true; // Remove intro screen if (introContainer) { introContainer.destroy(); introContainer = null; } // Show game UI elements scoreText.visible = true; depthText.visible = true; livesText.visible = true; // Make lives text visible treasureContainer.visible = true; // Create shop container var shopContainer = new Container(); // Add shop background centered on screen var shopBackground = shopContainer.attachAsset('Shopbackground', { anchorX: 0.5, anchorY: 0.5, x: 0, // Center of screen (0 for GUI center) y: 0 // Center of screen (0 for GUI center) }); // Add shop sign to shop container (will be moved to GUI when shop closes) var shopSign = shopContainer.attachAsset('ShopSign', { anchorX: 0.5, anchorY: 0.5, x: shopBackground.x + shopBackground.width / 2 - 100, // Position relative to shop background y: shopBackground.y - shopBackground.height / 2 // Align with top edge of shop background }); // Make shop sign interactive shopSign.interactive = true; // Add click handler to shop sign shopSign.down = function () { // Move shop sign back to shop container if it's in GUI if (shopSign.parent === LK.gui.topRight) { LK.gui.topRight.removeChild(shopSign); shopContainer.addChild(shopSign); // Reset shop sign position relative to shop background shopSign.x = shopBackground.x + shopBackground.width / 2 - 100; shopSign.y = shopBackground.y - shopBackground.height / 2; } // Show shop container when shop sign is clicked shopContainer.visible = true; // Hide shop sign when shop is open shopSign.visible = false; // Pause the game gamePaused = true; }; // Add close button at top right corner of shop background var shopCloseButton = shopContainer.attachAsset('CloseButtonAsset', { anchorX: 0.5, anchorY: 0.5, x: shopBackground.x + shopBackground.width / 2 - 50, // 50px from right edge of centered background y: shopBackground.y - shopBackground.height / 2 + 50 // 50px from top edge of centered background }); // Make close button interactive shopCloseButton.interactive = true; // Add "X" text to close button var shopCloseText = new Text2('X', { size: 70, fill: 0xFFFFFF // White "X" }); shopCloseText.anchor.set(0.5, 0.5); shopCloseText.x = shopCloseButton.x; shopCloseText.y = shopCloseButton.y; shopContainer.addChild(shopCloseText); // Initially hide shop container and move shop sign to GUI shopContainer.visible = false; // Move shop sign to GUI top right shopContainer.removeChild(shopSign); LK.gui.topRight.addChild(shopSign); shopSign.visible = true; shopSign.x = -100; // Offset from right edge shopSign.y = 180; // Below treasure texts // Close button functionality shopCloseButton.down = function () { shopContainer.visible = false; // Move shop sign to GUI and show it if (shopSign.parent === shopContainer) { shopContainer.removeChild(shopSign); LK.gui.topRight.addChild(shopSign); shopSign.visible = true; shopSign.x = -100; // Offset from right edge shopSign.y = 180; // Below treasure texts } // Unpause the game gamePaused = false; }; // Add shop container to GUI LK.gui.center.addChild(shopContainer); // Reset lives for new game playerLives = INITIAL_PLAYER_LIVES; livesText.setText('Lives: ' + playerLives); // Initialize game initializeTerrain(); player = game.addChild(new Player()); player.x = 1024; player.y = 200; // Stop intro music and play game music LK.stopMusic(); LK.playMusic('Gamemusic1'); } // Create intro screen instead of starting game immediately createIntroScreen(); game.down = function (x, y, obj) { if (!gameStarted) { return; } isDragging = true; // Clamp target position to game boundaries targetX = Math.max(0, Math.min(x, 2048)); targetY = Math.max(0, Math.min(y, 2732)); }; game.move = function (x, y, obj) { if (!gameStarted) { return; } // Clamp target position to game boundaries targetX = Math.max(0, Math.min(x, 2048)); targetY = Math.max(0, Math.min(y, 2732)); }; game.up = function (x, y, obj) { if (!gameStarted) { return; } isDragging = false; }; game.update = function () { if (!gameStarted) { return; } if (gamePaused) { return; // Skip all updates when game is paused } if (player && targetX !== null && targetY !== null) { movePlayerTowardsTarget(); } // Check for enemy spawn every 50 miles of depth progression at the top of the screen if (depth - lastFiftyMileSpawnCheckDepth >= FIFTY_MILE_DEPTH_PIXELS) { lastFiftyMileSpawnCheckDepth = Math.floor(depth / FIFTY_MILE_DEPTH_PIXELS) * FIFTY_MILE_DEPTH_PIXELS; // Determine treasure zone based on current depth (top of screen) // The enemy will spawn at the bottom, so its actual depth will be greater. var depthInMilesForTreasureZone = Math.floor(depth / (blockSize / 2)); var currentTreasureZone = Math.floor(depthInMilesForTreasureZone / 1000) % 5; var enemyTypeString = ZONE_TO_ENEMY_TYPE[currentTreasureZone]; if (enemyTypeString) { // Calculate the 50-mile zone ID for an enemy spawning at the bottom of the screen var effectiveSpawnDepthForUpdate = depth + (2732 - blockSize); // Approximate actual depth of new enemy var fiftyMileZoneIdForNewEnemy = Math.floor(effectiveSpawnDepthForUpdate / FIFTY_MILE_DEPTH_PIXELS); // Check 1: Is the target 50-mile slot (for bottom of screen) for this enemy type empty? var enemiesInTargetFiftyMileSlot = 0; for (var k_update_check_slot = 0; k_update_check_slot < enemies.length; k_update_check_slot++) { var existingEnemy = enemies[k_update_check_slot]; if (!existingEnemy.shouldBeDestroyed && existingEnemy.fiftyMileZoneId === fiftyMileZoneIdForNewEnemy && existingEnemy.associatedZoneIndex === currentTreasureZone) { enemiesInTargetFiftyMileSlot++; } } if (enemiesInTargetFiftyMileSlot === 0) { // Check 2: Is the overall cap for the treasure zone not reached? var enemiesInThisTreasureZoneOverall = 0; for (var k_update_check_cap = 0; k_update_check_cap < enemies.length; k_update_check_cap++) { if (!enemies[k_update_check_cap].shouldBeDestroyed && enemies[k_update_check_cap].associatedZoneIndex === currentTreasureZone) { enemiesInThisTreasureZoneOverall++; } } if (enemiesInThisTreasureZoneOverall < MAX_ENEMIES_PER_ZONE_ONSCREEN) { // Spawn ONE enemy var newEnemy = new Enemy(); game.addChild(newEnemy); var spawnX = Math.random() * (mineableEndX - mineableStartX - 2) * blockSize + (mineableStartX + 1) * blockSize; var spawnY = 2732 - blockSize; // Spawn from bottom of screen newEnemy.initEnemy(enemyTypeString, spawnX, spawnY, currentTreasureZone, fiftyMileZoneIdForNewEnemy); if (!newEnemy.shouldBeDestroyed) { enemies.push(newEnemy); } else { newEnemy.destroy(); } } } } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.shouldBeDestroyed) { enemy.destroy(); enemies.splice(i, 1); } else { enemy.update(); // Additional check: if enemy graphic exists and it scrolled too high (e.g. its zone is long gone) if (!isScrolling && enemy.graphics && enemy.y < -(enemy.graphics.height * enemy.graphics.scale.y * 2)) { enemy.shouldBeDestroyed = true; } } } // Update enemy shots for (var i = enemyShots.length - 1; i >= 0; i--) { var shot = enemyShots[i]; if (shot.shouldBeDestroyed) { shot.destroy(); enemyShots.splice(i, 1); } else { shot.update(); } } };
===================================================================
--- original.js
+++ change.js
@@ -129,9 +129,14 @@
// Calculate the per-frame scroll compensation
var scrollCompensation = blockSize / (scrollAnimationDuration / (1000 / 60)); // pixels per frame
moveY += scrollCompensation; // Add downward movement to compensate for upward scroll
}
- self.x += moveX;
+ // Calculate mineable area boundaries
+ var leftBoundary = mineableStartX * blockSize + blockSize / 2;
+ var rightBoundary = (mineableEndX - 1) * blockSize + blockSize / 2;
+ // Apply movement but clamp to mineable area
+ var newX = self.x + moveX;
+ self.x = Math.max(leftBoundary, Math.min(newX, rightBoundary));
self.y += moveY;
mineBlock(self.x, self.y, false); // Enemy action, dig while moving
}
} else {
@@ -188,9 +193,14 @@
if (isScrolling) {
var scrollCompensation = blockSize / (scrollAnimationDuration / (1000 / 60));
moveY += scrollCompensation;
}
- self.x += moveX;
+ // Calculate mineable area boundaries
+ var leftBoundary = mineableStartX * blockSize + blockSize / 2;
+ var rightBoundary = (mineableEndX - 1) * blockSize + blockSize / 2;
+ // Apply movement but clamp to mineable area
+ var newX = self.x + moveX;
+ self.x = Math.max(leftBoundary, Math.min(newX, rightBoundary));
self.y += moveY;
mineBlock(self.x, self.y, false); // Enemy action, dig while moving
}
}
@@ -526,10 +536,10 @@
/****
* Game Code
****/
-// A nice dark blue/grey
// A flat red for the button
+// A nice dark blue/grey
game.setBackgroundColor(0x000000);
var blockSize = 200;
var gridWidth = Math.floor(2048 / blockSize);
var gridHeight = Math.floor(2732 / blockSize);
@@ -597,8 +607,13 @@
function movePlayerTowardsTarget() {
if (targetX === null || targetY === null || !player) {
return;
}
+ // Calculate mineable area boundaries
+ var leftBoundary = mineableStartX * blockSize + blockSize / 2;
+ var rightBoundary = (mineableEndX - 1) * blockSize + blockSize / 2;
+ // Clamp target position to stay within mineable area
+ targetX = Math.max(leftBoundary, Math.min(targetX, rightBoundary));
// Move player towards target position, restricting to X or Y axis per update
var dx = targetX - player.x;
var dy = targetY - player.y;
if (dx === 0 && dy === 0) {
@@ -606,9 +621,11 @@
// The mineBlock() call after this block will handle mining at the current position.
} else if (Math.abs(dx) > Math.abs(dy)) {
// Prioritize horizontal movement
if (Math.abs(dx) > playerSpeed) {
- player.x += Math.sign(dx) * playerSpeed;
+ var newX = player.x + Math.sign(dx) * playerSpeed;
+ // Clamp player position to stay within mineable area
+ player.x = Math.max(leftBoundary, Math.min(newX, rightBoundary));
} else {
player.x = targetX; // Snap to target X
}
} else {
Same ninja character, with pack of all colors
Same ninja character, with pack of colors silver,gold,diamond,pink crystal
Same ninja character, with silver color
Same ninja character, with gold color
Same ninja character, with diamond color
Same ninja character, with crystal pink color
Pack of crystals, pink color In-Game asset. 2d. High contrast. No shadows
same image but add beside the character +1 with green color.
Same image of ninja with dig machine but with different colors. HD colors. yellow
Same image of ninja with dig machine but with different colors. HD colors. red
Same image of ninja with dig machine but with different colors. HD colors. blue
Same image of ninja with dig machine but with different colors. HD colors. black ninja
Same image of ninja with dig machine but with different colors. HD colors. green
Playerdiggingsound1
Sound effect
Enemydiggingsound1
Sound effect
Gamemusic1
Music
CollectedTreasuresound1
Sound effect
Startsound1
Sound effect
Intromusic1
Music
PlayerShotSound
Sound effect
EnemyShotshound1
Sound effect
Lifesound1
Sound effect
enemyshotsound1
Sound effect
playershotsound1
Sound effect