/****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
flipY: 1 // flip vertically
});
// Target coordinates for the bullet
self.targetX = null;
self.targetY = null;
self.speed = 48; // pixels per frame, positive value
self.dirX = 0;
self.dirY = -1;
self.setTarget = function (tx, ty) {
// Calculate direction vector
var dx = tx - self.x;
var dy = ty - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.dirX = dx / len;
self.dirY = dy / len;
} else {
self.dirX = 0;
self.dirY = -1;
}
self.targetX = tx;
self.targetY = ty;
};
self.update = function () {
// Store lastX/lastY for event triggers
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
// Move in direction
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Rotate bullet sprite to face movement direction
if (self.dirX !== 0 || self.dirY !== 0) {
// atan2 returns angle in radians, 0 is to the right, so subtract Math.PI/2 to make 0 point up
bulletSprite.rotation = Math.atan2(self.dirY, self.dirX) - Math.PI / 2;
}
// Update lastX/lastY for next frame
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = enemySprite.width * 0.5;
self.speed = (5 + Math.random() * 3) * 0.5; // slightly faster zombies
self.update = function () {
// Move toward player
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.x += dx / len * self.speed;
self.y += dy / len * self.speed;
} else {
self.y += self.speed;
}
} else {
self.y += self.speed;
}
// Update lastX/lastY for event triggers
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// FastZombie class
var FastZombie = Container.expand(function () {
var self = Container.call(this);
var fastZombieSprite = self.attachAsset('FastZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = fastZombieSprite.width * 0.5;
self.speed = (10 + Math.random() * 4) * 0.4; // much faster than normal zombies, but slowed down
self.update = function () {
// Move toward player
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.x += dx / len * self.speed;
self.y += dy / len * self.speed;
} else {
self.y += self.speed;
}
} else {
self.y += self.speed;
}
// Update lastX/lastY for event triggers
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsSprite = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsSprite.width;
self.height = obsSprite.height;
self.speed = 12 + Math.random() * 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = playerSprite.width * 0.5;
self.lives = 1;
self.invincible = false;
self.invincibleTicks = 0;
self.shootCooldown = 0;
self.update = function () {
if (self.invincible) {
self.invincibleTicks--;
if (self.invincibleTicks <= 0) {
self.invincible = false;
playerSprite.alpha = 1;
} else {
// Flicker effect
playerSprite.alpha = LK.ticks % 10 < 5 ? 0.4 : 1;
}
}
if (self.shootCooldown > 0) self.shootCooldown--;
};
// Called when player is hit
self.hit = function () {
if (self.invincible) return;
self.lives--;
self.invincible = true;
self.invincibleTicks = 90; // 1.5 seconds
playerSprite.alpha = 0.4;
LK.effects.flashObject(self, 0xff0000, 500);
LK.getSound('hit').play();
};
// Called when player picks up a powerup
self.powerup = function () {
self.invincible = true;
self.invincibleTicks = 180; // 3 seconds
playerSprite.alpha = 0.4;
LK.getSound('powerup').play();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Game state variables
// Player character: blue box
// Enemy: red ellipse
// Obstacle: orange box
// Bullet: yellow box
// Powerup: green ellipse
// Sound effects
// Music
var player;
var bullets = [];
var enemies = [];
// Removed obstacles array as obstacles are deleted from the game
var dragNode = null;
var lastTouch = {
x: 0,
y: 0
};
var scoreTxt, livesTxt;
var spawnTick = 0;
var difficulty = 1;
var gameOver = false;
// UI
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
livesTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xFF5252
});
livesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(livesTxt);
// Start music
LK.playMusic('music');
// Create player
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Helper: update lives display
function updateLives() {
var s = '';
for (var i = 0; i < player.lives; i++) s += '♥';
livesTxt.setText(s);
}
// Helper: spawn enemy
function spawnEnemy() {
var e = new Enemy();
// Randomly pick a spawn edge: 0=top, 1=bottom, 2=left, 3=right
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
e.x = 200 + Math.random() * (2048 - 400);
e.y = -100;
} else if (edge === 1) {
// bottom
e.x = 200 + Math.random() * (2048 - 400);
e.y = 2732 + 100;
} else if (edge === 2) {
// left
e.x = -100;
e.y = 200 + Math.random() * (2732 - 400);
} else {
// right
e.x = 2048 + 100;
e.y = 200 + Math.random() * (2732 - 400);
}
enemies.push(e);
game.addChild(e);
}
// Removed spawnObstacle function as obstacles are deleted from the game
// Powerup spawn helper removed as per requirements
// Touch controls: drag player
game.down = function (x, y, obj) {
// Don't allow drag from top left 100x100
if (x < 100 && y < 100) return;
if (player && !gameOver) {
dragNode = player;
lastTouch.x = x;
lastTouch.y = y;
}
};
game.move = function (x, y, obj) {
if (dragNode && !gameOver) {
// Player movement disabled: do not update player.x or player.y
lastTouch.x = x;
lastTouch.y = y;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Tap to shoot (anywhere except top left 100x100)
game.on('down', function (x, y, obj) {
if (x < 100 && y < 100) return;
if (player && !gameOver && player.shootCooldown <= 0) {
var b = new Bullet();
b.x = player.x;
b.y = player.y - player.radius - 20;
b.setTarget(x, y); // Set bullet direction toward tap
bullets.push(b);
game.addChild(b);
player.shootCooldown = 12; // 0.2s
LK.getSound('shoot').play();
}
});
// Main game loop
game.update = function () {
if (gameOver) return;
// Increase difficulty over time
// Only increase Powerup and FastZombie spawn rates, not enemy spawn rate
var newDifficulty = 1 + Math.floor(LK.getScore() / 50) * 0.1;
if (newDifficulty !== difficulty) {
difficulty = newDifficulty;
}
// Enemy spawn rate stays constant, but we use difficulty for powerup and fastzombie rates
// Player update
player.update();
// Spawn logic
spawnTick++;
if (spawnTick % 30 === 0) {
// Limit total number of enemies, fastzombies, and powerups to 10
var totalActive = enemies.length;
var maxActive = 10;
// Enemy spawn rate is constant, not affected by difficulty
if (totalActive < maxActive && Math.random() < 0.5) {
spawnEnemy();
totalActive++;
}
if (totalActive < maxActive && Math.random() < 0.10) {
spawnEnemy();
totalActive++;
}
// FastZombie: 7% base chance, scales with difficulty, only after score >= 25
// After score > 50, FastZombie spawn chance is 50%
var fastZombieChance = 0.07 * difficulty;
if (LK.getScore() > 50) {
fastZombieChance = 0.5;
}
if (totalActive < maxActive && LK.getScore() >= 25 && Math.random() < fastZombieChance) {
var fz = new FastZombie();
// Randomly pick a spawn edge: 0=top, 1=bottom, 2=left, 3=right
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
fz.x = 200 + Math.random() * (2048 - 400);
fz.y = -100;
} else if (edge === 1) {
// bottom
fz.x = 200 + Math.random() * (2048 - 400);
fz.y = 2732 + 100;
} else if (edge === 2) {
// left
fz.x = -100;
fz.y = 200 + Math.random() * (2732 - 400);
} else {
// right
fz.x = 2048 + 100;
fz.y = 200 + Math.random() * (2732 - 400);
}
enemies.push(fz);
game.addChild(fz);
totalActive++;
}
}
// Bulletpowerup timer logic
// bulletpowerupActive is now permanent once collected; timer logic removed
// Bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
if (b.y < -60) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Bullet hits enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
if (LK.getScore() >= 150) {
gameOver = true;
LK.showYouWin();
return;
}
LK.getSound('enemyDown').play();
e.destroy();
enemies.splice(j, 1);
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
if (e.y > 2732 + 100) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy hits player
if (e.intersects(player)) {
if (!player.invincible) {
player.hit();
updateLives();
if (player.lives <= 0) {
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
e.destroy();
enemies.splice(i, 1);
}
}
// Obstacles removed from game
};
// Score display
scoreTxt.setText(LK.getScore());
updateLives(); /****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
flipY: 1 // flip vertically
});
// Target coordinates for the bullet
self.targetX = null;
self.targetY = null;
self.speed = 48; // pixels per frame, positive value
self.dirX = 0;
self.dirY = -1;
self.setTarget = function (tx, ty) {
// Calculate direction vector
var dx = tx - self.x;
var dy = ty - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.dirX = dx / len;
self.dirY = dy / len;
} else {
self.dirX = 0;
self.dirY = -1;
}
self.targetX = tx;
self.targetY = ty;
};
self.update = function () {
// Store lastX/lastY for event triggers
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
// Move in direction
self.x += self.dirX * self.speed;
self.y += self.dirY * self.speed;
// Rotate bullet sprite to face movement direction
if (self.dirX !== 0 || self.dirY !== 0) {
// atan2 returns angle in radians, 0 is to the right, so subtract Math.PI/2 to make 0 point up
bulletSprite.rotation = Math.atan2(self.dirY, self.dirX) - Math.PI / 2;
}
// Update lastX/lastY for next frame
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = enemySprite.width * 0.5;
self.speed = (5 + Math.random() * 3) * 0.5; // slightly faster zombies
self.update = function () {
// Move toward player
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.x += dx / len * self.speed;
self.y += dy / len * self.speed;
} else {
self.y += self.speed;
}
} else {
self.y += self.speed;
}
// Update lastX/lastY for event triggers
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// FastZombie class
var FastZombie = Container.expand(function () {
var self = Container.call(this);
var fastZombieSprite = self.attachAsset('FastZombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = fastZombieSprite.width * 0.5;
self.speed = (10 + Math.random() * 4) * 0.4; // much faster than normal zombies, but slowed down
self.update = function () {
// Move toward player
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
if (player) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.x += dx / len * self.speed;
self.y += dy / len * self.speed;
} else {
self.y += self.speed;
}
} else {
self.y += self.speed;
}
// Update lastX/lastY for event triggers
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsSprite = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsSprite.width;
self.height = obsSprite.height;
self.speed = 12 + Math.random() * 8;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = playerSprite.width * 0.5;
self.lives = 1;
self.invincible = false;
self.invincibleTicks = 0;
self.shootCooldown = 0;
self.update = function () {
if (self.invincible) {
self.invincibleTicks--;
if (self.invincibleTicks <= 0) {
self.invincible = false;
playerSprite.alpha = 1;
} else {
// Flicker effect
playerSprite.alpha = LK.ticks % 10 < 5 ? 0.4 : 1;
}
}
if (self.shootCooldown > 0) self.shootCooldown--;
};
// Called when player is hit
self.hit = function () {
if (self.invincible) return;
self.lives--;
self.invincible = true;
self.invincibleTicks = 90; // 1.5 seconds
playerSprite.alpha = 0.4;
LK.effects.flashObject(self, 0xff0000, 500);
LK.getSound('hit').play();
};
// Called when player picks up a powerup
self.powerup = function () {
self.invincible = true;
self.invincibleTicks = 180; // 3 seconds
playerSprite.alpha = 0.4;
LK.getSound('powerup').play();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Game state variables
// Player character: blue box
// Enemy: red ellipse
// Obstacle: orange box
// Bullet: yellow box
// Powerup: green ellipse
// Sound effects
// Music
var player;
var bullets = [];
var enemies = [];
// Removed obstacles array as obstacles are deleted from the game
var dragNode = null;
var lastTouch = {
x: 0,
y: 0
};
var scoreTxt, livesTxt;
var spawnTick = 0;
var difficulty = 1;
var gameOver = false;
// UI
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
livesTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xFF5252
});
livesTxt.anchor.set(1, 0);
LK.gui.topRight.addChild(livesTxt);
// Start music
LK.playMusic('music');
// Create player
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// Helper: update lives display
function updateLives() {
var s = '';
for (var i = 0; i < player.lives; i++) s += '♥';
livesTxt.setText(s);
}
// Helper: spawn enemy
function spawnEnemy() {
var e = new Enemy();
// Randomly pick a spawn edge: 0=top, 1=bottom, 2=left, 3=right
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
e.x = 200 + Math.random() * (2048 - 400);
e.y = -100;
} else if (edge === 1) {
// bottom
e.x = 200 + Math.random() * (2048 - 400);
e.y = 2732 + 100;
} else if (edge === 2) {
// left
e.x = -100;
e.y = 200 + Math.random() * (2732 - 400);
} else {
// right
e.x = 2048 + 100;
e.y = 200 + Math.random() * (2732 - 400);
}
enemies.push(e);
game.addChild(e);
}
// Removed spawnObstacle function as obstacles are deleted from the game
// Powerup spawn helper removed as per requirements
// Touch controls: drag player
game.down = function (x, y, obj) {
// Don't allow drag from top left 100x100
if (x < 100 && y < 100) return;
if (player && !gameOver) {
dragNode = player;
lastTouch.x = x;
lastTouch.y = y;
}
};
game.move = function (x, y, obj) {
if (dragNode && !gameOver) {
// Player movement disabled: do not update player.x or player.y
lastTouch.x = x;
lastTouch.y = y;
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Tap to shoot (anywhere except top left 100x100)
game.on('down', function (x, y, obj) {
if (x < 100 && y < 100) return;
if (player && !gameOver && player.shootCooldown <= 0) {
var b = new Bullet();
b.x = player.x;
b.y = player.y - player.radius - 20;
b.setTarget(x, y); // Set bullet direction toward tap
bullets.push(b);
game.addChild(b);
player.shootCooldown = 12; // 0.2s
LK.getSound('shoot').play();
}
});
// Main game loop
game.update = function () {
if (gameOver) return;
// Increase difficulty over time
// Only increase Powerup and FastZombie spawn rates, not enemy spawn rate
var newDifficulty = 1 + Math.floor(LK.getScore() / 50) * 0.1;
if (newDifficulty !== difficulty) {
difficulty = newDifficulty;
}
// Enemy spawn rate stays constant, but we use difficulty for powerup and fastzombie rates
// Player update
player.update();
// Spawn logic
spawnTick++;
if (spawnTick % 30 === 0) {
// Limit total number of enemies, fastzombies, and powerups to 10
var totalActive = enemies.length;
var maxActive = 10;
// Enemy spawn rate is constant, not affected by difficulty
if (totalActive < maxActive && Math.random() < 0.5) {
spawnEnemy();
totalActive++;
}
if (totalActive < maxActive && Math.random() < 0.10) {
spawnEnemy();
totalActive++;
}
// FastZombie: 7% base chance, scales with difficulty, only after score >= 25
// After score > 50, FastZombie spawn chance is 50%
var fastZombieChance = 0.07 * difficulty;
if (LK.getScore() > 50) {
fastZombieChance = 0.5;
}
if (totalActive < maxActive && LK.getScore() >= 25 && Math.random() < fastZombieChance) {
var fz = new FastZombie();
// Randomly pick a spawn edge: 0=top, 1=bottom, 2=left, 3=right
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
fz.x = 200 + Math.random() * (2048 - 400);
fz.y = -100;
} else if (edge === 1) {
// bottom
fz.x = 200 + Math.random() * (2048 - 400);
fz.y = 2732 + 100;
} else if (edge === 2) {
// left
fz.x = -100;
fz.y = 200 + Math.random() * (2732 - 400);
} else {
// right
fz.x = 2048 + 100;
fz.y = 200 + Math.random() * (2732 - 400);
}
enemies.push(fz);
game.addChild(fz);
totalActive++;
}
}
// Bulletpowerup timer logic
// bulletpowerupActive is now permanent once collected; timer logic removed
// Bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
if (b.y < -60) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Bullet hits enemy
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
LK.setScore(LK.getScore() + 1);
scoreTxt.setText(LK.getScore());
if (LK.getScore() >= 150) {
gameOver = true;
LK.showYouWin();
return;
}
LK.getSound('enemyDown').play();
e.destroy();
enemies.splice(j, 1);
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
if (e.y > 2732 + 100) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy hits player
if (e.intersects(player)) {
if (!player.invincible) {
player.hit();
updateLives();
if (player.lives <= 0) {
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
e.destroy();
enemies.splice(i, 1);
}
}
// Obstacles removed from game
};
// Score display
scoreTxt.setText(LK.getScore());
updateLives();
soldier pixel. In-Game asset. 2d. High contrast. No shadows
pixel ammo. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
pixel brick wall. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
blood pixel. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
White ghost with red eyes pixel. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
blue ghost pixel. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
White ghost pixel. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat