User prompt
Undo the last thing
User prompt
Whenever they shoot make them walk a bit faster
User prompt
Make code so that wherever the player shoots that's where they go
User prompt
Make code so zombies don't do as much damage
User prompt
I didn't say make them instantly die make it like it was
User prompt
Can you code it so you can't get stuck Inside zombies so you die instantly please
User prompt
Can you make code for the player to be able to choose where to move
User prompt
Can you write code for it
Code edit (1 edits merged)
Please save this source code
User prompt
Zombie Bros: Apocalypse Duo
Initial prompt
Can you make a game about two brothers battling zombies in the apocalypse and you only control one brother. Make it feel like it is multiplayer with the other brother and also make it in an 8-bit type style please.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ var AmmoDisplay = Container.expand(function () { var self = Container.call(this); self.indicators = []; self.player = null; self.init = function (playerRef) { self.player = playerRef; // Clear existing indicators for (var i = 0; i < self.indicators.length; i++) { self.removeChild(self.indicators[i]); } self.indicators = []; // Create new indicators for (var i = 0; i < self.player.maxAmmo; i++) { var indicator = LK.getAsset('ammoIndicator', { anchorX: 0.5, anchorY: 0.5, x: i * 20, y: 0 }); self.addChild(indicator); self.indicators.push(indicator); } }; self.update = function () { if (!self.player) { return; } // Update visibility based on current ammo for (var i = 0; i < self.indicators.length; i++) { self.indicators[i].visible = i < self.player.ammo; // Fade effect when reloading if (self.player.isReloading) { var reloadProgress = 1 - self.player.reloadCounter / self.player.reloadTime; if (i < Math.floor(reloadProgress * self.player.maxAmmo)) { self.indicators[i].visible = true; self.indicators[i].alpha = 0.5; } else { self.indicators[i].visible = false; } } else { self.indicators[i].alpha = 1; } } }; return self; }); var Blood = Container.expand(function () { var self = Container.call(this); var bloodGraphics = self.attachAsset('blood', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 }); self.lifespan = 60; // Frames self.init = function (x, y) { self.x = x; self.y = y; self.scale.x = 0.5 + Math.random() * 0.5; self.scale.y = 0.5 + Math.random() * 0.5; self.rotation = Math.random() * Math.PI * 2; self.alpha = 0.8; }; self.update = function () { self.lifespan--; if (self.lifespan <= 20) { self.alpha = self.lifespan / 20 * 0.8; } if (self.lifespan <= 0) { self.shouldDestroy = true; } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.damage = 25; self.direction = { x: 0, y: 0 }; self.shooter = null; self.update = function () { self.x += self.direction.x * self.speed; self.y += self.direction.y * self.speed; // Destroy bullet if it goes off screen if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) { self.shouldDestroy = true; } }; self.fire = function (startX, startY, targetX, targetY, shooterRef) { self.x = startX; self.y = startY; self.shooter = shooterRef; // Calculate direction var dx = targetX - startX; var dy = targetY - startY; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.direction.x = dx / distance; self.direction.y = dy / distance; } }; return self; }); var HealthBar = Container.expand(function () { var self = Container.call(this); var background = self.attachAsset('healthBarBackground', { anchorX: 0, anchorY: 0 }); var bar = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0, x: 2, y: 2 }); var playerText = new Text2("P1", { size: 20, fill: 0xFFFFFF }); playerText.anchor.set(0.5); playerText.x = background.width / 2; playerText.y = background.height / 2; self.addChild(playerText); self.player = null; self.init = function (playerRef, isPlayer1) { self.player = playerRef; playerText.setText(isPlayer1 ? "P1" : "P2"); }; self.update = function () { if (!self.player) { return; } var healthPercent = self.player.health / self.player.maxHealth; bar.scale.x = healthPercent; }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); self.isPlayer1 = true; // Will be set when initializing var graphicsId = 'player1'; // Default self.health = 100; self.maxHealth = 100; self.moveSpeed = 5; self.fireRate = 15; // Frames between shots self.fireCounter = 0; self.ammo = 12; self.maxAmmo = 12; self.reloadTime = 120; // 2 seconds at 60fps self.reloadCounter = 0; self.isReloading = false; self.isDead = false; self.lastMoveDirection = { x: 0, y: -1 }; // Default facing up self.init = function (isFirstPlayer) { self.isPlayer1 = isFirstPlayer; graphicsId = isFirstPlayer ? 'player1' : 'player2'; if (self.graphics) { self.removeChild(self.graphics); } self.graphics = self.attachAsset(graphicsId, { anchorX: 0.5, anchorY: 0.5, tint: isFirstPlayer ? 0xff0000 : 0x0000ff // Red for player1, Blue for player2 }); self.health = self.maxHealth; var nameTag = new Text2(isFirstPlayer ? "Player 1" : "Player 2", { // Name tag size: 20, fill: 0xFFFFFF }); nameTag.anchor.set(0.5); nameTag.y = -self.graphics.height / 2 - 10; // Position above the player self.addChild(nameTag); self.ammo = self.maxAmmo; self.fireCounter = 0; self.reloadCounter = 0; self.isReloading = false; self.isDead = false; }; self.takeDamage = function (amount) { if (self.isDead) { return; } self.health -= amount; if (self.health <= 0) { self.health = 0; self.die(); } else { // Flash red when hit LK.effects.flashObject(self, self.isPlayer1 ? 0xff0000 : 0x0000ff, 300); // Red flash for player1, Blue flash for player2 LK.getSound('playerHit').play(); } }; self.die = function () { self.isDead = true; self.alpha = 0.5; // Flash red when dead LK.effects.flashObject(self, 0xff0000, 1000); }; self.shoot = function (targetX, targetY) { if (self.isDead || self.isReloading) { return null; } if (self.ammo <= 0) { self.reload(); return null; } if (self.fireCounter <= 0) { // Create bullet var bullet = new Bullet(); bullet.fire(self.x, self.y, targetX, targetY, self); // Update state self.ammo--; self.fireCounter = self.fireRate; // Keep track of facing direction based on shot var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.lastMoveDirection.x = dx / distance; self.lastMoveDirection.y = dy / distance; } // Play sound LK.getSound('shoot').play(); if (self.isPlayer1) { LK.getSound('player1Shoot').play(); // Unique sound for player 1 } else { LK.getSound('player2Shoot').play(); // Unique sound for player 2 } return bullet; } return null; }; self.reload = function () { if (self.isDead || self.isReloading || self.ammo === self.maxAmmo) { return; } self.isReloading = true; self.reloadCounter = self.reloadTime; LK.getSound('reload').play(); }; self.move = function (dx, dy) { if (self.isDead) { return; } // If moving, update last move direction if (dx !== 0 || dy !== 0) { var length = Math.sqrt(dx * dx + dy * dy); if (length > 0) { self.lastMoveDirection.x = dx / length; self.lastMoveDirection.y = dy / length; } } // Apply movement var newX = self.x + dx * self.moveSpeed; var newY = self.y + dy * self.moveSpeed; // Constrain to game bounds with a margin var margin = 50; newX = Math.max(margin, Math.min(2048 - margin, newX)); newY = Math.max(margin, Math.min(2732 - margin, newY)); self.x = newX; self.y = newY; }; self.update = function () { // Update reload state if (self.isReloading) { self.reloadCounter--; if (self.reloadCounter <= 0) { self.isReloading = false; self.ammo = self.maxAmmo; } } // Update fire rate counter if (self.fireCounter > 0) { self.fireCounter--; } // AI behavior for Player 2 if (!self.isPlayer1 && !self.isDead) { self.updateAI(); } }; self.updateAI = function () { // Find the nearest zombie var nearestZombie = null; var nearestDistance = Number.MAX_VALUE; for (var i = 0; i < zombies.length; i++) { var zombie = zombies[i]; var distance = getDistance(self, zombie); if (distance < nearestDistance) { nearestDistance = distance; nearestZombie = zombie; } } // Move and shoot logic if (nearestZombie) { // If zombie is close, move away from it if (nearestDistance < 300) { var dx = self.x - nearestZombie.x; var dy = self.y - nearestZombie.y; var length = Math.sqrt(dx * dx + dy * dy); if (length > 0) { dx /= length; dy /= length; self.move(dx, dy); } } // Otherwise, stay close to player 1 else { var distToPlayer1 = getDistance(self, player1); if (distToPlayer1 > 300) { var dx = player1.x - self.x; var dy = player1.y - self.y; var length = Math.sqrt(dx * dx + dy * dy); if (length > 0) { dx /= length; dy /= length; self.move(dx, dy); } } } // Shoot at nearest zombie if not reloading and it's in range if (!self.isReloading && nearestDistance < 600) { var bullet = self.shoot(nearestZombie.x, nearestZombie.y); if (bullet) { bullets.push(bullet); game.addChild(bullet); } } // Reload if low on ammo and no zombies nearby if (self.ammo <= self.maxAmmo / 4 && nearestDistance > 400) { self.reload(); } } }; 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 = 100; self.moveSpeed = 2; self.damage = 5; self.attackRate = 60; // 1 second between attacks self.attackCounter = 0; self.isDead = false; self.init = function (x, y, moveSpeed, health) { self.x = x; self.y = y; self.moveSpeed = moveSpeed || 2; self.health = health || 100; }; self.takeDamage = function (amount) { if (self.isDead) { return; } self.health -= amount; // Flash red when hit LK.effects.flashObject(self, 0xff0000, 150); LK.getSound('zombieHit').play(); if (self.health <= 0) { self.die(); } }; self.die = function () { self.isDead = true; self.shouldDestroy = true; // Create blood splatter createBlood(self.x, self.y); // Increase score currentScore += 10; updateScoreText(); }; self.attack = function (player) { if (self.isDead || self.attackCounter > 0) { return; } player.takeDamage(self.damage); self.attackCounter = self.attackRate; }; self.update = function () { if (self.isDead) { return; } // Update attack counter if (self.attackCounter > 0) { self.attackCounter--; } // Target the nearest player that's alive var target = null; if (!player1.isDead && !player2.isDead) { // Target closest player var dist1 = getDistance(self, player1); var dist2 = getDistance(self, player2); target = dist1 < dist2 ? player1 : player2; } else if (!player1.isDead) { target = player1; } else if (!player2.isDead) { target = player2; } if (target) { // Move towards target var dx = target.x - self.x; var dy = target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var moveX = dx / distance * self.moveSpeed; var moveY = dy / distance * self.moveSpeed; self.x += moveX; self.y += moveY; } // Attack if close enough if (distance < 80) { self.attack(target); // Push the player out if they are stuck inside the zombie if (distance < 40) { var pushX = dx / distance * (40 - distance); var pushY = dy / distance * (40 - distance); target.x += pushX; target.y += pushY; } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Game state variables var player1; var player2; var player1HealthBar; var player2HealthBar; var player1AmmoDisplay; var player2AmmoDisplay; var bullets = []; var zombies = []; var bloodSplatters = []; var currentWave = 0; var zombiesInWave = 0; var zombiesLeft = 0; var currentScore = 0; var gameActive = false; var waveText; var scoreText; var highScoreText; var gameStartText; var groundTiles = []; // Helper functions function getDistance(obj1, obj2) { var dx = obj1.x - obj2.x; var dy = obj1.y - obj2.y; return Math.sqrt(dx * dx + dy * dy); } function createGround() { var tileSize = 128; var tilesX = Math.ceil(2048 / tileSize); var tilesY = Math.ceil(2732 / tileSize); for (var y = 0; y < tilesY; y++) { for (var x = 0; x < tilesX; x++) { var tile = LK.getAsset('groundTile', { anchorX: 0, anchorY: 0, x: x * tileSize, y: y * tileSize, alpha: 0.3 }); // Add slight randomness to tiles tile.tint = 0x666666 + Math.floor(Math.random() * 0x111111); groundTiles.push(tile); game.addChild(tile); } } } function updateScoreText() { scoreText.setText("Score: " + currentScore); // Update high score if needed if (currentScore > storage.highScore) { storage.highScore = currentScore; highScoreText.setText("High Score: " + storage.highScore); } } function createBlood(x, y) { for (var i = 0; i < 3; i++) { var blood = new Blood(); blood.init(x + (Math.random() * 60 - 30), y + (Math.random() * 60 - 30)); bloodSplatters.push(blood); game.addChild(blood); } } function startGame() { // Reset game state gameActive = true; currentWave = 0; currentScore = 0; // Clear any existing entities clearEntities(); // Create players player1 = new Player(); player1.init(true); player1.x = 2048 / 2 - 150; player1.y = 2732 / 2; game.addChild(player1); player2 = new Player(); player2.init(false); player2.x = 2048 / 2 + 150; player2.y = 2732 / 2; game.addChild(player2); // Create health bars player1HealthBar = new HealthBar(); player1HealthBar.init(player1, true); player1HealthBar.x = 20; player1HealthBar.y = 100; LK.gui.left.addChild(player1HealthBar); player2HealthBar = new HealthBar(); player2HealthBar.init(player2, false); player2HealthBar.x = 20; player2HealthBar.y = 160; LK.gui.left.addChild(player2HealthBar); // Create ammo displays player1AmmoDisplay = new AmmoDisplay(); player1AmmoDisplay.init(player1); player1AmmoDisplay.x = 250; player1AmmoDisplay.y = 115; LK.gui.left.addChild(player1AmmoDisplay); player2AmmoDisplay = new AmmoDisplay(); player2AmmoDisplay.init(player2); player2AmmoDisplay.x = 250; player2AmmoDisplay.y = 175; LK.gui.left.addChild(player2AmmoDisplay); // Add UI elements for health and ammo var player1UI = new Container(); player1UI.addChild(player1HealthBar); player1UI.addChild(player1AmmoDisplay); LK.gui.left.addChild(player1UI); var player2UI = new Container(); player2UI.addChild(player2HealthBar); player2UI.addChild(player2AmmoDisplay); LK.gui.left.addChild(player2UI); // Update score display updateScoreText(); // Start first wave startNextWave(); // Hide start game text gameStartText.visible = false; // Play music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 2000 } }); } function clearEntities() { // Remove all bullets for (var i = 0; i < bullets.length; i++) { if (bullets[i].parent) { bullets[i].parent.removeChild(bullets[i]); } } bullets = []; // Remove all zombies for (var i = 0; i < zombies.length; i++) { if (zombies[i].parent) { zombies[i].parent.removeChild(zombies[i]); } } zombies = []; // Remove all blood splatters for (var i = 0; i < bloodSplatters.length; i++) { if (bloodSplatters[i].parent) { bloodSplatters[i].parent.removeChild(bloodSplatters[i]); } } bloodSplatters = []; // Remove players and their UI if they exist if (player1 && player1.parent) { player1.parent.removeChild(player1); } if (player2 && player2.parent) { player2.parent.removeChild(player2); } // Remove UI elements if they exist if (player1HealthBar && player1HealthBar.parent) { player1HealthBar.parent.removeChild(player1HealthBar); } if (player2HealthBar && player2HealthBar.parent) { player2HealthBar.parent.removeChild(player2HealthBar); } if (player1AmmoDisplay && player1AmmoDisplay.parent) { player1AmmoDisplay.parent.removeChild(player1AmmoDisplay); } if (player2AmmoDisplay && player2AmmoDisplay.parent) { player2AmmoDisplay.parent.removeChild(player2AmmoDisplay); } } function spawnZombie() { if (zombiesLeft <= 0) { return; } var zombie = new Zombie(); // Determine zombie spawn position (outside the screen) var spawnSide = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left var x, y; switch (spawnSide) { case 0: // top x = Math.random() * 2048; y = -100; break; case 1: // right x = 2148; y = Math.random() * 2732; break; case 2: // bottom x = Math.random() * 2048; y = 2832; break; case 3: // left x = -100; y = Math.random() * 2732; break; } // Scale zombie difficulty based on wave var healthMultiplier = 1 + currentWave * 0.1; var speedMultiplier = 1 + currentWave * 0.05; zombie.init(x, y, 2 * speedMultiplier, 100 * healthMultiplier); zombies.push(zombie); game.addChild(zombie); zombiesLeft--; } function startNextWave() { currentWave++; zombiesInWave = 5 + currentWave * 3; zombiesLeft = zombiesInWave; waveText.setText("Wave " + currentWave); tween(waveText, { alpha: 1 }, { duration: 500 }); // Play wave start sound LK.getSound('waveStart').play(); // After 2 seconds, fade out the wave text LK.setTimeout(function () { tween(waveText, { alpha: 0 }, { duration: 500 }); }, 2000); // Initial zombie spawn for (var i = 0; i < Math.min(5, zombiesInWave); i++) { spawnZombie(); } } function checkGameOver() { if (!gameActive) { return; } // Game over if both players are dead if (player1.isDead && player2.isDead) { gameActive = false; // Show game over screen after a short delay LK.setTimeout(function () { LK.showGameOver(); }, 1500); // Stop music with fade out LK.playMusic('bgMusic', { fade: { start: 0.3, end: 0, duration: 1500 } }); } } function handlePlayerMovement() { if (!gameActive || player1.isDead) { return; } var dx = 0; var dy = 0; if (moveUp) { dy -= 1; } if (moveDown) { dy += 1; } if (moveLeft) { dx -= 1; } if (moveRight) { dx += 1; } // Normalize diagonal movement if (dx !== 0 && dy !== 0) { var length = Math.sqrt(dx * dx + dy * dy); dx /= length; dy /= length; } player1.move(dx, dy); } // Initialize UI elements function createUI() { // Wave text waveText = new Text2("Wave 1", { size: 100, fill: 0xFFFFFF }); waveText.anchor.set(0.5); waveText.alpha = 0; LK.gui.center.addChild(waveText); // Score text scoreText = new Text2("Score: 0", { size: 40, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); scoreText.y = 20; LK.gui.top.addChild(scoreText); // High score text highScoreText = new Text2("High Score: " + storage.highScore, { size: 30, fill: 0xAAAAAA }); highScoreText.anchor.set(0.5, 0); highScoreText.y = 70; LK.gui.top.addChild(highScoreText); // Game start text gameStartText = new Text2("TAP TO START", { size: 80, fill: 0xFFFFFF }); gameStartText.anchor.set(0.5); LK.gui.center.addChild(gameStartText); // Pulse animation for game start text function pulseText() { tween(gameStartText.scale, { x: 1.1, y: 1.1 }, { duration: 800, easing: tween.sinceOut, onFinish: function onFinish() { tween(gameStartText.scale, { x: 1, y: 1 }, { duration: 800, easing: tween.sinceIn, onFinish: pulseText }); } }); } pulseText(); } // Input tracking variables var moveUp = false; var moveDown = false; var moveLeft = false; var moveRight = false; var isShooting = false; var shootTarget = { x: 0, y: 0 }; // Input handlers game.down = function (x, y, obj) { if (!gameActive) { startGame(); return; } // Determine which side of the screen was pressed if (x < 2048 / 2) { // Left side controls movement var centerX = 2048 / 4; var centerY = 2732 / 2; var dx = x - centerX; var dy = y - centerY; var length = Math.sqrt(dx * dx + dy * dy); if (length > 0) { dx /= length; dy /= length; } moveUp = dy < -0.5; moveDown = dy > 0.5; moveLeft = dx < -0.5; moveRight = dx > 0.5; } else { // Right side controls shooting isShooting = true; shootTarget.x = x; shootTarget.y = y; } }; game.up = function (x, y, obj) { // Stop movement or shooting based on which side was released if (x < 2048 / 2) { moveUp = moveDown = moveLeft = moveRight = false; } else { isShooting = false; } }; game.move = function (x, y, obj) { // Update shoot target if shooting if (isShooting && x > 2048 / 2) { shootTarget.x = x; shootTarget.y = y; } // Update movement direction if on left side if (x < 2048 / 2) { var centerX = 2048 / 4; var centerY = 2732 / 2; var dx = x - centerX; var dy = y - centerY; var length = Math.sqrt(dx * dx + dy * dy); if (length > 0) { dx /= length; dy /= length; } moveUp = dy < -0.5; moveDown = dy > 0.5; moveLeft = dx < -0.5; moveRight = dx > 0.5; } }; // Create the ground and UI createGround(); createUI(); // Main game update loop game.update = function () { if (gameActive) { // Handle player movement handlePlayerMovement(); // Handle player shooting if (isShooting && !player1.isDead) { var bullet = player1.shoot(shootTarget.x, shootTarget.y); if (bullet) { bullets.push(bullet); game.addChild(bullet); } } // Update players player1.update(); player2.update(); // Update health bars player1HealthBar.update(); player2HealthBar.update(); // Update ammo displays player1AmmoDisplay.update(); player2AmmoDisplay.update(); // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; bullet.update(); // Check for bullet collisions with zombies for (var j = zombies.length - 1; j >= 0; j--) { var zombie = zombies[j]; if (!zombie.isDead && bullet.intersects(zombie)) { zombie.takeDamage(bullet.damage); bullet.shouldDestroy = true; break; } } // Remove bullets marked for destruction if (bullet.shouldDestroy) { game.removeChild(bullet); bullets.splice(i, 1); } } // Update zombies for (var i = zombies.length - 1; i >= 0; i--) { var zombie = zombies[i]; zombie.update(); // Remove zombies marked for destruction if (zombie.shouldDestroy) { game.removeChild(zombie); zombies.splice(i, 1); } } // Update blood splatters for (var i = bloodSplatters.length - 1; i >= 0; i--) { var blood = bloodSplatters[i]; blood.update(); // Remove blood marked for destruction if (blood.shouldDestroy) { game.removeChild(blood); bloodSplatters.splice(i, 1); } } // Spawn more zombies if needed if (zombies.length === 0 && zombiesLeft === 0) { // All zombies in wave defeated, start next wave startNextWave(); } else if (zombies.length < 10 && zombiesLeft > 0 && LK.ticks % 60 === 0) { // Spawn a new zombie every second if there are less than 10 on screen spawnZombie(); } // Check for game over checkGameOver(); } }; // Play music with low volume LK.playMusic('bgMusic', { volume: 0.3 });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0
});
/****
* Classes
****/
var AmmoDisplay = Container.expand(function () {
var self = Container.call(this);
self.indicators = [];
self.player = null;
self.init = function (playerRef) {
self.player = playerRef;
// Clear existing indicators
for (var i = 0; i < self.indicators.length; i++) {
self.removeChild(self.indicators[i]);
}
self.indicators = [];
// Create new indicators
for (var i = 0; i < self.player.maxAmmo; i++) {
var indicator = LK.getAsset('ammoIndicator', {
anchorX: 0.5,
anchorY: 0.5,
x: i * 20,
y: 0
});
self.addChild(indicator);
self.indicators.push(indicator);
}
};
self.update = function () {
if (!self.player) {
return;
}
// Update visibility based on current ammo
for (var i = 0; i < self.indicators.length; i++) {
self.indicators[i].visible = i < self.player.ammo;
// Fade effect when reloading
if (self.player.isReloading) {
var reloadProgress = 1 - self.player.reloadCounter / self.player.reloadTime;
if (i < Math.floor(reloadProgress * self.player.maxAmmo)) {
self.indicators[i].visible = true;
self.indicators[i].alpha = 0.5;
} else {
self.indicators[i].visible = false;
}
} else {
self.indicators[i].alpha = 1;
}
}
};
return self;
});
var Blood = Container.expand(function () {
var self = Container.call(this);
var bloodGraphics = self.attachAsset('blood', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8
});
self.lifespan = 60; // Frames
self.init = function (x, y) {
self.x = x;
self.y = y;
self.scale.x = 0.5 + Math.random() * 0.5;
self.scale.y = 0.5 + Math.random() * 0.5;
self.rotation = Math.random() * Math.PI * 2;
self.alpha = 0.8;
};
self.update = function () {
self.lifespan--;
if (self.lifespan <= 20) {
self.alpha = self.lifespan / 20 * 0.8;
}
if (self.lifespan <= 0) {
self.shouldDestroy = true;
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 25;
self.direction = {
x: 0,
y: 0
};
self.shooter = null;
self.update = function () {
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Destroy bullet if it goes off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.shouldDestroy = true;
}
};
self.fire = function (startX, startY, targetX, targetY, shooterRef) {
self.x = startX;
self.y = startY;
self.shooter = shooterRef;
// Calculate direction
var dx = targetX - startX;
var dy = targetY - startY;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.direction.x = dx / distance;
self.direction.y = dy / distance;
}
};
return self;
});
var HealthBar = Container.expand(function () {
var self = Container.call(this);
var background = self.attachAsset('healthBarBackground', {
anchorX: 0,
anchorY: 0
});
var bar = self.attachAsset('healthBar', {
anchorX: 0,
anchorY: 0,
x: 2,
y: 2
});
var playerText = new Text2("P1", {
size: 20,
fill: 0xFFFFFF
});
playerText.anchor.set(0.5);
playerText.x = background.width / 2;
playerText.y = background.height / 2;
self.addChild(playerText);
self.player = null;
self.init = function (playerRef, isPlayer1) {
self.player = playerRef;
playerText.setText(isPlayer1 ? "P1" : "P2");
};
self.update = function () {
if (!self.player) {
return;
}
var healthPercent = self.player.health / self.player.maxHealth;
bar.scale.x = healthPercent;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
self.isPlayer1 = true; // Will be set when initializing
var graphicsId = 'player1'; // Default
self.health = 100;
self.maxHealth = 100;
self.moveSpeed = 5;
self.fireRate = 15; // Frames between shots
self.fireCounter = 0;
self.ammo = 12;
self.maxAmmo = 12;
self.reloadTime = 120; // 2 seconds at 60fps
self.reloadCounter = 0;
self.isReloading = false;
self.isDead = false;
self.lastMoveDirection = {
x: 0,
y: -1
}; // Default facing up
self.init = function (isFirstPlayer) {
self.isPlayer1 = isFirstPlayer;
graphicsId = isFirstPlayer ? 'player1' : 'player2';
if (self.graphics) {
self.removeChild(self.graphics);
}
self.graphics = self.attachAsset(graphicsId, {
anchorX: 0.5,
anchorY: 0.5,
tint: isFirstPlayer ? 0xff0000 : 0x0000ff // Red for player1, Blue for player2
});
self.health = self.maxHealth;
var nameTag = new Text2(isFirstPlayer ? "Player 1" : "Player 2", {
// Name tag
size: 20,
fill: 0xFFFFFF
});
nameTag.anchor.set(0.5);
nameTag.y = -self.graphics.height / 2 - 10; // Position above the player
self.addChild(nameTag);
self.ammo = self.maxAmmo;
self.fireCounter = 0;
self.reloadCounter = 0;
self.isReloading = false;
self.isDead = false;
};
self.takeDamage = function (amount) {
if (self.isDead) {
return;
}
self.health -= amount;
if (self.health <= 0) {
self.health = 0;
self.die();
} else {
// Flash red when hit
LK.effects.flashObject(self, self.isPlayer1 ? 0xff0000 : 0x0000ff, 300); // Red flash for player1, Blue flash for player2
LK.getSound('playerHit').play();
}
};
self.die = function () {
self.isDead = true;
self.alpha = 0.5;
// Flash red when dead
LK.effects.flashObject(self, 0xff0000, 1000);
};
self.shoot = function (targetX, targetY) {
if (self.isDead || self.isReloading) {
return null;
}
if (self.ammo <= 0) {
self.reload();
return null;
}
if (self.fireCounter <= 0) {
// Create bullet
var bullet = new Bullet();
bullet.fire(self.x, self.y, targetX, targetY, self);
// Update state
self.ammo--;
self.fireCounter = self.fireRate;
// Keep track of facing direction based on shot
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.lastMoveDirection.x = dx / distance;
self.lastMoveDirection.y = dy / distance;
}
// Play sound
LK.getSound('shoot').play();
if (self.isPlayer1) {
LK.getSound('player1Shoot').play(); // Unique sound for player 1
} else {
LK.getSound('player2Shoot').play(); // Unique sound for player 2
}
return bullet;
}
return null;
};
self.reload = function () {
if (self.isDead || self.isReloading || self.ammo === self.maxAmmo) {
return;
}
self.isReloading = true;
self.reloadCounter = self.reloadTime;
LK.getSound('reload').play();
};
self.move = function (dx, dy) {
if (self.isDead) {
return;
}
// If moving, update last move direction
if (dx !== 0 || dy !== 0) {
var length = Math.sqrt(dx * dx + dy * dy);
if (length > 0) {
self.lastMoveDirection.x = dx / length;
self.lastMoveDirection.y = dy / length;
}
}
// Apply movement
var newX = self.x + dx * self.moveSpeed;
var newY = self.y + dy * self.moveSpeed;
// Constrain to game bounds with a margin
var margin = 50;
newX = Math.max(margin, Math.min(2048 - margin, newX));
newY = Math.max(margin, Math.min(2732 - margin, newY));
self.x = newX;
self.y = newY;
};
self.update = function () {
// Update reload state
if (self.isReloading) {
self.reloadCounter--;
if (self.reloadCounter <= 0) {
self.isReloading = false;
self.ammo = self.maxAmmo;
}
}
// Update fire rate counter
if (self.fireCounter > 0) {
self.fireCounter--;
}
// AI behavior for Player 2
if (!self.isPlayer1 && !self.isDead) {
self.updateAI();
}
};
self.updateAI = function () {
// Find the nearest zombie
var nearestZombie = null;
var nearestDistance = Number.MAX_VALUE;
for (var i = 0; i < zombies.length; i++) {
var zombie = zombies[i];
var distance = getDistance(self, zombie);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestZombie = zombie;
}
}
// Move and shoot logic
if (nearestZombie) {
// If zombie is close, move away from it
if (nearestDistance < 300) {
var dx = self.x - nearestZombie.x;
var dy = self.y - nearestZombie.y;
var length = Math.sqrt(dx * dx + dy * dy);
if (length > 0) {
dx /= length;
dy /= length;
self.move(dx, dy);
}
}
// Otherwise, stay close to player 1
else {
var distToPlayer1 = getDistance(self, player1);
if (distToPlayer1 > 300) {
var dx = player1.x - self.x;
var dy = player1.y - self.y;
var length = Math.sqrt(dx * dx + dy * dy);
if (length > 0) {
dx /= length;
dy /= length;
self.move(dx, dy);
}
}
}
// Shoot at nearest zombie if not reloading and it's in range
if (!self.isReloading && nearestDistance < 600) {
var bullet = self.shoot(nearestZombie.x, nearestZombie.y);
if (bullet) {
bullets.push(bullet);
game.addChild(bullet);
}
}
// Reload if low on ammo and no zombies nearby
if (self.ammo <= self.maxAmmo / 4 && nearestDistance > 400) {
self.reload();
}
}
};
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 = 100;
self.moveSpeed = 2;
self.damage = 5;
self.attackRate = 60; // 1 second between attacks
self.attackCounter = 0;
self.isDead = false;
self.init = function (x, y, moveSpeed, health) {
self.x = x;
self.y = y;
self.moveSpeed = moveSpeed || 2;
self.health = health || 100;
};
self.takeDamage = function (amount) {
if (self.isDead) {
return;
}
self.health -= amount;
// Flash red when hit
LK.effects.flashObject(self, 0xff0000, 150);
LK.getSound('zombieHit').play();
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
self.isDead = true;
self.shouldDestroy = true;
// Create blood splatter
createBlood(self.x, self.y);
// Increase score
currentScore += 10;
updateScoreText();
};
self.attack = function (player) {
if (self.isDead || self.attackCounter > 0) {
return;
}
player.takeDamage(self.damage);
self.attackCounter = self.attackRate;
};
self.update = function () {
if (self.isDead) {
return;
}
// Update attack counter
if (self.attackCounter > 0) {
self.attackCounter--;
}
// Target the nearest player that's alive
var target = null;
if (!player1.isDead && !player2.isDead) {
// Target closest player
var dist1 = getDistance(self, player1);
var dist2 = getDistance(self, player2);
target = dist1 < dist2 ? player1 : player2;
} else if (!player1.isDead) {
target = player1;
} else if (!player2.isDead) {
target = player2;
}
if (target) {
// Move towards target
var dx = target.x - self.x;
var dy = target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var moveX = dx / distance * self.moveSpeed;
var moveY = dy / distance * self.moveSpeed;
self.x += moveX;
self.y += moveY;
}
// Attack if close enough
if (distance < 80) {
self.attack(target);
// Push the player out if they are stuck inside the zombie
if (distance < 40) {
var pushX = dx / distance * (40 - distance);
var pushY = dy / distance * (40 - distance);
target.x += pushX;
target.y += pushY;
}
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game state variables
var player1;
var player2;
var player1HealthBar;
var player2HealthBar;
var player1AmmoDisplay;
var player2AmmoDisplay;
var bullets = [];
var zombies = [];
var bloodSplatters = [];
var currentWave = 0;
var zombiesInWave = 0;
var zombiesLeft = 0;
var currentScore = 0;
var gameActive = false;
var waveText;
var scoreText;
var highScoreText;
var gameStartText;
var groundTiles = [];
// Helper functions
function getDistance(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
return Math.sqrt(dx * dx + dy * dy);
}
function createGround() {
var tileSize = 128;
var tilesX = Math.ceil(2048 / tileSize);
var tilesY = Math.ceil(2732 / tileSize);
for (var y = 0; y < tilesY; y++) {
for (var x = 0; x < tilesX; x++) {
var tile = LK.getAsset('groundTile', {
anchorX: 0,
anchorY: 0,
x: x * tileSize,
y: y * tileSize,
alpha: 0.3
});
// Add slight randomness to tiles
tile.tint = 0x666666 + Math.floor(Math.random() * 0x111111);
groundTiles.push(tile);
game.addChild(tile);
}
}
}
function updateScoreText() {
scoreText.setText("Score: " + currentScore);
// Update high score if needed
if (currentScore > storage.highScore) {
storage.highScore = currentScore;
highScoreText.setText("High Score: " + storage.highScore);
}
}
function createBlood(x, y) {
for (var i = 0; i < 3; i++) {
var blood = new Blood();
blood.init(x + (Math.random() * 60 - 30), y + (Math.random() * 60 - 30));
bloodSplatters.push(blood);
game.addChild(blood);
}
}
function startGame() {
// Reset game state
gameActive = true;
currentWave = 0;
currentScore = 0;
// Clear any existing entities
clearEntities();
// Create players
player1 = new Player();
player1.init(true);
player1.x = 2048 / 2 - 150;
player1.y = 2732 / 2;
game.addChild(player1);
player2 = new Player();
player2.init(false);
player2.x = 2048 / 2 + 150;
player2.y = 2732 / 2;
game.addChild(player2);
// Create health bars
player1HealthBar = new HealthBar();
player1HealthBar.init(player1, true);
player1HealthBar.x = 20;
player1HealthBar.y = 100;
LK.gui.left.addChild(player1HealthBar);
player2HealthBar = new HealthBar();
player2HealthBar.init(player2, false);
player2HealthBar.x = 20;
player2HealthBar.y = 160;
LK.gui.left.addChild(player2HealthBar);
// Create ammo displays
player1AmmoDisplay = new AmmoDisplay();
player1AmmoDisplay.init(player1);
player1AmmoDisplay.x = 250;
player1AmmoDisplay.y = 115;
LK.gui.left.addChild(player1AmmoDisplay);
player2AmmoDisplay = new AmmoDisplay();
player2AmmoDisplay.init(player2);
player2AmmoDisplay.x = 250;
player2AmmoDisplay.y = 175;
LK.gui.left.addChild(player2AmmoDisplay);
// Add UI elements for health and ammo
var player1UI = new Container();
player1UI.addChild(player1HealthBar);
player1UI.addChild(player1AmmoDisplay);
LK.gui.left.addChild(player1UI);
var player2UI = new Container();
player2UI.addChild(player2HealthBar);
player2UI.addChild(player2AmmoDisplay);
LK.gui.left.addChild(player2UI);
// Update score display
updateScoreText();
// Start first wave
startNextWave();
// Hide start game text
gameStartText.visible = false;
// Play music
LK.playMusic('bgMusic', {
fade: {
start: 0,
end: 0.3,
duration: 2000
}
});
}
function clearEntities() {
// Remove all bullets
for (var i = 0; i < bullets.length; i++) {
if (bullets[i].parent) {
bullets[i].parent.removeChild(bullets[i]);
}
}
bullets = [];
// Remove all zombies
for (var i = 0; i < zombies.length; i++) {
if (zombies[i].parent) {
zombies[i].parent.removeChild(zombies[i]);
}
}
zombies = [];
// Remove all blood splatters
for (var i = 0; i < bloodSplatters.length; i++) {
if (bloodSplatters[i].parent) {
bloodSplatters[i].parent.removeChild(bloodSplatters[i]);
}
}
bloodSplatters = [];
// Remove players and their UI if they exist
if (player1 && player1.parent) {
player1.parent.removeChild(player1);
}
if (player2 && player2.parent) {
player2.parent.removeChild(player2);
}
// Remove UI elements if they exist
if (player1HealthBar && player1HealthBar.parent) {
player1HealthBar.parent.removeChild(player1HealthBar);
}
if (player2HealthBar && player2HealthBar.parent) {
player2HealthBar.parent.removeChild(player2HealthBar);
}
if (player1AmmoDisplay && player1AmmoDisplay.parent) {
player1AmmoDisplay.parent.removeChild(player1AmmoDisplay);
}
if (player2AmmoDisplay && player2AmmoDisplay.parent) {
player2AmmoDisplay.parent.removeChild(player2AmmoDisplay);
}
}
function spawnZombie() {
if (zombiesLeft <= 0) {
return;
}
var zombie = new Zombie();
// Determine zombie spawn position (outside the screen)
var spawnSide = Math.floor(Math.random() * 4); // 0: top, 1: right, 2: bottom, 3: left
var x, y;
switch (spawnSide) {
case 0:
// top
x = Math.random() * 2048;
y = -100;
break;
case 1:
// right
x = 2148;
y = Math.random() * 2732;
break;
case 2:
// bottom
x = Math.random() * 2048;
y = 2832;
break;
case 3:
// left
x = -100;
y = Math.random() * 2732;
break;
}
// Scale zombie difficulty based on wave
var healthMultiplier = 1 + currentWave * 0.1;
var speedMultiplier = 1 + currentWave * 0.05;
zombie.init(x, y, 2 * speedMultiplier, 100 * healthMultiplier);
zombies.push(zombie);
game.addChild(zombie);
zombiesLeft--;
}
function startNextWave() {
currentWave++;
zombiesInWave = 5 + currentWave * 3;
zombiesLeft = zombiesInWave;
waveText.setText("Wave " + currentWave);
tween(waveText, {
alpha: 1
}, {
duration: 500
});
// Play wave start sound
LK.getSound('waveStart').play();
// After 2 seconds, fade out the wave text
LK.setTimeout(function () {
tween(waveText, {
alpha: 0
}, {
duration: 500
});
}, 2000);
// Initial zombie spawn
for (var i = 0; i < Math.min(5, zombiesInWave); i++) {
spawnZombie();
}
}
function checkGameOver() {
if (!gameActive) {
return;
}
// Game over if both players are dead
if (player1.isDead && player2.isDead) {
gameActive = false;
// Show game over screen after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 1500);
// Stop music with fade out
LK.playMusic('bgMusic', {
fade: {
start: 0.3,
end: 0,
duration: 1500
}
});
}
}
function handlePlayerMovement() {
if (!gameActive || player1.isDead) {
return;
}
var dx = 0;
var dy = 0;
if (moveUp) {
dy -= 1;
}
if (moveDown) {
dy += 1;
}
if (moveLeft) {
dx -= 1;
}
if (moveRight) {
dx += 1;
}
// Normalize diagonal movement
if (dx !== 0 && dy !== 0) {
var length = Math.sqrt(dx * dx + dy * dy);
dx /= length;
dy /= length;
}
player1.move(dx, dy);
}
// Initialize UI elements
function createUI() {
// Wave text
waveText = new Text2("Wave 1", {
size: 100,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5);
waveText.alpha = 0;
LK.gui.center.addChild(waveText);
// Score text
scoreText = new Text2("Score: 0", {
size: 40,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
scoreText.y = 20;
LK.gui.top.addChild(scoreText);
// High score text
highScoreText = new Text2("High Score: " + storage.highScore, {
size: 30,
fill: 0xAAAAAA
});
highScoreText.anchor.set(0.5, 0);
highScoreText.y = 70;
LK.gui.top.addChild(highScoreText);
// Game start text
gameStartText = new Text2("TAP TO START", {
size: 80,
fill: 0xFFFFFF
});
gameStartText.anchor.set(0.5);
LK.gui.center.addChild(gameStartText);
// Pulse animation for game start text
function pulseText() {
tween(gameStartText.scale, {
x: 1.1,
y: 1.1
}, {
duration: 800,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(gameStartText.scale, {
x: 1,
y: 1
}, {
duration: 800,
easing: tween.sinceIn,
onFinish: pulseText
});
}
});
}
pulseText();
}
// Input tracking variables
var moveUp = false;
var moveDown = false;
var moveLeft = false;
var moveRight = false;
var isShooting = false;
var shootTarget = {
x: 0,
y: 0
};
// Input handlers
game.down = function (x, y, obj) {
if (!gameActive) {
startGame();
return;
}
// Determine which side of the screen was pressed
if (x < 2048 / 2) {
// Left side controls movement
var centerX = 2048 / 4;
var centerY = 2732 / 2;
var dx = x - centerX;
var dy = y - centerY;
var length = Math.sqrt(dx * dx + dy * dy);
if (length > 0) {
dx /= length;
dy /= length;
}
moveUp = dy < -0.5;
moveDown = dy > 0.5;
moveLeft = dx < -0.5;
moveRight = dx > 0.5;
} else {
// Right side controls shooting
isShooting = true;
shootTarget.x = x;
shootTarget.y = y;
}
};
game.up = function (x, y, obj) {
// Stop movement or shooting based on which side was released
if (x < 2048 / 2) {
moveUp = moveDown = moveLeft = moveRight = false;
} else {
isShooting = false;
}
};
game.move = function (x, y, obj) {
// Update shoot target if shooting
if (isShooting && x > 2048 / 2) {
shootTarget.x = x;
shootTarget.y = y;
}
// Update movement direction if on left side
if (x < 2048 / 2) {
var centerX = 2048 / 4;
var centerY = 2732 / 2;
var dx = x - centerX;
var dy = y - centerY;
var length = Math.sqrt(dx * dx + dy * dy);
if (length > 0) {
dx /= length;
dy /= length;
}
moveUp = dy < -0.5;
moveDown = dy > 0.5;
moveLeft = dx < -0.5;
moveRight = dx > 0.5;
}
};
// Create the ground and UI
createGround();
createUI();
// Main game update loop
game.update = function () {
if (gameActive) {
// Handle player movement
handlePlayerMovement();
// Handle player shooting
if (isShooting && !player1.isDead) {
var bullet = player1.shoot(shootTarget.x, shootTarget.y);
if (bullet) {
bullets.push(bullet);
game.addChild(bullet);
}
}
// Update players
player1.update();
player2.update();
// Update health bars
player1HealthBar.update();
player2HealthBar.update();
// Update ammo displays
player1AmmoDisplay.update();
player2AmmoDisplay.update();
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
bullet.update();
// Check for bullet collisions with zombies
for (var j = zombies.length - 1; j >= 0; j--) {
var zombie = zombies[j];
if (!zombie.isDead && bullet.intersects(zombie)) {
zombie.takeDamage(bullet.damage);
bullet.shouldDestroy = true;
break;
}
}
// Remove bullets marked for destruction
if (bullet.shouldDestroy) {
game.removeChild(bullet);
bullets.splice(i, 1);
}
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var zombie = zombies[i];
zombie.update();
// Remove zombies marked for destruction
if (zombie.shouldDestroy) {
game.removeChild(zombie);
zombies.splice(i, 1);
}
}
// Update blood splatters
for (var i = bloodSplatters.length - 1; i >= 0; i--) {
var blood = bloodSplatters[i];
blood.update();
// Remove blood marked for destruction
if (blood.shouldDestroy) {
game.removeChild(blood);
bloodSplatters.splice(i, 1);
}
}
// Spawn more zombies if needed
if (zombies.length === 0 && zombiesLeft === 0) {
// All zombies in wave defeated, start next wave
startNextWave();
} else if (zombies.length < 10 && zombiesLeft > 0 && LK.ticks % 60 === 0) {
// Spawn a new zombie every second if there are less than 10 on screen
spawnZombie();
}
// Check for game over
checkGameOver();
}
};
// Play music with low volume
LK.playMusic('bgMusic', {
volume: 0.3
});
Make it a 8 bit zombie that is menacing. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Make an 8 bit boy. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Make an 8 bit black and white grayscale boy. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Make an 8bit red blood splater. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Make an green grass 8bit texture 2d and flat. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows