User prompt
Make infinite levels!
User prompt
Pick the random maze after you complete the first level.
User prompt
When you click start level 2, puts you on to new level.
User prompt
Make a second level, when you complete the first level and reach the cell, it has an option to go to level 2, then let me make the level 2.
User prompt
Makes the player faster.
User prompt
DECREASE PLAYER SPEED
User prompt
some reason it moves me in different directions the ones that i don't want to go in so wherever you click make it go in the exact direction that you click
User prompt
Gwnewch y chwaraewr yn fwy cyflym.
User prompt
Make less enemies.
User prompt
Make enemies slower.
User prompt
Makes the maze a little bit easier.
User prompt
Make the doors visible.
User prompt
The maze have doors and when you click on the doors they open up.
User prompt
Add all the monsters in, like the skeleton, the zombie skeleton, all them.
User prompt
Make the monsters move.
User prompt
Please fix the bug: 'ReferenceError: charNode is not defined' in or related to this line: 'var dx = targetX - charNode.x;' Line Number: 509
User prompt
Remove that code.
User prompt
Switch skeleton class with Eggsy.
User prompt
Switch skeleton class with player.
User prompt
Switch the player with the skeleton class.
User prompt
Add a main player.
User prompt
Remove the code about the golden zombie.
User prompt
Get rid of the code about the zombie!
User prompt
Six bug L172
User prompt
Fix this bug that's happening.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Monster class for AI movement and attack
var Monster = Container.expand(function () {
var self = Container.call(this);
self.type = null;
self.col = 0;
self.row = 0;
self.targetCol = 0;
self.targetRow = 0;
self.lastCol = 0;
self.lastRow = 0;
self.moveSpeed = 4; // px per frame, slower than before
self.path = null;
self.pathStepIndex = 0;
self.isMoving = false;
self.attackCooldown = 0;
self.attackDelay = 60; // frames between attacks
self.asset = null;
// Set up monster asset
self.init = function (type, col, row) {
self.type = type;
self.col = col;
self.row = row;
self.targetCol = col;
self.targetRow = row;
var assetId = type;
self.asset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
width: MAZE_CELL_SIZE * 0.8,
height: MAZE_CELL_SIZE * 0.8
});
self.x = col * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
self.y = row * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
};
// Find path to player using BFS
self.findPathToPlayer = function (playerCol, playerRow) {
// Defensive: check bounds
if (playerCol < 0 || playerCol >= MAZE_COLS || playerRow < 0 || playerRow >= MAZE_ROWS) return null;
if (maze[playerRow][playerCol] === 1) return null;
var queue = [];
var visited = [];
for (var y = 0; y < MAZE_ROWS; y++) {
visited[y] = [];
for (var x = 0; x < MAZE_COLS; x++) {
visited[y][x] = false;
}
}
var prev = [];
for (var y = 0; y < MAZE_ROWS; y++) {
prev[y] = [];
for (var x = 0; x < MAZE_COLS; x++) {
prev[y][x] = null;
}
}
queue.push({
col: self.col,
row: self.row
});
visited[self.row][self.col] = true;
var found = false;
while (queue.length > 0) {
var curr = queue.shift();
if (curr.col === playerCol && curr.row === playerRow) {
found = true;
break;
}
var dirs = [{
dc: 0,
dr: -1
},
// up
{
dc: 0,
dr: 1
},
// down
{
dc: -1,
dr: 0
},
// left
{
dc: 1,
dr: 0
} // right
];
for (var d = 0; d < dirs.length; d++) {
var nc = curr.col + dirs[d].dc;
var nr = curr.row + dirs[d].dr;
if (nc >= 0 && nc < MAZE_COLS && nr >= 0 && nr < MAZE_ROWS && !visited[nr][nc] && maze[nr][nc] !== 1 && maze[nr][nc] !== 4) {
queue.push({
col: nc,
row: nr
});
visited[nr][nc] = true;
prev[nr][nc] = {
col: curr.col,
row: curr.row
};
}
}
}
if (!found) return null;
// Reconstruct path
var path = [];
var currCol = playerCol,
currRow = playerRow;
while (!(currCol === self.col && currRow === self.row)) {
path.unshift({
col: currCol,
row: currRow
});
var p = prev[currRow][currCol];
currCol = p.col;
currRow = p.row;
}
return path;
};
// Monster update: move toward player, attack if adjacent
self.update = function () {
// Track last position for event logic
self.lastCol = self.col;
self.lastRow = self.row;
// Only recalc path every 15 frames for performance
if (LK.ticks % 15 === 0 || !self.path || self.path.length === 0) {
self.path = self.findPathToPlayer(charCol, charRow);
self.pathStepIndex = 0;
}
// If adjacent to player, attack
var dx = Math.abs(self.col - charCol);
var dy = Math.abs(self.row - charRow);
if (dx + dy === 1) {
// Attack cooldown
if (self.attackCooldown <= 0) {
// Show attack effect
var effect = LK.getAsset('attackEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: charNode.x,
y: charNode.y,
width: MAZE_CELL_SIZE * 0.7,
height: MAZE_CELL_SIZE * 0.7
});
effect.alpha = 1;
game.addChild(effect);
// Defensive: ensure effect is defined and tween is a function
if (effect && typeof tween === "function") {
var tweenObj = tween(effect);
if (tweenObj && typeof tweenObj.to === "function") {
tweenObj.to({
alpha: 0
}, 400).onComplete(function () {
effect.destroy();
});
}
} else if (effect) {
// fallback: just destroy after a timeout if tween is not available
LK.setTimeout(function () {
effect.destroy();
}, 400);
}
// Flash screen red and show game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
self.attackCooldown = self.attackDelay;
}
} else if (self.path && self.path.length > 0 && self.pathStepIndex < self.path.length) {
// Move toward next cell in path
var next = self.path[self.pathStepIndex];
var targetX = next.col * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
var targetY = next.row * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
var ddx = targetX - self.x;
var ddy = targetY - self.y;
var dist = Math.sqrt(ddx * ddx + ddy * ddy);
if (dist > 2) {
var step = Math.min(self.moveSpeed, dist);
self.x += ddx / dist * step;
self.y += ddy / dist * step;
} else {
// Snap to cell
self.x = targetX;
self.y = targetY;
self.col = next.col;
self.row = next.row;
self.pathStepIndex++;
}
}
if (self.attackCooldown > 0) self.attackCooldown--;
};
return self;
});
/****
* Initialize Game
****/
// No classes needed for maze logic
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Game constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
// Maze configuration
// 0 = empty, 1 = wall, 2 = start, 3 = goal, 4 = door (closed)
var MAZE_CELL_SIZE = 128;
var MAZE_COLS = 16;
var MAZE_ROWS = 21;
// Example maze: outer walls, a few inner walls, start at (1,1), goal at (14,19)
var maze = [];
for (var y = 0; y < MAZE_ROWS; y++) {
maze[y] = [];
for (var x = 0; x < MAZE_COLS; x++) {
if (y === 0 || y === MAZE_ROWS - 1 || x === 0 || x === MAZE_COLS - 1) {
maze[y][x] = 1; // border wall
} else {
maze[y][x] = 0; // empty
}
}
}
// Add some doors (4 = closed door) at interesting locations
maze[7][8] = 4;
maze[12][5] = 4;
maze[16][10] = 4;
maze[5][11] = 4;
// Make the maze easier: fewer inner walls and dead ends
for (var i = 4; i < 12; i++) {
maze[7][i] = 1;
maze[12][i] = 1;
}
// Remove some vertical walls and dead ends for easier navigation
for (var i = 4; i < 17; i++) {
if (i !== 10) maze[i][4] = 1;
}
for (var i = 7; i < 11; i++) {
maze[9][i] = 1;
}
// Remove some isolated wall blocks to open up the maze
maze[5][2] = 1;
maze[14][2] = 1;
maze[2][13] = 1;
maze[14][13] = 1;
maze[1][1] = 2; // start
maze[19][14] = 3; // goal
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFF700
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Draw maze walls and monsters
var wallNodes = [];
var monsterNodes = [];
// Place monsters at specific hard maze locations for challenge
var monsterSpawns = [{
type: 'skeleton',
col: 5,
row: 5
}, {
type: 'goldenSkeleton',
col: 7,
row: 15
}, {
type: 'zombie',
col: 10,
row: 10
}, {
type: 'zombieSkeleton',
col: 13,
row: 8
}];
// Add asset for zombieSkeleton if not present
for (var y = 0; y < MAZE_ROWS; y++) {
for (var x = 0; x < MAZE_COLS; x++) {
if (maze[y][x] === 1) {
var wall = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
x: x * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
y: y * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
width: MAZE_CELL_SIZE,
height: MAZE_CELL_SIZE
});
wallNodes.push(wall);
game.addChild(wall);
}
// Render doors as special clickable assets
if (maze[y][x] === 4) {
// Use a different color for doors (yellowish)
var door = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
x: x * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
y: y * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
width: MAZE_CELL_SIZE,
height: MAZE_CELL_SIZE,
tint: 0xFFD700 // gold/yellow
});
door._mazeCol = x;
door._mazeRow = y;
// Make door clickable: open on tap
door.down = function (lx, ly, obj) {
// Defensive: check if still a door
var col = this._mazeCol;
var row = this._mazeRow;
if (maze[row][col] === 4) {
// Open the door: set to empty
maze[row][col] = 0;
// Remove the door node
this.destroy();
}
};
wallNodes.push(door);
game.addChild(door);
}
if (maze[y][x] === 3) {
// Goal marker
var goalNode = LK.getAsset('skeleton', {
anchorX: 0.5,
anchorY: 0.5,
x: x * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
y: y * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
width: MAZE_CELL_SIZE,
height: MAZE_CELL_SIZE
});
game.addChild(goalNode);
}
// Place monsters at their spawn locations
for (var mi = 0; mi < monsterSpawns.length; mi++) {
var m = monsterSpawns[mi];
if (m.col === x && m.row === y) {
var monster = new Monster();
monster.init(m.type, x, y);
monsterNodes.push(monster);
game.addChild(monster);
}
}
}
}
// Find start position
var charCol = 1,
charRow = 1;
for (var y = 0; y < MAZE_ROWS; y++) {
for (var x = 0; x < MAZE_COLS; x++) {
if (maze[y][x] === 2) {
charCol = x;
charRow = y;
}
}
}
// Create player character node and add to game
var charNode = LK.getAsset('Player', {
anchorX: 0.5,
anchorY: 0.5,
x: charCol * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
y: charRow * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
width: MAZE_CELL_SIZE * 0.8,
height: MAZE_CELL_SIZE * 0.8
});
game.addChild(charNode);
// Movement state
var moveTargetCol = charCol;
var moveTargetRow = charRow;
var isMoving = false;
var moveSpeed = 20; // px per frame (increased for faster player movement)
// Touch/drag movement: tap or drag to adjacent cell
function getCellFromPos(x, y) {
var col = Math.floor(x / MAZE_CELL_SIZE);
var row = Math.floor(y / MAZE_CELL_SIZE);
return {
col: col,
row: row
};
}
// Simple BFS pathfinding to allow tap-to-move to any reachable cell
function findPath(startCol, startRow, endCol, endRow) {
// Defensive: check bounds
if (endCol < 0 || endCol >= MAZE_COLS || endRow < 0 || endRow >= MAZE_ROWS) return null;
if (maze[endRow][endCol] === 1) return null;
var queue = [];
var visited = [];
for (var y = 0; y < MAZE_ROWS; y++) {
visited[y] = [];
for (var x = 0; x < MAZE_COLS; x++) {
visited[y][x] = false;
}
}
var prev = [];
for (var y = 0; y < MAZE_ROWS; y++) {
prev[y] = [];
for (var x = 0; x < MAZE_COLS; x++) {
prev[y][x] = null;
}
}
queue.push({
col: startCol,
row: startRow
});
visited[startRow][startCol] = true;
var found = false;
while (queue.length > 0) {
var curr = queue.shift();
if (curr.col === endCol && curr.row === endRow) {
found = true;
break;
}
var dirs = [{
dc: 0,
dr: -1
},
// up
{
dc: 0,
dr: 1
},
// down
{
dc: -1,
dr: 0
},
// left
{
dc: 1,
dr: 0
} // right
];
for (var d = 0; d < dirs.length; d++) {
var nc = curr.col + dirs[d].dc;
var nr = curr.row + dirs[d].dr;
if (nc >= 0 && nc < MAZE_COLS && nr >= 0 && nr < MAZE_ROWS && !visited[nr][nc] && maze[nr][nc] !== 1 && maze[nr][nc] !== 4) {
queue.push({
col: nc,
row: nr
});
visited[nr][nc] = true;
prev[nr][nc] = {
col: curr.col,
row: curr.row
};
}
}
}
if (!found) return null;
// Reconstruct path
var path = [];
var currCol = endCol,
currRow = endRow;
while (!(currCol === startCol && currRow === startRow)) {
path.unshift({
col: currCol,
row: currRow
});
var p = prev[currRow][currCol];
currCol = p.col;
currRow = p.row;
}
return path;
}
var pathToFollow = null;
var pathStepIndex = 0;
game.down = function (x, y, obj) {
// Prevent new move if already moving
if (isMoving) return;
// Compute direction vector from player to tap/click
var px = charNode.x;
var py = charNode.y;
var dx = x - px;
var dy = y - py;
var dist = Math.sqrt(dx * dx + dy * dy);
// Defensive: don't move if tap is too close to player
if (dist < 10) return;
// Normalize direction
var dirX = dx / dist;
var dirY = dy / dist;
// Compute the furthest cell in that direction that is not a wall/door
var currCol = charCol;
var currRow = charRow;
var lastFreeCol = charCol;
var lastFreeRow = charRow;
var maxSteps = 50; // Prevent infinite loop
for (var step = 0; step < maxSteps; step++) {
// Move a small step in direction
var nextX = px + dirX * (step + 1) * MAZE_CELL_SIZE * 0.5;
var nextY = py + dirY * (step + 1) * MAZE_CELL_SIZE * 0.5;
var cell = getCellFromPos(nextX, nextY);
// Defensive: check bounds
if (cell.col < 0 || cell.col >= MAZE_COLS || cell.row < 0 || cell.row >= MAZE_ROWS) break;
// Stop at wall or door
if (maze[cell.row][cell.col] === 1 || maze[cell.row][cell.col] === 4) break;
lastFreeCol = cell.col;
lastFreeRow = cell.row;
}
// Don't move if already at that cell
if (lastFreeCol === charCol && lastFreeRow === charRow) return;
moveTargetCol = lastFreeCol;
moveTargetRow = lastFreeRow;
isMoving = true;
pathToFollow = null;
pathStepIndex = 0;
};
game.move = function (x, y, obj) {
// Only allow drag-move if not already moving
if (!isMoving) {
game.down(x, y, obj);
}
};
game.up = function (x, y, obj) {
// No-op
};
game.update = function () {
// Move character toward target cell
var targetX = moveTargetCol * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
var targetY = moveTargetRow * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
var dx = targetX - charNode.x;
var dy = targetY - charNode.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (isMoving && dist > 2) {
var step = Math.min(moveSpeed, dist);
charNode.x += dx / dist * step;
charNode.y += dy / dist * step;
} else if (isMoving) {
// Snap to cell
charNode.x = targetX;
charNode.y = targetY;
charCol = moveTargetCol;
charRow = moveTargetRow;
// If following a path, advance to next step
if (typeof pathToFollow !== "undefined" && pathToFollow && pathStepIndex < pathToFollow.length - 1) {
pathStepIndex++;
moveTargetCol = pathToFollow[pathStepIndex].col;
moveTargetRow = pathToFollow[pathStepIndex].row;
// Remain isMoving = true
} else {
isMoving = false;
pathToFollow = null;
pathStepIndex = 0;
// Check for goal
if (maze[charRow][charCol] === 3) {
LK.effects.flashScreen(0x00ff00, 800);
// Infinite levels: immediately start a new random level after a short delay
LK.setTimeout(function () {
startNextLevel();
}, 1000);
// Pause game update until next level starts
return;
}
}
}
// Update all monsters
for (var i = 0; i < monsterNodes.length; i++) {
if (monsterNodes[i] && typeof monsterNodes[i].update === "function") {
monsterNodes[i].update();
}
}
// Show current cell in score text (for debugging movement)
scoreTxt.setText("Cell: " + charCol + "," + charRow);
};
// Infinite levels: generate a new random maze and monsters each time
function startNextLevel() {
// Remove all wall nodes and goal node from previous level
for (var i = 0; i < wallNodes.length; i++) {
if (wallNodes[i] && typeof wallNodes[i].destroy === "function") {
wallNodes[i].destroy();
}
}
wallNodes.length = 0;
// Remove all monsters
for (var i = 0; i < monsterNodes.length; i++) {
if (monsterNodes[i] && typeof monsterNodes[i].destroy === "function") {
monsterNodes[i].destroy();
}
}
monsterNodes.length = 0;
// Generate a random maze
// 0 = empty, 1 = wall, 2 = start, 3 = goal, 4 = door (closed)
// Always outer walls
for (var y = 0; y < MAZE_ROWS; y++) {
maze[y] = [];
for (var x = 0; x < MAZE_COLS; x++) {
if (y === 0 || y === MAZE_ROWS - 1 || x === 0 || x === MAZE_COLS - 1) {
maze[y][x] = 1;
} else {
// Randomly place walls, doors, or empty
var r = Math.random();
if (r < 0.13) {
maze[y][x] = 1; // wall
} else if (r < 0.16) {
maze[y][x] = 4; // door
} else {
maze[y][x] = 0; // empty
}
}
}
}
// Pick random start and goal positions (not on wall/door)
function randomEmptyCell() {
var tries = 0;
while (tries < 1000) {
var rx = 1 + Math.floor(Math.random() * (MAZE_COLS - 2));
var ry = 1 + Math.floor(Math.random() * (MAZE_ROWS - 2));
if (maze[ry][rx] === 0) return {
col: rx,
row: ry
};
tries++;
}
// fallback
return {
col: 1,
row: 1
};
}
var start = randomEmptyCell();
maze[start.row][start.col] = 2;
var goal = randomEmptyCell();
while (goal.col === start.col && goal.row === start.row) {
goal = randomEmptyCell();
}
maze[goal.row][goal.col] = 3;
// Redraw maze walls, doors, and goal
for (var y = 0; y < MAZE_ROWS; y++) {
for (var x = 0; x < MAZE_COLS; x++) {
if (maze[y][x] === 1) {
var wall = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
x: x * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
y: y * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
width: MAZE_CELL_SIZE,
height: MAZE_CELL_SIZE
});
wallNodes.push(wall);
game.addChild(wall);
}
if (maze[y][x] === 4) {
var door = LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5,
x: x * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
y: y * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
width: MAZE_CELL_SIZE,
height: MAZE_CELL_SIZE,
tint: 0xFFD700
});
door._mazeCol = x;
door._mazeRow = y;
door.down = function (lx, ly, obj) {
var col = this._mazeCol;
var row = this._mazeRow;
if (maze[row][col] === 4) {
maze[row][col] = 0;
this.destroy();
}
};
wallNodes.push(door);
game.addChild(door);
}
if (maze[y][x] === 3) {
var goalNode = LK.getAsset('skeleton', {
anchorX: 0.5,
anchorY: 0.5,
x: x * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
y: y * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2,
width: MAZE_CELL_SIZE,
height: MAZE_CELL_SIZE
});
game.addChild(goalNode);
}
}
}
// Place new monsters for this level
// Place 3-5 monsters at random empty cells (not start/goal)
var monsterTypes = ['skeleton', 'goldenSkeleton', 'zombie', 'zombieSkeleton'];
var monsterCount = 3 + Math.floor(Math.random() * 3);
var monsterSpots = [];
for (var i = 0; i < monsterCount; i++) {
var spot = randomEmptyCell();
// Don't place on start or goal
while (spot.col === start.col && spot.row === start.row || spot.col === goal.col && spot.row === goal.row || monsterSpots.some(function (ms) {
return ms.col === spot.col && ms.row === spot.row;
})) {
spot = randomEmptyCell();
}
monsterSpots.push(spot);
var mtype = monsterTypes[Math.floor(Math.random() * monsterTypes.length)];
var monster = new Monster();
monster.init(mtype, spot.col, spot.row);
monsterNodes.push(monster);
game.addChild(monster);
}
// Reset player to new start
charCol = start.col;
charRow = start.row;
charNode.x = charCol * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
charNode.y = charRow * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
moveTargetCol = charCol;
moveTargetRow = charRow;
isMoving = false;
// Show a message for new level start
if (typeof startNextLevel.level === "undefined") startNextLevel.level = 2;else startNextLevel.level++;
var msg = new Text2("Level " + startNextLevel.level + "!", {
size: 100,
fill: 0x00FFAA
});
msg.anchor.set(0.5, 0.5);
msg.x = GAME_WIDTH / 2;
msg.y = GAME_HEIGHT / 2 + 200;
game.addChild(msg);
LK.setTimeout(function () {
msg.destroy();
}, 1500);
} ===================================================================
--- original.js
+++ change.js
@@ -548,30 +548,13 @@
pathStepIndex = 0;
// Check for goal
if (maze[charRow][charCol] === 3) {
LK.effects.flashScreen(0x00ff00, 800);
- // Show a button to go to level 2
- if (!game._level2Button) {
- var btn = new Text2("Go to Level 2", {
- size: 120,
- fill: 0x00FF00
- });
- btn.anchor.set(0.5, 0.5);
- btn.x = GAME_WIDTH / 2;
- btn.y = GAME_HEIGHT / 2;
- btn.interactive = true;
- btn.buttonMode = true;
- btn.down = function (x, y, obj) {
- // Remove button
- btn.destroy();
- game._level2Button = null;
- // Call function to start level 2
- if (typeof startLevel2 === "function") startLevel2();
- };
- game.addChild(btn);
- game._level2Button = btn;
- }
- // Pause game update until button is pressed
+ // Infinite levels: immediately start a new random level after a short delay
+ LK.setTimeout(function () {
+ startNextLevel();
+ }, 1000);
+ // Pause game update until next level starts
return;
}
}
}
@@ -583,10 +566,10 @@
}
// Show current cell in score text (for debugging movement)
scoreTxt.setText("Cell: " + charCol + "," + charRow);
};
-// Stub for level 2 logic. You can now implement level 2 here!
-function startLevel2() {
+// Infinite levels: generate a new random maze and monsters each time
+function startNextLevel() {
// Remove all wall nodes and goal node from previous level
for (var i = 0; i < wallNodes.length; i++) {
if (wallNodes[i] && typeof wallNodes[i].destroy === "function") {
wallNodes[i].destroy();
@@ -599,88 +582,54 @@
monsterNodes[i].destroy();
}
}
monsterNodes.length = 0;
- // Define a set of random mazes for level 2
- var level2Mazes = [
- // Maze 1
- function () {
- for (var y = 0; y < MAZE_ROWS; y++) {
- maze[y] = [];
- for (var x = 0; x < MAZE_COLS; x++) {
- if (y === 0 || y === MAZE_ROWS - 1 || x === 0 || x === MAZE_COLS - 1) {
- maze[y][x] = 1;
+ // Generate a random maze
+ // 0 = empty, 1 = wall, 2 = start, 3 = goal, 4 = door (closed)
+ // Always outer walls
+ for (var y = 0; y < MAZE_ROWS; y++) {
+ maze[y] = [];
+ for (var x = 0; x < MAZE_COLS; x++) {
+ if (y === 0 || y === MAZE_ROWS - 1 || x === 0 || x === MAZE_COLS - 1) {
+ maze[y][x] = 1;
+ } else {
+ // Randomly place walls, doors, or empty
+ var r = Math.random();
+ if (r < 0.13) {
+ maze[y][x] = 1; // wall
+ } else if (r < 0.16) {
+ maze[y][x] = 4; // door
} else {
- maze[y][x] = 0;
+ maze[y][x] = 0; // empty
}
}
}
- maze[5][5] = 4;
- maze[10][10] = 4;
- maze[15][7] = 4;
- maze[8][12] = 4;
- for (var i = 3; i < 13; i++) {
- maze[10][i] = 1;
+ }
+ // Pick random start and goal positions (not on wall/door)
+ function randomEmptyCell() {
+ var tries = 0;
+ while (tries < 1000) {
+ var rx = 1 + Math.floor(Math.random() * (MAZE_COLS - 2));
+ var ry = 1 + Math.floor(Math.random() * (MAZE_ROWS - 2));
+ if (maze[ry][rx] === 0) return {
+ col: rx,
+ row: ry
+ };
+ tries++;
}
- for (var i = 5; i < 17; i++) {
- if (i !== 12) maze[i][6] = 1;
- }
- maze[2][2] = 2; // start
- maze[18][13] = 3; // goal
- },
- // Maze 2
- function () {
- for (var y = 0; y < MAZE_ROWS; y++) {
- maze[y] = [];
- for (var x = 0; x < MAZE_COLS; x++) {
- if (y === 0 || y === MAZE_ROWS - 1 || x === 0 || x === MAZE_COLS - 1) {
- maze[y][x] = 1;
- } else {
- maze[y][x] = 0;
- }
- }
- }
- maze[6][6] = 4;
- maze[12][12] = 4;
- maze[16][8] = 4;
- maze[9][10] = 4;
- for (var i = 2; i < 14; i++) {
- maze[12][i] = 1;
- }
- for (var i = 4; i < 18; i++) {
- if (i !== 8) maze[i][8] = 1;
- }
- maze[3][3] = 2; // start
- maze[17][12] = 3; // goal
- },
- // Maze 3
- function () {
- for (var y = 0; y < MAZE_ROWS; y++) {
- maze[y] = [];
- for (var x = 0; x < MAZE_COLS; x++) {
- if (y === 0 || y === MAZE_ROWS - 1 || x === 0 || x === MAZE_COLS - 1) {
- maze[y][x] = 1;
- } else {
- maze[y][x] = 0;
- }
- }
- }
- maze[7][7] = 4;
- maze[11][11] = 4;
- maze[14][9] = 4;
- maze[6][13] = 4;
- for (var i = 5; i < 15; i++) {
- maze[8][i] = 1;
- }
- for (var i = 6; i < 19; i++) {
- if (i !== 9) maze[i][10] = 1;
- }
- maze[4][4] = 2; // start
- maze[16][14] = 3; // goal
- }];
- // Pick a random maze for level 2
- var mazeIndex = Math.floor(Math.random() * level2Mazes.length);
- level2Mazes[mazeIndex]();
+ // fallback
+ return {
+ col: 1,
+ row: 1
+ };
+ }
+ var start = randomEmptyCell();
+ maze[start.row][start.col] = 2;
+ var goal = randomEmptyCell();
+ while (goal.col === start.col && goal.row === start.row) {
+ goal = randomEmptyCell();
+ }
+ maze[goal.row][goal.col] = 3;
// Redraw maze walls, doors, and goal
for (var y = 0; y < MAZE_ROWS; y++) {
for (var x = 0; x < MAZE_COLS; x++) {
if (maze[y][x] === 1) {
@@ -730,43 +679,39 @@
game.addChild(goalNode);
}
}
}
- // Place new monsters for level 2
- var level2Monsters = [{
- type: 'skeleton',
- col: 4,
- row: 4
- }, {
- type: 'goldenSkeleton',
- col: 12,
- row: 16
- }, {
- type: 'zombie',
- col: 8,
- row: 8
- }, {
- type: 'zombieSkeleton',
- col: 14,
- row: 5
- }];
- for (var mi = 0; mi < level2Monsters.length; mi++) {
- var m = level2Monsters[mi];
+ // Place new monsters for this level
+ // Place 3-5 monsters at random empty cells (not start/goal)
+ var monsterTypes = ['skeleton', 'goldenSkeleton', 'zombie', 'zombieSkeleton'];
+ var monsterCount = 3 + Math.floor(Math.random() * 3);
+ var monsterSpots = [];
+ for (var i = 0; i < monsterCount; i++) {
+ var spot = randomEmptyCell();
+ // Don't place on start or goal
+ while (spot.col === start.col && spot.row === start.row || spot.col === goal.col && spot.row === goal.row || monsterSpots.some(function (ms) {
+ return ms.col === spot.col && ms.row === spot.row;
+ })) {
+ spot = randomEmptyCell();
+ }
+ monsterSpots.push(spot);
+ var mtype = monsterTypes[Math.floor(Math.random() * monsterTypes.length)];
var monster = new Monster();
- monster.init(m.type, m.col, m.row);
+ monster.init(mtype, spot.col, spot.row);
monsterNodes.push(monster);
game.addChild(monster);
}
- // Reset player to new start for level 2
- charCol = 2;
- charRow = 2;
+ // Reset player to new start
+ charCol = start.col;
+ charRow = start.row;
charNode.x = charCol * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
charNode.y = charRow * MAZE_CELL_SIZE + MAZE_CELL_SIZE / 2;
moveTargetCol = charCol;
moveTargetRow = charRow;
isMoving = false;
- // Show a message for level 2 start
- var msg = new Text2("Level 2! Good luck!", {
+ // Show a message for new level start
+ if (typeof startNextLevel.level === "undefined") startNextLevel.level = 2;else startNextLevel.level++;
+ var msg = new Text2("Level " + startNextLevel.level + "!", {
size: 100,
fill: 0x00FFAA
});
msg.anchor.set(0.5, 0.5);
@@ -774,6 +719,6 @@
msg.y = GAME_HEIGHT / 2 + 200;
game.addChild(msg);
LK.setTimeout(function () {
msg.destroy();
- }, 2000);
+ }, 1500);
}
\ No newline at end of file
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Monster Mash: Undead Defense" and with the description "Defend your base from zombies, skeletons, and their golden counterparts by tapping or dragging to attack. Survive waves and score points!". No text on banner!
Zombie with long red eyes. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A zombie with long eyes but everything is golden. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A golden terrifying skeleton. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A zombie fixed with a skeleton. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Ghost. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Heart. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Monster with big hands and ginger hair. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat