Code edit (1 edits merged)
Please save this source code
User prompt
Something is not working with the movement. I click / tap on a cell and it's not walking to it. Please check and fix.
User prompt
Ok the movement seems borked. What I need you to do is, if I click / tap on a free cell adjacent cell, the player should move to it. Ignore the movement if it's not adjacent or not free (there is a wall, a brick or a bomb)
Code edit (1 edits merged)
Please save this source code
User prompt
Grid Blast Mania
Initial prompt
Bomberman, the retro arcarde
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Represents a placed bomb var Bomb = Container.expand(function (col, row) { var self = Container.call(this); var graphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); self.col = col; self.row = row; self.x = getWorldCoords(col, row).x; self.y = getWorldCoords(col, row).y; self.isBomb = true; // Identifier // Start the explosion timer var explosionTimer = LK.setTimeout(function () { explodeBomb(self); }, BOMB_TIMER); // Public method to trigger explosion early (chain reaction) self.triggerExplosion = function () { LK.clearTimeout(explosionTimer); explodeBomb(self); }; // Override destroy to clear the timer var baseDestroy = self.destroy; self.destroy = function () { LK.clearTimeout(explosionTimer); // Make sure the grid cell is marked empty when bomb is destroyed (e.g., by explosion) if (grid[self.col] && grid[self.col][self.row] === CELL_TYPE.BOMB) { setCell(self.col, self.row, CELL_TYPE.EMPTY); } // Remove from active bombs list in game scope var index = activeBombs.indexOf(self); if (index !== -1) { activeBombs.splice(index, 1); } if (baseDestroy) { baseDestroy.call(self); } // Call original destroy if exists }; return self; }); // Represents a single explosion particle/cell var Explosion = Container.expand(function (col, row) { var self = Container.call(this); var graphics = self.attachAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); self.col = col; self.row = row; self.x = getWorldCoords(col, row).x; self.y = getWorldCoords(col, row).y; self.isExplosion = true; // Identifier for collision checks // Fade out and destroy after duration var timer = LK.setTimeout(function () { self.destroy(); // Destroy handled by Game update loop removal }, EXPLOSION_DURATION); // Override destroy to clear the timer var baseDestroy = self.destroy; self.destroy = function () { LK.clearTimeout(timer); if (baseDestroy) { baseDestroy.call(self); } // Call original destroy if exists }; return self; }); // Represents the player character var Player = Container.expand(function (startCol, startRow) { var self = Container.call(this); var graphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.col = startCol; self.row = startRow; self.isPlayer = true; // Identifier // Snap to initial grid position var initialPos = getWorldCoords(self.col, self.row); self.x = initialPos.x; self.y = initialPos.y; self.isMoving = false; // Flag to track movement animation state // Public method to move the player self.moveTo = function (newCol, newRow) { // The walkability check is now done in game.down before calling moveTo self.isMoving = true; // Set flag: player is now moving self.col = newCol; self.row = newRow; // Use tween for smooth movement var targetPos = getWorldCoords(newCol, newRow); tween(self, { x: targetPos.x, y: targetPos.y }, { duration: 100, // Keep the movement snappy easing: tween.linear, onComplete: function onComplete() { self.isMoving = false; // Clear flag: movement animation finished } }); return true; // Move initiated successfully }; // Public method to place a bomb self.placeBomb = function () { if (activeBombs.length < MAX_BOMBS && getCellType(self.col, self.row) === CELL_TYPE.EMPTY) { LK.getSound('place_bomb').play(); var newBomb = new Bomb(self.col, self.row); game.addChild(newBomb); activeBombs.push(newBomb); setCell(self.col, self.row, CELL_TYPE.BOMB); // Mark cell as containing a bomb } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xaaaaaa // Light gray background }); /**** * Game Code ****/ // Let's define the shapes we'll use: // Engine will implicitly create assets based on usage below. // --- Constants --- var GRID_COLS = 15; // Ensure odd number for wall placement var GRID_ROWS = 21; // Ensure odd number for wall placement var CELL_SIZE = 128; var GRID_WIDTH = GRID_COLS * CELL_SIZE; var GRID_HEIGHT = GRID_ROWS * CELL_SIZE; var GRID_OFFSET_X = (2048 - GRID_WIDTH) / 2; var GRID_OFFSET_Y = (2732 - GRID_HEIGHT) / 2 + 50; // Adjust Y offset slightly down var CELL_TYPE = { EMPTY: 0, DESTRUCTIBLE: 1, INDESTRUCTIBLE: 2, BOMB: 3, // Cell temporarily occupied by a bomb EXPLOSION: 4 // Cell temporarily occupied by explosion }; var BOMB_TIMER = 2500; // Milliseconds before bomb explodes var EXPLOSION_DURATION = 400; // Milliseconds explosion visuals last var BOMB_RANGE = 2; // Number of cells explosion reaches in each direction var MAX_BOMBS = 1; // Max bombs player can have active at once var BRICK_DENSITY = 0.6; // Probability of a potential spot having a brick // --- Game State Variables --- var grid = []; // 2D array holding CELL_TYPE for each cell var gridSprites = []; // 2D array holding visual sprites for walls/bricks var player; var activeBombs = []; var activeExplosions = []; // Array to hold active Explosion objects var score = 0; var scoreTxt; var isGameOver = false; // --- Helper Functions --- // Convert screen coordinates to grid cell indices function getGridCoords(screenX, screenY) { var col = Math.floor((screenX - GRID_OFFSET_X) / CELL_SIZE); var row = Math.floor((screenY - GRID_OFFSET_Y) / CELL_SIZE); return { col: col, row: row }; } // Convert grid cell indices to world coordinates (center of the cell) function getWorldCoords(col, row) { var x = GRID_OFFSET_X + col * CELL_SIZE + CELL_SIZE / 2; var y = GRID_OFFSET_Y + row * CELL_SIZE + CELL_SIZE / 2; return { x: x, y: y }; } // Get the type of a cell, handling out-of-bounds function getCellType(col, row) { if (col < 0 || col >= GRID_COLS || row < 0 || row >= GRID_ROWS) { return CELL_TYPE.INDESTRUCTIBLE; // Treat out of bounds as walls } return grid[col][row]; } // Set the type of a cell function setCell(col, row, type) { if (col >= 0 && col < GRID_COLS && row >= 0 && row < GRID_ROWS) { grid[col][row] = type; } } // Check if a cell is valid and empty for movement function isWalkable(col, row) { var cellType = getCellType(col, row); // A cell is walkable only if it's currently empty. Player cannot initiate move into other tiles. return cellType === CELL_TYPE.EMPTY; } // --- Grid Generation --- function generateGrid() { grid = []; gridSprites = []; // Clear previous sprites if any game.children.forEach(function (child) { if (child.isGridElement) { // Add a flag to identify grid sprites child.destroy(); } }); game.removeChildren(); // Clear all children before regenerating // Add floor tiles everywhere first for (var c = 0; c < GRID_COLS; c++) { for (var r = 0; r < GRID_ROWS; r++) { var floorTile = LK.getAsset('floor', { anchorX: 0.0, anchorY: 0.0 }); floorTile.x = GRID_OFFSET_X + c * CELL_SIZE; floorTile.y = GRID_OFFSET_Y + r * CELL_SIZE; floorTile.isGridElement = true; game.addChild(floorTile); } } for (var c = 0; c < GRID_COLS; c++) { grid[c] = []; gridSprites[c] = []; for (var r = 0; r < GRID_ROWS; r++) { var cellType; var sprite = null; // Place indestructible walls around border and in a checkerboard pattern inside if (c === 0 || c === GRID_COLS - 1 || r === 0 || r === GRID_ROWS - 1 || c % 2 === 0 && r % 2 === 0) { cellType = CELL_TYPE.INDESTRUCTIBLE; sprite = LK.getAsset('wall', { anchorX: 0.0, anchorY: 0.0 }); } else { // Place destructible bricks randomly, ensuring player start area is clear if (c <= 2 && r <= 2 || c >= GRID_COLS - 3 && r >= GRID_ROWS - 3) { // Clear corners for potential spawns cellType = CELL_TYPE.EMPTY; } else if (Math.random() < BRICK_DENSITY) { cellType = CELL_TYPE.DESTRUCTIBLE; sprite = LK.getAsset('brick', { anchorX: 0.0, anchorY: 0.0 }); } else { cellType = CELL_TYPE.EMPTY; } } grid[c][r] = cellType; if (sprite) { sprite.x = GRID_OFFSET_X + c * CELL_SIZE; sprite.y = GRID_OFFSET_Y + r * CELL_SIZE; sprite.isGridElement = true; // Mark as part of the grid visuals game.addChild(sprite); gridSprites[c][r] = sprite; } else { gridSprites[c][r] = null; } } } // Ensure player start is clear setCell(1, 1, CELL_TYPE.EMPTY); setCell(1, 2, CELL_TYPE.EMPTY); setCell(2, 1, CELL_TYPE.EMPTY); if (gridSprites[1] && gridSprites[1][1]) { var _gridSprites$1$; (_gridSprites$1$ = gridSprites[1][1]) === null || _gridSprites$1$ === void 0 || _gridSprites$1$.destroy(); gridSprites[1][1] = null; } if (gridSprites[1] && gridSprites[1][2]) { var _gridSprites$1$2; (_gridSprites$1$2 = gridSprites[1][2]) === null || _gridSprites$1$2 === void 0 || _gridSprites$1$2.destroy(); gridSprites[1][2] = null; } if (gridSprites[2] && gridSprites[2][1]) { var _gridSprites$2$; (_gridSprites$2$ = gridSprites[2][1]) === null || _gridSprites$2$ === void 0 || _gridSprites$2$.destroy(); gridSprites[2][1] = null; } } // --- Explosion Handling --- function createExplosion(col, row) { if (col < 0 || col >= GRID_COLS || row < 0 || row >= GRID_ROWS) { return false; } // Out of bounds var cellType = getCellType(col, row); if (cellType === CELL_TYPE.INDESTRUCTIBLE) { return false; // Explosion stops at indestructible walls } // Create visual explosion part var explosionPart = new Explosion(col, row); game.addChild(explosionPart); activeExplosions.push(explosionPart); setCell(col, row, CELL_TYPE.EXPLOSION); // Mark cell as exploding // Check for chain reactions or destroying bricks if (cellType === CELL_TYPE.DESTRUCTIBLE) { var brickSprite = gridSprites[col][row]; if (brickSprite) { brickSprite.destroy(); gridSprites[col][row] = null; } setCell(col, row, CELL_TYPE.EXPLOSION); // Becomes explosion temporarily LK.getSound('destroy_brick').play(); LK.setScore(LK.getScore() + 10); // Award score for destroying brick scoreTxt.setText(LK.getScore()); return false; // Explosion stops after destroying a brick } else if (cellType === CELL_TYPE.BOMB) { // Find the bomb object at this location and trigger it var bombToTrigger = null; for (var i = 0; i < activeBombs.length; i++) { if (activeBombs[i].col === col && activeBombs[i].row === row) { bombToTrigger = activeBombs[i]; break; } } if (bombToTrigger && !bombToTrigger.isExploding) { // Prevent infinite loops if already triggered bombToTrigger.isExploding = true; // Mark as exploding to avoid re-triggering bombToTrigger.triggerExplosion(); } return false; // Chain reaction handles further explosion; stop this particular ray } return true; // Explosion continues } function explodeBomb(bomb) { if (!bomb || bomb.destroyed) { return; } // Bomb might already be destroyed by chain reaction LK.getSound('explosion_sound').play(); var centerCol = bomb.col; var centerRow = bomb.row; // Remove bomb object itself first to prevent chain reaction with self bomb.destroy(); // This also removes from activeBombs and sets cell to EMPTY // Create explosion at the center createExplosion(centerCol, centerRow); // Create explosions outwards in four directions var directions = [[0, 1], [0, -1], [1, 0], [-1, 0]]; // Down, Up, Right, Left for (var i = 0; i < directions.length; i++) { var dx = directions[i][0]; var dy = directions[i][1]; for (var r = 1; r <= BOMB_RANGE; r++) { var currentC = centerCol + dx * r; var currentR = centerRow + dy * r; if (!createExplosion(currentC, currentR)) { break; // Stop this direction if explosion is blocked } } } } // --- Game Initialization --- function initGame() { isGameOver = false; score = 0; LK.setScore(0); activeBombs = []; activeExplosions = []; generateGrid(); // Create Player player = new Player(1, 1); // Start at top-left clear area game.addChild(player); // Score Display scoreTxt = new Text2('0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Add score to top center UI // Bomb Button var bombButton = LK.getAsset('bomb_button', { anchorX: 0.5, anchorY: 0.5 }); bombButton.down = function () { if (player && !isGameOver) { player.placeBomb(); } }; // Place button carefully, avoiding bottom corners if possible, maybe bottom center? // LK.gui.bottom.addChild(bombButton); // Center bottom // Or bottom right, ensuring it's not too close to the edge bombButton.x = -150; // Offset from right edge bombButton.y = -150; // Offset from bottom edge LK.gui.bottomRight.addChild(bombButton); } // --- Event Handlers --- // Handle player movement by tapping adjacent walkable cells game.down = function (x, y, obj) { // Check if player exists, game isn't over, AND player is not already moving if (isGameOver || !player || player.isMoving) { return; } // Ignore taps if game over, player doesn't exist, or player is currently moving var gameCoords = game.toLocal({ x: x, y: y }); // Convert event coords to game coords console.log(gameCoords); var tapped = getGridCoords(gameCoords.x, gameCoords.y); var playerCell = { col: player.col, row: player.row }; // Check if the tapped cell is adjacent (not diagonal) var dx = tapped.col - playerCell.col; var dy = tapped.row - playerCell.row; if (Math.abs(dx) + Math.abs(dy) === 1) { // Is adjacent? // Check if the target cell is walkable (empty) before moving if (isWalkable(tapped.col, tapped.row)) { player.moveTo(tapped.col, tapped.row); } } }; // --- Game Update Loop --- game.update = function () { if (isGameOver) { return; } // Check for player collision with explosions var playerCol = player.col; var playerRow = player.row; if (getCellType(playerCol, playerRow) === CELL_TYPE.EXPLOSION) { LK.getSound('player_die').play(); LK.effects.flashObject(player, 0xff0000, 300); // Flash player red isGameOver = true; LK.setTimeout(function () { LK.showGameOver(); // Show game over screen after a short delay }, 500); return; // Stop further updates after game over } // Clean up finished explosions and reset cell types for (var i = activeExplosions.length - 1; i >= 0; i--) { var explosion = activeExplosions[i]; // Check if the explosion sprite itself has been destroyed (by its internal timer) // Accessing internal properties like `_destroyed` is generally discouraged, // but necessary if the destroy mechanism is purely timer-based without explicit flags. // A better approach would be for Explosion class to set a 'finished' flag. // Let's assume Explosion.destroy correctly removes it from the stage. // We need a reliable way to know when to remove from activeExplosions and reset the grid cell. // We'll check if it's still parented to the game stage as a proxy. if (!explosion.parent) { // If it's been removed from the stage // Reset the grid cell only if it's still marked as EXPLOSION if (getCellType(explosion.col, explosion.row) === CELL_TYPE.EXPLOSION) { setCell(explosion.col, explosion.row, CELL_TYPE.EMPTY); } activeExplosions.splice(i, 1); // Remove from active list } } // Optional: Add win condition check (e.g., all bricks destroyed) // let bricksRemaining = false; // for (var c = 0; c < GRID_COLS; c++) { // for (var r = 0; r < GRID_ROWS; r++) { // if (getCellType(c, r) === CELL_TYPE.DESTRUCTIBLE) { // bricksRemaining = true; // break; // } // } // if (bricksRemaining) break; // } // if (!bricksRemaining) { // LK.showYouWin(); // } }; // --- Start the Game --- initGame();
===================================================================
--- original.js
+++ change.js
@@ -400,8 +400,9 @@
var gameCoords = game.toLocal({
x: x,
y: y
}); // Convert event coords to game coords
+ console.log(gameCoords);
var tapped = getGridCoords(gameCoords.x, gameCoords.y);
var playerCell = {
col: player.col,
row: player.row
concrete floor tile, retro, pixel style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
bomb, pixel style, retro. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a demonic strawberry, pixel style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a demonic cherry, pixel style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a demonic kiwi, pixel style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A human character, player from an arcade retro game, looking like in the 80s 90s, hair, male, looking right, pixel style. In-Game asset. 2d. High contrast. No shadows
fire texture pixel style retro square. In-Game asset. 2d. High contrast. No shadows
powerup icon for an additional charge of a bomb, retro arcade game. In-Game asset. 2d. High contrast. No shadows
powerup icon for an additional range of the bomb explosion you can throw, retro arcade game. In-Game asset. 2d. High contrast. No shadows