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
explosion_sound
Sound effect
background
Music
backgroundMusic
Music
destroy_brick
Sound effect
player_die
Sound effect
powerup_pickup
Sound effect
voices
Sound effect
voices1
Sound effect
voices2
Sound effect
voices3
Sound effect
placeBomb
Sound effect
tinyMonsterDie
Sound effect