User prompt
make simple maze and reduce gold
User prompt
hadirkan musuh dalam maze
User prompt
buat aturan dinding tidak bisa ditembus
User prompt
player bisa menembak dengan swipe
User prompt
hapus batas waktu
User prompt
buat 10 background
User prompt
perkecil ukuran maze dan letakkan di bawah layar
User prompt
tap control untuk player
Code edit (1 edits merged)
Please save this source code
User prompt
Maze Hunter: Golden Ball Chase
Initial prompt
create a maze in the middle with random entrance. hunting a golden balls
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = 0; self.speedY = 0; self.speed = 8; self.update = function () { // Store previous position for wall collision detection var prevX = self.x; var prevY = self.y; self.x += self.speedX; self.y += self.speedY; // Check if bullet hit a wall var mazeX = Math.floor((self.x - MAZE_START_X) / CELL_SIZE); var mazeY = Math.floor((self.y - MAZE_START_Y) / CELL_SIZE); // If bullet is inside maze bounds and hits a wall, destroy it if (mazeX >= 0 && mazeX < MAZE_WIDTH && mazeY >= 0 && mazeY < MAZE_HEIGHT) { if (maze[mazeY][mazeX] === 1) { self.destroy(); return; } } // Remove bullet if it goes off screen if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.destroy(); } }; self.setDirection = function (dirX, dirY) { var length = Math.sqrt(dirX * dirX + dirY * dirY); if (length > 0) { self.speedX = dirX / length * self.speed; self.speedY = dirY / length * self.speed; } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.targetX = 0; self.targetY = 0; self.moveTimer = 0; self.moveDelay = 60; // Change direction every 60 frames self.directions = [{ x: 0, y: -1 }, // up { x: 1, y: 0 }, // right { x: 0, y: 1 }, // down { x: -1, y: 0 } // left ]; self.update = function () { // Only update every 3 frames to reduce computation if (LK.ticks % 3 !== 0) return; // AI movement - change direction periodically self.moveTimer++; if (self.moveTimer >= self.moveDelay) { self.moveTimer = 0; self.chooseNewDirection(); } var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 2) { // Simplified movement - don't check walls every frame self.x += dx > 0 ? self.speed : dx < 0 ? -self.speed : 0; self.y += dy > 0 ? self.speed : dy < 0 ? -self.speed : 0; } else { // Reached target, choose new direction self.chooseNewDirection(); } }; self.chooseNewDirection = function () { var currentMazeX = Math.floor((self.x - MAZE_START_X) / CELL_SIZE); var currentMazeY = Math.floor((self.y - MAZE_START_Y) / CELL_SIZE); var validDirections = []; // Check each direction for valid movement for (var i = 0; i < self.directions.length; i++) { var dir = self.directions[i]; var newMazeX = currentMazeX + dir.x; var newMazeY = currentMazeY + dir.y; if (newMazeX >= 0 && newMazeX < MAZE_WIDTH && newMazeY >= 0 && newMazeY < MAZE_HEIGHT) { if (maze[newMazeY][newMazeX] === 0) { validDirections.push(dir); } } } if (validDirections.length > 0) { var chosenDir = validDirections[Math.floor(Math.random() * validDirections.length)]; var targetMazeX = currentMazeX + chosenDir.x; var targetMazeY = currentMazeY + chosenDir.y; self.targetX = MAZE_START_X + targetMazeX * CELL_SIZE + CELL_SIZE / 2; self.targetY = MAZE_START_Y + targetMazeY * CELL_SIZE + CELL_SIZE / 2; } }; return self; }); var GoldenBall = Container.expand(function () { var self = Container.call(this); var roseGraphics = self.attachAsset('goldenBall', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; self.update = function () { // Initialize floating animation on first update if (!self.floatingInitialized && !self.collected) { self.floatingInitialized = true; self.baseY = self.y; // Start vertical floating animation tween(roseGraphics, { y: -20 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Float back down tween(roseGraphics, { y: 20 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Reset and repeat roseGraphics.y = 0; self.floatingInitialized = false; } }); } }); } }; self.collect = function () { if (!self.collected) { self.collected = true; tween(roseGraphics, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.visible = false; } }); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.targetX = 0; self.targetY = 0; self.facingRight = true; // Track which direction player is facing self.update = function () { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 2) { // Update facing direction based on horizontal movement if (Math.abs(dx) > 1) { // Only change facing if significant horizontal movement if (dx > 0 && !self.facingRight) { // Moving right, currently facing left self.facingRight = true; tween(playerGraphics, { scaleX: 1 }, { duration: 200, easing: tween.easeOut }); } else if (dx < 0 && self.facingRight) { // Moving left, currently facing right self.facingRight = false; tween(playerGraphics, { scaleX: -1 }, { duration: 200, easing: tween.easeOut }); } } // Calculate next position var nextX = self.x + dx / distance * self.speed; var nextY = self.y + dy / distance * self.speed; // Check if next position is valid (not a wall) var mazeX = Math.floor((nextX - MAZE_START_X) / CELL_SIZE); var mazeY = Math.floor((nextY - MAZE_START_Y) / CELL_SIZE); // Check if next position is within maze bounds if (mazeX >= 0 && mazeX < MAZE_WIDTH && mazeY >= 0 && mazeY < MAZE_HEIGHT) { // Inside maze - only move if not a wall if (maze[mazeY][mazeX] === 0) { self.x = nextX; self.y = nextY; } } else { // Outside maze bounds - allow free movement anywhere on screen if (nextX >= 0 && nextX <= 2048 && nextY >= 0 && nextY <= 2732) { self.x = nextX; self.y = nextY; } } } }; self.setTarget = function (x, y) { self.targetX = x; self.targetY = y; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ // Set background color explicitly game.setBackgroundColor(0x000000); // Add background image var background = game.addChild(LK.getAsset('background', { x: 0, y: 0, width: 2048, height: 2732, alpha: 1.0, tint: 0xffffff })); // Send background to the back so other elements appear on top game.setChildIndex(background, 0); // Make background more sharp and visible background.alpha = 1.0; background.scale.set(1.0, 1.0); var CELL_SIZE = 48; var MAZE_WIDTH = 16; var MAZE_HEIGHT = 20; var MAZE_START_X = (2048 - MAZE_WIDTH * CELL_SIZE) / 2; var MAZE_START_Y = (2732 - MAZE_HEIGHT * CELL_SIZE) / 2; var maze = []; var walls = []; var floors = []; var player; var goldenBalls = []; var totalBalls = 0; var collectedBalls = 0; var bullets = []; var enemies = []; var enemyCount = 3; var swipeStart = { x: 0, y: 0 }; var isSwipping = false; // Tap controls - no drag node needed // Score display var scoreText = new Text2('Roses: 0/0', { size: 50, fill: 0xFFD700 }); scoreText.anchor.set(1, 0); LK.gui.topRight.addChild(scoreText); scoreText.x = -20; scoreText.y = 80; function generateMaze() { // Initialize maze with walls for (var y = 0; y < MAZE_HEIGHT; y++) { maze[y] = []; for (var x = 0; x < MAZE_WIDTH; x++) { maze[y][x] = 1; // 1 = wall, 0 = path } } // Generate maze without dead ends using modified algorithm var startX = 1; var startY = 1; maze[startY][startX] = 0; var directions = [{ x: 0, y: -2 }, // up { x: 2, y: 0 }, // right { x: 0, y: 2 }, // down { x: -2, y: 0 } // left ]; // First pass: create main paths using recursive backtracking var stack = []; stack.push({ x: startX, y: startY }); while (stack.length > 0) { var current = stack[stack.length - 1]; var neighbors = []; for (var i = 0; i < directions.length; i++) { var nx = current.x + directions[i].x; var ny = current.y + directions[i].y; if (nx > 0 && nx < MAZE_WIDTH - 1 && ny > 0 && ny < MAZE_HEIGHT - 1 && maze[ny][nx] === 1) { neighbors.push({ x: nx, y: ny, dir: directions[i] }); } } if (neighbors.length > 0) { var next = neighbors[Math.floor(Math.random() * neighbors.length)]; maze[next.y][next.x] = 0; maze[current.y + next.dir.y / 2][current.x + next.dir.x / 2] = 0; stack.push({ x: next.x, y: next.y }); } else { stack.pop(); } } // Second pass: add loops to eliminate dead ends for (var attempts = 0; attempts < 20; attempts++) { // Find all dead ends (cells with only one neighbor) var deadEnds = []; for (var y = 1; y < MAZE_HEIGHT - 1; y += 2) { for (var x = 1; x < MAZE_WIDTH - 1; x += 2) { if (maze[y][x] === 0) { var pathCount = 0; var possibleConnections = []; // Check all four directions if (y > 1 && maze[y - 1][x] === 0) pathCount++; if (y < MAZE_HEIGHT - 2 && maze[y + 1][x] === 0) pathCount++; if (x > 1 && maze[y][x - 1] === 0) pathCount++; if (x < MAZE_WIDTH - 2 && maze[y][x + 1] === 0) pathCount++; // If it's a dead end, try to connect it to another path if (pathCount === 1) { // Try to connect to nearby paths for (var i = 0; i < directions.length; i++) { var newX = x + directions[i].x; var newY = y + directions[i].y; if (newX > 0 && newX < MAZE_WIDTH - 1 && newY > 0 && newY < MAZE_HEIGHT - 1) { if (maze[newY][newX] === 0) { var wallX = x + directions[i].x / 2; var wallY = y + directions[i].y / 2; if (maze[wallY][wallX] === 1) { possibleConnections.push({ wallX: wallX, wallY: wallY }); } } } } if (possibleConnections.length > 0) { var connection = possibleConnections[Math.floor(Math.random() * possibleConnections.length)]; maze[connection.wallY][connection.wallX] = 0; } } } } } } // Create entrance var entranceX = Math.floor(Math.random() * (MAZE_WIDTH - 2)) + 1; maze[0][entranceX] = 0; return { x: entranceX, y: 0 }; } function createMazeVisuals() { for (var y = 0; y < MAZE_HEIGHT; y++) { for (var x = 0; x < MAZE_WIDTH; x++) { var cellX = MAZE_START_X + x * CELL_SIZE; var cellY = MAZE_START_Y + y * CELL_SIZE; if (maze[y][x] === 1) { var wall = game.addChild(LK.getAsset('wall', { x: cellX, y: cellY })); walls.push(wall); } else { var floor = game.addChild(LK.getAsset('floor', { x: cellX, y: cellY })); floors.push(floor); } } } } function spawnEnemies() { var spawnedEnemies = 0; var attempts = 0; var maxAttempts = 100; while (spawnedEnemies < enemyCount && attempts < maxAttempts) { var x = Math.floor(Math.random() * MAZE_WIDTH); var y = Math.floor(Math.random() * MAZE_HEIGHT); // Make sure enemy spawns in a path cell and not too close to player start if (maze[y][x] === 0 && (Math.abs(x - entrance.x) > 3 || Math.abs(y - entrance.y) > 3)) { var cellX = MAZE_START_X + x * CELL_SIZE + CELL_SIZE / 2; var cellY = MAZE_START_Y + y * CELL_SIZE + CELL_SIZE / 2; var enemy = game.addChild(new Enemy()); enemy.x = cellX; enemy.y = cellY; enemy.targetX = cellX; enemy.targetY = cellY; enemy.mazeX = x; enemy.mazeY = y; enemies.push(enemy); spawnedEnemies++; } attempts++; } } function placeGoldenRoses() { var roseCount = Math.floor(MAZE_WIDTH * MAZE_HEIGHT * 0.02); // 2% of maze cells var placedRoses = 0; while (placedRoses < roseCount) { var x = Math.floor(Math.random() * MAZE_WIDTH); var y = Math.floor(Math.random() * MAZE_HEIGHT); if (maze[y][x] === 0) { var cellX = MAZE_START_X + x * CELL_SIZE + CELL_SIZE / 2; var cellY = MAZE_START_Y + y * CELL_SIZE + CELL_SIZE / 2; var rose = game.addChild(new GoldenBall()); rose.x = cellX; rose.y = cellY; rose.mazeX = x; rose.mazeY = y; goldenBalls.push(rose); placedRoses++; } } totalBalls = goldenBalls.length; updateScoreText(); } function updateScoreText() { scoreText.setText('Roses: ' + collectedBalls + '/' + totalBalls); } function isValidPosition(x, y) { var mazeX = Math.floor((x - MAZE_START_X) / CELL_SIZE); var mazeY = Math.floor((y - MAZE_START_Y) / CELL_SIZE); if (mazeX < 0 || mazeX >= MAZE_WIDTH || mazeY < 0 || mazeY >= MAZE_HEIGHT) { return false; } return maze[mazeY][mazeX] === 0; } function getValidTargetPosition(targetX, targetY) { var currentMazeX = Math.floor((player.x - MAZE_START_X) / CELL_SIZE); var currentMazeY = Math.floor((player.y - MAZE_START_Y) / CELL_SIZE); var targetMazeX = Math.floor((targetX - MAZE_START_X) / CELL_SIZE); var targetMazeY = Math.floor((targetY - MAZE_START_Y) / CELL_SIZE); if (targetMazeX < 0 || targetMazeX >= MAZE_WIDTH || targetMazeY < 0 || targetMazeY >= MAZE_HEIGHT) { return { x: player.x, y: player.y }; } if (maze[targetMazeY][targetMazeX] === 1) { return { x: player.x, y: player.y }; } var cellCenterX = MAZE_START_X + targetMazeX * CELL_SIZE + CELL_SIZE / 2; var cellCenterY = MAZE_START_Y + targetMazeY * CELL_SIZE + CELL_SIZE / 2; return { x: cellCenterX, y: cellCenterY }; } // Initialize maze var entrance = generateMaze(); createMazeVisuals(); // Create player player = game.addChild(new Player()); player.x = MAZE_START_X + entrance.x * CELL_SIZE + CELL_SIZE / 2; player.y = MAZE_START_Y + entrance.y * CELL_SIZE + CELL_SIZE / 2; player.setTarget(player.x, player.y); // Place golden roses placeGoldenRoses(); // Spawn enemies spawnEnemies(); // Touch controls - swipe to shoot, tap to move game.down = function (x, y, obj) { swipeStart.x = x; swipeStart.y = y; isSwipping = true; }; game.move = function (x, y, obj) { // Track swipe movement but don't move player during swipe }; game.up = function (x, y, obj) { if (isSwipping) { var swipeEndX = x; var swipeEndY = y; var swipeDistanceX = swipeEndX - swipeStart.x; var swipeDistanceY = swipeEndY - swipeStart.y; var swipeDistance = Math.sqrt(swipeDistanceX * swipeDistanceX + swipeDistanceY * swipeDistanceY); if (swipeDistance > 30) { // Minimum swipe distance // Create bullet and shoot in swipe direction var bullet = game.addChild(new Bullet()); bullet.x = player.x; bullet.y = player.y; bullet.setDirection(swipeDistanceX, swipeDistanceY); bullets.push(bullet); LK.getSound('shoot').play(); } else { // Short tap - move player to tap position (no maze validation) player.setTarget(x, y); } isSwipping = false; } }; // Game update loop game.update = function () { // Clean up destroyed bullets for (var b = bullets.length - 1; b >= 0; b--) { var bullet = bullets[b]; if (!bullet.parent) { // Bullet has been destroyed bullets.splice(b, 1); } } // Only check collisions every other frame to reduce load if (LK.ticks % 2 === 0) { // Check player-enemy collision for (var e = 0; e < enemies.length; e++) { var enemy = enemies[e]; if (player.intersects(enemy)) { // Player touched enemy - game over LK.showGameOver(); return; } } } // Check bullet-enemy collision (keep this every frame for responsiveness) for (var b = bullets.length - 1; b >= 0; b--) { var bullet = bullets[b]; var hitEnemy = false; for (var e = enemies.length - 1; e >= 0; e--) { var enemy = enemies[e]; if (bullet.intersects(enemy)) { // Store enemy position for respawn var respawnX = enemy.x; var respawnY = enemy.y; var respawnMazeX = enemy.mazeX; var respawnMazeY = enemy.mazeY; // Calculate knockback direction (opposite to bullet direction) var knockbackX = -bullet.speedX * 15; // Amplify knockback distance var knockbackY = -bullet.speedY * 15; // Calculate final knockback position (outside maze) var finalX = enemy.x + knockbackX; var finalY = enemy.y + knockbackY; // Ensure knockback goes outside screen boundaries if (finalX > -100 && finalX < 2148) { finalX = finalX < 1024 ? -200 : 2248; } if (finalY > -100 && finalY < 2832) { finalY = finalY < 1366 ? -200 : 2932; } // Remove bullet immediately bullet.destroy(); bullets.splice(b, 1); // Add score for defeating enemy LK.setScore(LK.getScore() + 10); // Animate enemy knockback with tween tween(enemy, { x: finalX, y: finalY, rotation: Math.PI * 4, // Spin while being knocked back scaleX: 0.5, scaleY: 0.5, alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Remove enemy from arrays and destroy for (var idx = enemies.length - 1; idx >= 0; idx--) { if (enemies[idx] === enemy) { enemies.splice(idx, 1); break; } } enemy.destroy(); // Schedule enemy respawn after 5 seconds using tween var dummyObject = {}; tween(dummyObject, {}, { duration: 5000, onFinish: function onFinish() { // Respawn enemy at same position var newEnemy = game.addChild(new Enemy()); newEnemy.x = respawnX; newEnemy.y = respawnY; newEnemy.targetX = respawnX; newEnemy.targetY = respawnY; newEnemy.mazeX = respawnMazeX; newEnemy.mazeY = respawnMazeY; enemies.push(newEnemy); } }); } }); hitEnemy = true; break; } } if (hitEnemy) break; } // Check rose collection every 3 frames if (LK.ticks % 3 === 0) { for (var i = 0; i < goldenBalls.length; i++) { var rose = goldenBalls[i]; if (!rose.collected && player.intersects(rose)) { rose.collect(); collectedBalls++; LK.setScore(collectedBalls); updateScoreText(); LK.getSound('collect').play(); // Check win condition if (collectedBalls >= totalBalls) { LK.getSound('complete').play(); LK.setTimeout(function () { LK.showYouWin(); }, 500); } } } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 0;
self.speed = 8;
self.update = function () {
// Store previous position for wall collision detection
var prevX = self.x;
var prevY = self.y;
self.x += self.speedX;
self.y += self.speedY;
// Check if bullet hit a wall
var mazeX = Math.floor((self.x - MAZE_START_X) / CELL_SIZE);
var mazeY = Math.floor((self.y - MAZE_START_Y) / CELL_SIZE);
// If bullet is inside maze bounds and hits a wall, destroy it
if (mazeX >= 0 && mazeX < MAZE_WIDTH && mazeY >= 0 && mazeY < MAZE_HEIGHT) {
if (maze[mazeY][mazeX] === 1) {
self.destroy();
return;
}
}
// Remove bullet if it goes off screen
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.destroy();
}
};
self.setDirection = function (dirX, dirY) {
var length = Math.sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
self.speedX = dirX / length * self.speed;
self.speedY = dirY / length * self.speed;
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.targetX = 0;
self.targetY = 0;
self.moveTimer = 0;
self.moveDelay = 60; // Change direction every 60 frames
self.directions = [{
x: 0,
y: -1
},
// up
{
x: 1,
y: 0
},
// right
{
x: 0,
y: 1
},
// down
{
x: -1,
y: 0
} // left
];
self.update = function () {
// Only update every 3 frames to reduce computation
if (LK.ticks % 3 !== 0) return;
// AI movement - change direction periodically
self.moveTimer++;
if (self.moveTimer >= self.moveDelay) {
self.moveTimer = 0;
self.chooseNewDirection();
}
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 2) {
// Simplified movement - don't check walls every frame
self.x += dx > 0 ? self.speed : dx < 0 ? -self.speed : 0;
self.y += dy > 0 ? self.speed : dy < 0 ? -self.speed : 0;
} else {
// Reached target, choose new direction
self.chooseNewDirection();
}
};
self.chooseNewDirection = function () {
var currentMazeX = Math.floor((self.x - MAZE_START_X) / CELL_SIZE);
var currentMazeY = Math.floor((self.y - MAZE_START_Y) / CELL_SIZE);
var validDirections = [];
// Check each direction for valid movement
for (var i = 0; i < self.directions.length; i++) {
var dir = self.directions[i];
var newMazeX = currentMazeX + dir.x;
var newMazeY = currentMazeY + dir.y;
if (newMazeX >= 0 && newMazeX < MAZE_WIDTH && newMazeY >= 0 && newMazeY < MAZE_HEIGHT) {
if (maze[newMazeY][newMazeX] === 0) {
validDirections.push(dir);
}
}
}
if (validDirections.length > 0) {
var chosenDir = validDirections[Math.floor(Math.random() * validDirections.length)];
var targetMazeX = currentMazeX + chosenDir.x;
var targetMazeY = currentMazeY + chosenDir.y;
self.targetX = MAZE_START_X + targetMazeX * CELL_SIZE + CELL_SIZE / 2;
self.targetY = MAZE_START_Y + targetMazeY * CELL_SIZE + CELL_SIZE / 2;
}
};
return self;
});
var GoldenBall = Container.expand(function () {
var self = Container.call(this);
var roseGraphics = self.attachAsset('goldenBall', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.update = function () {
// Initialize floating animation on first update
if (!self.floatingInitialized && !self.collected) {
self.floatingInitialized = true;
self.baseY = self.y;
// Start vertical floating animation
tween(roseGraphics, {
y: -20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Float back down
tween(roseGraphics, {
y: 20
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Reset and repeat
roseGraphics.y = 0;
self.floatingInitialized = false;
}
});
}
});
}
};
self.collect = function () {
if (!self.collected) {
self.collected = true;
tween(roseGraphics, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.visible = false;
}
});
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 4;
self.targetX = 0;
self.targetY = 0;
self.facingRight = true; // Track which direction player is facing
self.update = function () {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 2) {
// Update facing direction based on horizontal movement
if (Math.abs(dx) > 1) {
// Only change facing if significant horizontal movement
if (dx > 0 && !self.facingRight) {
// Moving right, currently facing left
self.facingRight = true;
tween(playerGraphics, {
scaleX: 1
}, {
duration: 200,
easing: tween.easeOut
});
} else if (dx < 0 && self.facingRight) {
// Moving left, currently facing right
self.facingRight = false;
tween(playerGraphics, {
scaleX: -1
}, {
duration: 200,
easing: tween.easeOut
});
}
}
// Calculate next position
var nextX = self.x + dx / distance * self.speed;
var nextY = self.y + dy / distance * self.speed;
// Check if next position is valid (not a wall)
var mazeX = Math.floor((nextX - MAZE_START_X) / CELL_SIZE);
var mazeY = Math.floor((nextY - MAZE_START_Y) / CELL_SIZE);
// Check if next position is within maze bounds
if (mazeX >= 0 && mazeX < MAZE_WIDTH && mazeY >= 0 && mazeY < MAZE_HEIGHT) {
// Inside maze - only move if not a wall
if (maze[mazeY][mazeX] === 0) {
self.x = nextX;
self.y = nextY;
}
} else {
// Outside maze bounds - allow free movement anywhere on screen
if (nextX >= 0 && nextX <= 2048 && nextY >= 0 && nextY <= 2732) {
self.x = nextX;
self.y = nextY;
}
}
}
};
self.setTarget = function (x, y) {
self.targetX = x;
self.targetY = y;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a1a1a
});
/****
* Game Code
****/
// Set background color explicitly
game.setBackgroundColor(0x000000);
// Add background image
var background = game.addChild(LK.getAsset('background', {
x: 0,
y: 0,
width: 2048,
height: 2732,
alpha: 1.0,
tint: 0xffffff
}));
// Send background to the back so other elements appear on top
game.setChildIndex(background, 0);
// Make background more sharp and visible
background.alpha = 1.0;
background.scale.set(1.0, 1.0);
var CELL_SIZE = 48;
var MAZE_WIDTH = 16;
var MAZE_HEIGHT = 20;
var MAZE_START_X = (2048 - MAZE_WIDTH * CELL_SIZE) / 2;
var MAZE_START_Y = (2732 - MAZE_HEIGHT * CELL_SIZE) / 2;
var maze = [];
var walls = [];
var floors = [];
var player;
var goldenBalls = [];
var totalBalls = 0;
var collectedBalls = 0;
var bullets = [];
var enemies = [];
var enemyCount = 3;
var swipeStart = {
x: 0,
y: 0
};
var isSwipping = false;
// Tap controls - no drag node needed
// Score display
var scoreText = new Text2('Roses: 0/0', {
size: 50,
fill: 0xFFD700
});
scoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreText);
scoreText.x = -20;
scoreText.y = 80;
function generateMaze() {
// Initialize maze with walls
for (var y = 0; y < MAZE_HEIGHT; y++) {
maze[y] = [];
for (var x = 0; x < MAZE_WIDTH; x++) {
maze[y][x] = 1; // 1 = wall, 0 = path
}
}
// Generate maze without dead ends using modified algorithm
var startX = 1;
var startY = 1;
maze[startY][startX] = 0;
var directions = [{
x: 0,
y: -2
},
// up
{
x: 2,
y: 0
},
// right
{
x: 0,
y: 2
},
// down
{
x: -2,
y: 0
} // left
];
// First pass: create main paths using recursive backtracking
var stack = [];
stack.push({
x: startX,
y: startY
});
while (stack.length > 0) {
var current = stack[stack.length - 1];
var neighbors = [];
for (var i = 0; i < directions.length; i++) {
var nx = current.x + directions[i].x;
var ny = current.y + directions[i].y;
if (nx > 0 && nx < MAZE_WIDTH - 1 && ny > 0 && ny < MAZE_HEIGHT - 1 && maze[ny][nx] === 1) {
neighbors.push({
x: nx,
y: ny,
dir: directions[i]
});
}
}
if (neighbors.length > 0) {
var next = neighbors[Math.floor(Math.random() * neighbors.length)];
maze[next.y][next.x] = 0;
maze[current.y + next.dir.y / 2][current.x + next.dir.x / 2] = 0;
stack.push({
x: next.x,
y: next.y
});
} else {
stack.pop();
}
}
// Second pass: add loops to eliminate dead ends
for (var attempts = 0; attempts < 20; attempts++) {
// Find all dead ends (cells with only one neighbor)
var deadEnds = [];
for (var y = 1; y < MAZE_HEIGHT - 1; y += 2) {
for (var x = 1; x < MAZE_WIDTH - 1; x += 2) {
if (maze[y][x] === 0) {
var pathCount = 0;
var possibleConnections = [];
// Check all four directions
if (y > 1 && maze[y - 1][x] === 0) pathCount++;
if (y < MAZE_HEIGHT - 2 && maze[y + 1][x] === 0) pathCount++;
if (x > 1 && maze[y][x - 1] === 0) pathCount++;
if (x < MAZE_WIDTH - 2 && maze[y][x + 1] === 0) pathCount++;
// If it's a dead end, try to connect it to another path
if (pathCount === 1) {
// Try to connect to nearby paths
for (var i = 0; i < directions.length; i++) {
var newX = x + directions[i].x;
var newY = y + directions[i].y;
if (newX > 0 && newX < MAZE_WIDTH - 1 && newY > 0 && newY < MAZE_HEIGHT - 1) {
if (maze[newY][newX] === 0) {
var wallX = x + directions[i].x / 2;
var wallY = y + directions[i].y / 2;
if (maze[wallY][wallX] === 1) {
possibleConnections.push({
wallX: wallX,
wallY: wallY
});
}
}
}
}
if (possibleConnections.length > 0) {
var connection = possibleConnections[Math.floor(Math.random() * possibleConnections.length)];
maze[connection.wallY][connection.wallX] = 0;
}
}
}
}
}
}
// Create entrance
var entranceX = Math.floor(Math.random() * (MAZE_WIDTH - 2)) + 1;
maze[0][entranceX] = 0;
return {
x: entranceX,
y: 0
};
}
function createMazeVisuals() {
for (var y = 0; y < MAZE_HEIGHT; y++) {
for (var x = 0; x < MAZE_WIDTH; x++) {
var cellX = MAZE_START_X + x * CELL_SIZE;
var cellY = MAZE_START_Y + y * CELL_SIZE;
if (maze[y][x] === 1) {
var wall = game.addChild(LK.getAsset('wall', {
x: cellX,
y: cellY
}));
walls.push(wall);
} else {
var floor = game.addChild(LK.getAsset('floor', {
x: cellX,
y: cellY
}));
floors.push(floor);
}
}
}
}
function spawnEnemies() {
var spawnedEnemies = 0;
var attempts = 0;
var maxAttempts = 100;
while (spawnedEnemies < enemyCount && attempts < maxAttempts) {
var x = Math.floor(Math.random() * MAZE_WIDTH);
var y = Math.floor(Math.random() * MAZE_HEIGHT);
// Make sure enemy spawns in a path cell and not too close to player start
if (maze[y][x] === 0 && (Math.abs(x - entrance.x) > 3 || Math.abs(y - entrance.y) > 3)) {
var cellX = MAZE_START_X + x * CELL_SIZE + CELL_SIZE / 2;
var cellY = MAZE_START_Y + y * CELL_SIZE + CELL_SIZE / 2;
var enemy = game.addChild(new Enemy());
enemy.x = cellX;
enemy.y = cellY;
enemy.targetX = cellX;
enemy.targetY = cellY;
enemy.mazeX = x;
enemy.mazeY = y;
enemies.push(enemy);
spawnedEnemies++;
}
attempts++;
}
}
function placeGoldenRoses() {
var roseCount = Math.floor(MAZE_WIDTH * MAZE_HEIGHT * 0.02); // 2% of maze cells
var placedRoses = 0;
while (placedRoses < roseCount) {
var x = Math.floor(Math.random() * MAZE_WIDTH);
var y = Math.floor(Math.random() * MAZE_HEIGHT);
if (maze[y][x] === 0) {
var cellX = MAZE_START_X + x * CELL_SIZE + CELL_SIZE / 2;
var cellY = MAZE_START_Y + y * CELL_SIZE + CELL_SIZE / 2;
var rose = game.addChild(new GoldenBall());
rose.x = cellX;
rose.y = cellY;
rose.mazeX = x;
rose.mazeY = y;
goldenBalls.push(rose);
placedRoses++;
}
}
totalBalls = goldenBalls.length;
updateScoreText();
}
function updateScoreText() {
scoreText.setText('Roses: ' + collectedBalls + '/' + totalBalls);
}
function isValidPosition(x, y) {
var mazeX = Math.floor((x - MAZE_START_X) / CELL_SIZE);
var mazeY = Math.floor((y - MAZE_START_Y) / CELL_SIZE);
if (mazeX < 0 || mazeX >= MAZE_WIDTH || mazeY < 0 || mazeY >= MAZE_HEIGHT) {
return false;
}
return maze[mazeY][mazeX] === 0;
}
function getValidTargetPosition(targetX, targetY) {
var currentMazeX = Math.floor((player.x - MAZE_START_X) / CELL_SIZE);
var currentMazeY = Math.floor((player.y - MAZE_START_Y) / CELL_SIZE);
var targetMazeX = Math.floor((targetX - MAZE_START_X) / CELL_SIZE);
var targetMazeY = Math.floor((targetY - MAZE_START_Y) / CELL_SIZE);
if (targetMazeX < 0 || targetMazeX >= MAZE_WIDTH || targetMazeY < 0 || targetMazeY >= MAZE_HEIGHT) {
return {
x: player.x,
y: player.y
};
}
if (maze[targetMazeY][targetMazeX] === 1) {
return {
x: player.x,
y: player.y
};
}
var cellCenterX = MAZE_START_X + targetMazeX * CELL_SIZE + CELL_SIZE / 2;
var cellCenterY = MAZE_START_Y + targetMazeY * CELL_SIZE + CELL_SIZE / 2;
return {
x: cellCenterX,
y: cellCenterY
};
}
// Initialize maze
var entrance = generateMaze();
createMazeVisuals();
// Create player
player = game.addChild(new Player());
player.x = MAZE_START_X + entrance.x * CELL_SIZE + CELL_SIZE / 2;
player.y = MAZE_START_Y + entrance.y * CELL_SIZE + CELL_SIZE / 2;
player.setTarget(player.x, player.y);
// Place golden roses
placeGoldenRoses();
// Spawn enemies
spawnEnemies();
// Touch controls - swipe to shoot, tap to move
game.down = function (x, y, obj) {
swipeStart.x = x;
swipeStart.y = y;
isSwipping = true;
};
game.move = function (x, y, obj) {
// Track swipe movement but don't move player during swipe
};
game.up = function (x, y, obj) {
if (isSwipping) {
var swipeEndX = x;
var swipeEndY = y;
var swipeDistanceX = swipeEndX - swipeStart.x;
var swipeDistanceY = swipeEndY - swipeStart.y;
var swipeDistance = Math.sqrt(swipeDistanceX * swipeDistanceX + swipeDistanceY * swipeDistanceY);
if (swipeDistance > 30) {
// Minimum swipe distance
// Create bullet and shoot in swipe direction
var bullet = game.addChild(new Bullet());
bullet.x = player.x;
bullet.y = player.y;
bullet.setDirection(swipeDistanceX, swipeDistanceY);
bullets.push(bullet);
LK.getSound('shoot').play();
} else {
// Short tap - move player to tap position (no maze validation)
player.setTarget(x, y);
}
isSwipping = false;
}
};
// Game update loop
game.update = function () {
// Clean up destroyed bullets
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
if (!bullet.parent) {
// Bullet has been destroyed
bullets.splice(b, 1);
}
}
// Only check collisions every other frame to reduce load
if (LK.ticks % 2 === 0) {
// Check player-enemy collision
for (var e = 0; e < enemies.length; e++) {
var enemy = enemies[e];
if (player.intersects(enemy)) {
// Player touched enemy - game over
LK.showGameOver();
return;
}
}
}
// Check bullet-enemy collision (keep this every frame for responsiveness)
for (var b = bullets.length - 1; b >= 0; b--) {
var bullet = bullets[b];
var hitEnemy = false;
for (var e = enemies.length - 1; e >= 0; e--) {
var enemy = enemies[e];
if (bullet.intersects(enemy)) {
// Store enemy position for respawn
var respawnX = enemy.x;
var respawnY = enemy.y;
var respawnMazeX = enemy.mazeX;
var respawnMazeY = enemy.mazeY;
// Calculate knockback direction (opposite to bullet direction)
var knockbackX = -bullet.speedX * 15; // Amplify knockback distance
var knockbackY = -bullet.speedY * 15;
// Calculate final knockback position (outside maze)
var finalX = enemy.x + knockbackX;
var finalY = enemy.y + knockbackY;
// Ensure knockback goes outside screen boundaries
if (finalX > -100 && finalX < 2148) {
finalX = finalX < 1024 ? -200 : 2248;
}
if (finalY > -100 && finalY < 2832) {
finalY = finalY < 1366 ? -200 : 2932;
}
// Remove bullet immediately
bullet.destroy();
bullets.splice(b, 1);
// Add score for defeating enemy
LK.setScore(LK.getScore() + 10);
// Animate enemy knockback with tween
tween(enemy, {
x: finalX,
y: finalY,
rotation: Math.PI * 4,
// Spin while being knocked back
scaleX: 0.5,
scaleY: 0.5,
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove enemy from arrays and destroy
for (var idx = enemies.length - 1; idx >= 0; idx--) {
if (enemies[idx] === enemy) {
enemies.splice(idx, 1);
break;
}
}
enemy.destroy();
// Schedule enemy respawn after 5 seconds using tween
var dummyObject = {};
tween(dummyObject, {}, {
duration: 5000,
onFinish: function onFinish() {
// Respawn enemy at same position
var newEnemy = game.addChild(new Enemy());
newEnemy.x = respawnX;
newEnemy.y = respawnY;
newEnemy.targetX = respawnX;
newEnemy.targetY = respawnY;
newEnemy.mazeX = respawnMazeX;
newEnemy.mazeY = respawnMazeY;
enemies.push(newEnemy);
}
});
}
});
hitEnemy = true;
break;
}
}
if (hitEnemy) break;
}
// Check rose collection every 3 frames
if (LK.ticks % 3 === 0) {
for (var i = 0; i < goldenBalls.length; i++) {
var rose = goldenBalls[i];
if (!rose.collected && player.intersects(rose)) {
rose.collect();
collectedBalls++;
LK.setScore(collectedBalls);
updateScoreText();
LK.getSound('collect').play();
// Check win condition
if (collectedBalls >= totalBalls) {
LK.getSound('complete').play();
LK.setTimeout(function () {
LK.showYouWin();
}, 500);
}
}
}
}
};
2d side scroller bee. In-Game asset. 2d. High contrast. No shadows
big evil wasp. In-Game asset. 2d. High contrast. No shadows
2d side scroller rose fresh rose flower. In-Game asset. 2d. High contrast. No shadows
image 2d classic full warna tentang hamparan bunga bunga liar disuatu lembah. In-Game asset. 2d. High contrast. No shadows