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; // Public method to move the player self.moveTo = function (newCol, newRow) { if (isWalkable(newCol, newRow)) { 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, easing: tween.linear }); return true; // Move successful } return false; // Move blocked }; // 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); return cellType === CELL_TYPE.EMPTY || cellType === CELL_TYPE.EXPLOSION; // Can walk into empty or explosion (though latter means death) } // --- 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) { if (isGameOver || !player || player.isMoving) { return; } // Ignore taps if game over or player doesn't exist/is moving var gameCoords = game.toLocal({ x: x, y: y }); // Convert event coords to game coords var tapped = getGridCoords(gameCoords.x, gameCoords.y); var playerCell = { col: player.col, row: player.row }; // Check if the tapped cell is adjacent (not diagonal) and walkable var dx = tapped.col - playerCell.col; var dy = tapped.row - playerCell.row; if (Math.abs(dx) + Math.abs(dy) === 1) { // Is adjacent? 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
@@ -1,6 +1,463 @@
-/****
+/****
+* 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;
+ // Public method to move the player
+ self.moveTo = function (newCol, newRow) {
+ if (isWalkable(newCol, newRow)) {
+ 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,
+ easing: tween.linear
+ });
+ return true; // Move successful
+ }
+ return false; // Move blocked
+ };
+ // 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: 0x000000
-});
\ No newline at end of file
+ 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);
+ return cellType === CELL_TYPE.EMPTY || cellType === CELL_TYPE.EXPLOSION; // Can walk into empty or explosion (though latter means death)
+}
+// --- 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) {
+ if (isGameOver || !player || player.isMoving) {
+ return;
+ } // Ignore taps if game over or player doesn't exist/is moving
+ var gameCoords = game.toLocal({
+ x: x,
+ y: y
+ }); // Convert event coords to game coords
+ var tapped = getGridCoords(gameCoords.x, gameCoords.y);
+ var playerCell = {
+ col: player.col,
+ row: player.row
+ };
+ // Check if the tapped cell is adjacent (not diagonal) and walkable
+ var dx = tapped.col - playerCell.col;
+ var dy = tapped.row - playerCell.row;
+ if (Math.abs(dx) + Math.abs(dy) === 1) {
+ // Is adjacent?
+ 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();
\ No newline at end of file
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