Code edit (1 edits merged)
Please save this source code
User prompt
Ricochet Zombie Shooter
Initial prompt
create a Design a 2D physics-based puzzle shooter game where the player controls a lone survivor during a zombie apocalypse. The hero is equipped with a limited number of bullets per level and must strategically eliminate all zombies on screen using ricocheting shots, explosive barrels, falling traps, and other environmental objects. Each level is a compact battlefield filled with obstacles, bounce surfaces, movable crates, and zombie formations. Zombies stand in creative, challenging positions — on ledges, stacked crates, or hiding behind barriers — requiring the player to think critically and aim precisely. Bullets bounce off walls, ceilings, and floors, encouraging creative, indirect shots to maximize damage with minimal ammo. Game features to include: Hundreds of levels with increasing difficulty Ammo-limited challenges (1–5 bullets max per level) Special bullet types (explosive, splitting, fire, etc.) Diverse zombie types (slow walkers, shielded zombies, explosive zombies) Chain reaction mechanics (e.g., shoot a plank that drops a barrel on zombies) Stylized cartoon graphics with smooth physics animations Optional star-rating system per level based on efficiency Background themes: Abandoned cities, underground bunkers, haunted carnivals, snowy towns Fun sound effects, ragdoll zombie deaths, and comedy-style visuals The vibe should be casual, humorous, and addictive — easy to pick up but tough to master. Visuals should feel light-hearted yet satisfying, similar to “Angry Birds meets Zombies.”
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Barrel = Container.expand(function () {
var self = Container.call(this);
var barrelGraphics = self.attachAsset('barrel', {
anchorX: 0.5,
anchorY: 0.5
});
self.exploded = false;
self.explosionRadius = 120;
self.explode = function () {
if (self.exploded) return;
self.exploded = true;
LK.getSound('explosion').play();
LK.effects.flashObject(self, 0xFF4500, 800);
// Damage nearby zombies
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
if (distance <= self.explosionRadius && !zombie.isDead) {
zombie.takeDamage();
}
}
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 800,
onFinish: function onFinish() {
// Remove from barrels array
for (var j = 0; j < barrels.length; j++) {
if (barrels[j] === self) {
barrels.splice(j, 1);
break;
}
}
}
});
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 15;
self.bounces = 0;
self.maxBounces = 5;
self.active = true;
self.update = function () {
if (!self.active) return;
self.x += self.velocityX;
self.y += self.velocityY;
// Check wall collisions and bounce
if (self.x <= 10 && self.velocityX < 0) {
self.velocityX = -self.velocityX;
self.bounces++;
}
if (self.x >= 2038 && self.velocityX > 0) {
self.velocityX = -self.velocityX;
self.bounces++;
}
if (self.y <= 10 && self.velocityY < 0) {
self.velocityY = -self.velocityY;
self.bounces++;
}
if (self.y >= 2722 && self.velocityY > 0) {
self.velocityY = -self.velocityY;
self.bounces++;
}
// Check wall object collisions
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
if (self.intersects(wall)) {
// Simple bounce logic - reverse direction
var centerX = wall.x + wall.width / 2;
var centerY = wall.y + wall.height / 2;
if (Math.abs(self.x - centerX) > Math.abs(self.y - centerY)) {
self.velocityX = -self.velocityX;
} else {
self.velocityY = -self.velocityY;
}
self.bounces++;
break;
}
}
// Remove bullet after too many bounces
if (self.bounces > self.maxBounces) {
self.active = false;
}
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieGraphics = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 1;
self.isDead = false;
self.takeDamage = function () {
if (self.isDead) return;
self.health--;
if (self.health <= 0) {
self.isDead = true;
LK.getSound('zombieDeath').play();
LK.effects.flashObject(self, 0xFF0000, 500);
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 500,
onFinish: function onFinish() {
zombiesKilled++;
checkWinCondition();
}
});
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2F4F4F
});
/****
* Game Code
****/
var player;
var bullets = [];
var zombies = [];
var walls = [];
var barrels = [];
var bulletsRemaining = 3;
var totalZombies = 0;
var zombiesKilled = 0;
var gameWon = false;
var gameLost = false;
var aimLine = null;
var aiming = false;
var aimStartX = 0;
var aimStartY = 0;
// UI Elements
var bulletsText = new Text2('Bullets: 3', {
size: 80,
fill: 0xFFFFFF
});
bulletsText.anchor.set(0, 0);
bulletsText.x = 120;
bulletsText.y = 50;
LK.gui.topLeft.addChild(bulletsText);
var zombiesText = new Text2('Zombies: 0', {
size: 80,
fill: 0xFFFFFF
});
zombiesText.anchor.set(1, 0);
LK.gui.topRight.addChild(zombiesText);
// Create player
player = game.addChild(new Container());
var playerGraphics = player.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
player.x = 200;
player.y = 2500;
// Create level walls
function createWall(x, y, width, height) {
var wall = game.addChild(new Container());
var wallGraphics = wall.attachAsset('wall', {
anchorX: 0,
anchorY: 0,
width: width,
height: height
});
wall.x = x;
wall.y = y;
wall.width = width;
wall.height = height;
walls.push(wall);
return wall;
}
// Create level layout - simple first level
createWall(800, 1800, 400, 50);
createWall(1400, 1400, 300, 50);
createWall(600, 1000, 200, 50);
// Create zombies
function createZombie(x, y) {
var zombie = game.addChild(new Zombie());
zombie.x = x;
zombie.y = y;
zombies.push(zombie);
totalZombies++;
}
createZombie(900, 1750);
createZombie(1500, 1350);
createZombie(700, 950);
// Create barrels
function createBarrel(x, y) {
var barrel = game.addChild(new Barrel());
barrel.x = x;
barrel.y = y;
barrels.push(barrel);
}
createBarrel(1100, 1750);
// Update UI
zombiesText.setText('Zombies: ' + totalZombies);
function checkWinCondition() {
if (zombiesKilled >= totalZombies && !gameWon) {
gameWon = true;
LK.setTimeout(function () {
LK.showYouWin();
}, 1000);
}
}
function checkLoseCondition() {
if (bulletsRemaining <= 0 && bullets.length === 0 && zombiesKilled < totalZombies && !gameLost) {
gameLost = true;
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
}
}
function fireBullet(targetX, targetY) {
if (bulletsRemaining <= 0 || aiming === false) return;
var bullet = game.addChild(new Bullet());
bullet.x = player.x;
bullet.y = player.y;
var deltaX = targetX - player.x;
var deltaY = targetY - player.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
bullet.velocityX = deltaX / distance * bullet.speed;
bullet.velocityY = deltaY / distance * bullet.speed;
bullets.push(bullet);
bulletsRemaining--;
bulletsText.setText('Bullets: ' + bulletsRemaining);
LK.getSound('shoot').play();
aiming = false;
}
// Event handlers
game.down = function (x, y, obj) {
if (gameWon || gameLost) return;
aiming = true;
aimStartX = x;
aimStartY = y;
};
game.move = function (x, y, obj) {
if (!aiming || gameWon || gameLost) return;
// Visual feedback for aiming could be added here
};
game.up = function (x, y, obj) {
if (!aiming || gameWon || gameLost) return;
fireBullet(x, y);
};
game.update = function () {
if (gameWon || gameLost) return;
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet.active) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check zombie collisions
for (var j = 0; j < zombies.length; j++) {
var zombie = zombies[j];
if (!zombie.isDead && bullet.intersects(zombie)) {
zombie.takeDamage();
bullet.active = false;
break;
}
}
// Check barrel collisions
for (var k = 0; k < barrels.length; k++) {
var barrel = barrels[k];
if (!barrel.exploded && bullet.intersects(barrel)) {
barrel.explode();
bullet.active = false;
break;
}
}
// Remove bullets that go off screen
if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.active = false;
}
}
checkLoseCondition();
}; ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,305 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+var Barrel = Container.expand(function () {
+ var self = Container.call(this);
+ var barrelGraphics = self.attachAsset('barrel', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.exploded = false;
+ self.explosionRadius = 120;
+ self.explode = function () {
+ if (self.exploded) return;
+ self.exploded = true;
+ LK.getSound('explosion').play();
+ LK.effects.flashObject(self, 0xFF4500, 800);
+ // Damage nearby zombies
+ for (var i = 0; i < zombies.length; i++) {
+ var zombie = zombies[i];
+ var distance = Math.sqrt(Math.pow(zombie.x - self.x, 2) + Math.pow(zombie.y - self.y, 2));
+ if (distance <= self.explosionRadius && !zombie.isDead) {
+ zombie.takeDamage();
+ }
+ }
+ tween(self, {
+ alpha: 0,
+ scaleX: 2,
+ scaleY: 2
+ }, {
+ duration: 800,
+ onFinish: function onFinish() {
+ // Remove from barrels array
+ for (var j = 0; j < barrels.length; j++) {
+ if (barrels[j] === self) {
+ barrels.splice(j, 1);
+ break;
+ }
+ }
+ }
+ });
+ };
+ return self;
+});
+var Bullet = Container.expand(function () {
+ var self = Container.call(this);
+ var bulletGraphics = self.attachAsset('bullet', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.velocityX = 0;
+ self.velocityY = 0;
+ self.speed = 15;
+ self.bounces = 0;
+ self.maxBounces = 5;
+ self.active = true;
+ self.update = function () {
+ if (!self.active) return;
+ self.x += self.velocityX;
+ self.y += self.velocityY;
+ // Check wall collisions and bounce
+ if (self.x <= 10 && self.velocityX < 0) {
+ self.velocityX = -self.velocityX;
+ self.bounces++;
+ }
+ if (self.x >= 2038 && self.velocityX > 0) {
+ self.velocityX = -self.velocityX;
+ self.bounces++;
+ }
+ if (self.y <= 10 && self.velocityY < 0) {
+ self.velocityY = -self.velocityY;
+ self.bounces++;
+ }
+ if (self.y >= 2722 && self.velocityY > 0) {
+ self.velocityY = -self.velocityY;
+ self.bounces++;
+ }
+ // Check wall object collisions
+ for (var i = 0; i < walls.length; i++) {
+ var wall = walls[i];
+ if (self.intersects(wall)) {
+ // Simple bounce logic - reverse direction
+ var centerX = wall.x + wall.width / 2;
+ var centerY = wall.y + wall.height / 2;
+ if (Math.abs(self.x - centerX) > Math.abs(self.y - centerY)) {
+ self.velocityX = -self.velocityX;
+ } else {
+ self.velocityY = -self.velocityY;
+ }
+ self.bounces++;
+ break;
+ }
+ }
+ // Remove bullet after too many bounces
+ if (self.bounces > self.maxBounces) {
+ self.active = false;
+ }
+ };
+ return self;
+});
+var Zombie = Container.expand(function () {
+ var self = Container.call(this);
+ var zombieGraphics = self.attachAsset('zombie', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.health = 1;
+ self.isDead = false;
+ self.takeDamage = function () {
+ if (self.isDead) return;
+ self.health--;
+ if (self.health <= 0) {
+ self.isDead = true;
+ LK.getSound('zombieDeath').play();
+ LK.effects.flashObject(self, 0xFF0000, 500);
+ tween(self, {
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 500,
+ onFinish: function onFinish() {
+ zombiesKilled++;
+ checkWinCondition();
+ }
+ });
+ }
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x2F4F4F
+});
+
+/****
+* Game Code
+****/
+var player;
+var bullets = [];
+var zombies = [];
+var walls = [];
+var barrels = [];
+var bulletsRemaining = 3;
+var totalZombies = 0;
+var zombiesKilled = 0;
+var gameWon = false;
+var gameLost = false;
+var aimLine = null;
+var aiming = false;
+var aimStartX = 0;
+var aimStartY = 0;
+// UI Elements
+var bulletsText = new Text2('Bullets: 3', {
+ size: 80,
+ fill: 0xFFFFFF
+});
+bulletsText.anchor.set(0, 0);
+bulletsText.x = 120;
+bulletsText.y = 50;
+LK.gui.topLeft.addChild(bulletsText);
+var zombiesText = new Text2('Zombies: 0', {
+ size: 80,
+ fill: 0xFFFFFF
+});
+zombiesText.anchor.set(1, 0);
+LK.gui.topRight.addChild(zombiesText);
+// Create player
+player = game.addChild(new Container());
+var playerGraphics = player.attachAsset('player', {
+ anchorX: 0.5,
+ anchorY: 0.5
+});
+player.x = 200;
+player.y = 2500;
+// Create level walls
+function createWall(x, y, width, height) {
+ var wall = game.addChild(new Container());
+ var wallGraphics = wall.attachAsset('wall', {
+ anchorX: 0,
+ anchorY: 0,
+ width: width,
+ height: height
+ });
+ wall.x = x;
+ wall.y = y;
+ wall.width = width;
+ wall.height = height;
+ walls.push(wall);
+ return wall;
+}
+// Create level layout - simple first level
+createWall(800, 1800, 400, 50);
+createWall(1400, 1400, 300, 50);
+createWall(600, 1000, 200, 50);
+// Create zombies
+function createZombie(x, y) {
+ var zombie = game.addChild(new Zombie());
+ zombie.x = x;
+ zombie.y = y;
+ zombies.push(zombie);
+ totalZombies++;
+}
+createZombie(900, 1750);
+createZombie(1500, 1350);
+createZombie(700, 950);
+// Create barrels
+function createBarrel(x, y) {
+ var barrel = game.addChild(new Barrel());
+ barrel.x = x;
+ barrel.y = y;
+ barrels.push(barrel);
+}
+createBarrel(1100, 1750);
+// Update UI
+zombiesText.setText('Zombies: ' + totalZombies);
+function checkWinCondition() {
+ if (zombiesKilled >= totalZombies && !gameWon) {
+ gameWon = true;
+ LK.setTimeout(function () {
+ LK.showYouWin();
+ }, 1000);
+ }
+}
+function checkLoseCondition() {
+ if (bulletsRemaining <= 0 && bullets.length === 0 && zombiesKilled < totalZombies && !gameLost) {
+ gameLost = true;
+ LK.setTimeout(function () {
+ LK.showGameOver();
+ }, 1000);
+ }
+}
+function fireBullet(targetX, targetY) {
+ if (bulletsRemaining <= 0 || aiming === false) return;
+ var bullet = game.addChild(new Bullet());
+ bullet.x = player.x;
+ bullet.y = player.y;
+ var deltaX = targetX - player.x;
+ var deltaY = targetY - player.y;
+ var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+ bullet.velocityX = deltaX / distance * bullet.speed;
+ bullet.velocityY = deltaY / distance * bullet.speed;
+ bullets.push(bullet);
+ bulletsRemaining--;
+ bulletsText.setText('Bullets: ' + bulletsRemaining);
+ LK.getSound('shoot').play();
+ aiming = false;
+}
+// Event handlers
+game.down = function (x, y, obj) {
+ if (gameWon || gameLost) return;
+ aiming = true;
+ aimStartX = x;
+ aimStartY = y;
+};
+game.move = function (x, y, obj) {
+ if (!aiming || gameWon || gameLost) return;
+ // Visual feedback for aiming could be added here
+};
+game.up = function (x, y, obj) {
+ if (!aiming || gameWon || gameLost) return;
+ fireBullet(x, y);
+};
+game.update = function () {
+ if (gameWon || gameLost) return;
+ // Update bullets
+ for (var i = bullets.length - 1; i >= 0; i--) {
+ var bullet = bullets[i];
+ if (!bullet.active) {
+ bullet.destroy();
+ bullets.splice(i, 1);
+ continue;
+ }
+ // Check zombie collisions
+ for (var j = 0; j < zombies.length; j++) {
+ var zombie = zombies[j];
+ if (!zombie.isDead && bullet.intersects(zombie)) {
+ zombie.takeDamage();
+ bullet.active = false;
+ break;
+ }
+ }
+ // Check barrel collisions
+ for (var k = 0; k < barrels.length; k++) {
+ var barrel = barrels[k];
+ if (!barrel.exploded && bullet.intersects(barrel)) {
+ barrel.explode();
+ bullet.active = false;
+ break;
+ }
+ }
+ // Remove bullets that go off screen
+ if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
+ bullet.active = false;
+ }
+ }
+ checkLoseCondition();
+};
\ No newline at end of file