/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Bullet class var Bullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bulletSprite.width / 2; self.vx = 0; self.vy = 0; self.lifetime = 60; // frames self.update = function () { self.x += self.vx; self.y += self.vy; self.lifetime--; }; return self; }); // Hero class var Hero = Container.expand(function () { var self = Container.call(this); var heroSprite = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.heroSprite = heroSprite; // Add gun sprite to hero's hand var gunSprite = self.attachAsset('gun', { anchorX: 0.2, // grip of gun anchorY: 0.5, width: heroSprite.width * 0.45, height: heroSprite.height * 0.22 }); gunSprite.x = heroSprite.width * 0.32; // offset to right hand gunSprite.y = 0; self.radius = heroSprite.width / 2; self.speed = 18; // pixels per move self.lastShotTick = 0; self.shootCooldown = 18; // frames between shots // Allow skin change self.setSkin = function (skinId) { if (!skinId || skinId === "hero") { // Already default return; } // Remove old sprite if (self.heroSprite && self.heroSprite.parent) { self.removeChild(self.heroSprite); } // Add new sprite var newSprite = self.attachAsset(skinId, { anchorX: 0.5, anchorY: 0.5, width: heroSprite.width, height: heroSprite.height }); self.heroSprite = newSprite; // Move gun to top if (gunSprite && gunSprite.parent) { self.removeChild(gunSprite); self.addChild(gunSprite); } // Update radius self.radius = newSprite.width / 2; }; self.update = function () {}; return self; }); // Powerup class var Powerup = Container.expand(function () { var self = Container.call(this); var powerupSprite = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.radius = powerupSprite.width / 2; self.type = 'rapid'; // only one type for MVP self.update = function () {}; return self; }); // Zombie class var Zombie = Container.expand(function () { var self = Container.call(this); // Default to normal zombie asset var zombieSprite = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.zombieSprite = zombieSprite; self.radius = zombieSprite.width / 2; self.baseSpeed = 3 + Math.random() * 2; // base speed, will be increased by wave self.speed = self.baseSpeed; self.target = null; // set to hero self.maxHp = 1; self.hp = 1; self.wave = 1; self.type = "normal"; // can be "normal", "fast", "tank", "crawler" self.setWave = function (wave) { self.wave = wave; // Remove old sprite if present if (self.zombieSprite && self.zombieSprite.parent) { self.removeChild(self.zombieSprite); } // Decide type var r = Math.random(); if (wave >= 7 && r < 0.18) { // Crawler: slow, small, hard to hit self.type = "crawler"; self.speed = 1.7 + 0.12 * wave; self.maxHp = 2 + Math.floor(wave / 3); self.hp = self.maxHp; self.zombieSprite = self.attachAsset('zombie_crawler', { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.zombieSprite.width / 2; self.zombieSprite.tint = 0x8e8e8e; // grayish } else if (wave >= 6 && r < 0.38) { // Tank zombie: slow but high HP self.type = "tank"; self.speed = 2 + 0.15 * wave; self.maxHp = 3 + Math.floor(wave / 2); self.hp = self.maxHp; self.zombieSprite = self.attachAsset('zombie_tank', { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.zombieSprite.width / 2; self.zombieSprite.tint = 0x6e4e2e; // brownish } else if (wave >= 3 && r < 0.63) { // Fast zombie: high speed, low HP self.type = "fast"; self.speed = 5 + 0.4 * wave; self.maxHp = 1; self.hp = 1; self.zombieSprite = self.attachAsset('zombie_fast', { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.zombieSprite.width / 2; self.zombieSprite.tint = 0x7cf442; // bright green } else { // Normal zombie self.type = "normal"; self.speed = self.baseSpeed + (wave - 1) * 0.5; self.maxHp = 1 + Math.floor(wave / 4); self.hp = self.maxHp; self.zombieSprite = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.zombieSprite.width / 2; self.zombieSprite.tint = 0x7bbf3f; // normal green } }; // Health bar setup var healthBarW = zombieSprite.width * 0.7; var healthBarH = 16; var healthBarBg = LK.getAsset('hero', { anchorX: 0, anchorY: 0, width: healthBarW, height: healthBarH, color: 0x222222 }); healthBarBg.alpha = 0.7; var healthBarFill = LK.getAsset('hero', { anchorX: 0, anchorY: 0, width: healthBarW, height: healthBarH, color: 0x3FA34D }); healthBarFill.alpha = 0.92; self.addChild(healthBarBg); self.addChild(healthBarFill); self.update = function () { if (!self.target) return; var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.x += self.speed * dx / dist; self.y += self.speed * dy / dist; } // Update health bar position and fill var barY = -self.radius - 30; healthBarBg.x = -healthBarW / 2; healthBarBg.y = barY; healthBarFill.x = -healthBarW / 2; healthBarFill.y = barY; var ratio = Math.max(0, Math.min(1, self.hp / self.maxHp)); healthBarFill.width = healthBarW * ratio; if (ratio > 0.6) { healthBarFill.tint = 0x3FA34D; } else if (ratio > 0.3) { healthBarFill.tint = 0xF7E733; } else { healthBarFill.tint = 0xD83318; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Game area // Hero: red box // Zombie: green ellipse // Bullet: yellow box // Powerup: blue ellipse // Sound effects // Music // new gun asset var GAME_W = 2048; var GAME_H = 2732; // State var hero = null; var zombies = []; var bullets = []; var powerups = []; var dragNode = null; var lastIntersecting = false; var score = 0; var highScore = storage.highScore || 0; var wave = 1; var ticksSinceStart = 0; var zombieSpawnTick = 0; var zombieSpawnInterval = 90; // frames var rapidFireTicks = 0; // --- HEALTH SYSTEM --- var heroMaxHp = 5; var heroHp = heroMaxHp; // --- AMMO SYSTEM --- var heroMaxAmmo = 12; var heroAmmo = heroMaxAmmo; var heroReloading = false; var heroReloadTicks = 0; var heroReloadTime = 60; // frames to reload // Ammo bar UI var ammoBarBg = new Container(); var ammoBarWidth = 420; var ammoBarHeight = 28; var ammoBarBgRect = LK.getAsset('hero', { anchorX: 0, anchorY: 0, width: ammoBarWidth, height: ammoBarHeight, color: 0x222222 }); ammoBarBgRect.alpha = 0.7; ammoBarBg.addChild(ammoBarBgRect); var ammoBarFill = LK.getAsset('hero', { anchorX: 0, anchorY: 0, width: ammoBarWidth, height: ammoBarHeight, color: 0x3B7DD8 }); ammoBarFill.alpha = 0.92; ammoBarBg.addChild(ammoBarFill); ammoBarBg.x = (GAME_W - ammoBarWidth) / 2; ammoBarBg.y = 170; LK.gui.top.addChild(ammoBarBg); var ammoTxt = new Text2('', { size: 60, fill: "#fff" }); ammoTxt.anchor.set(0.5, 0); ammoTxt.x = GAME_W / 2; ammoTxt.y = 210; LK.gui.top.addChild(ammoTxt); function updateAmmoBar() { var ratio = Math.max(0, Math.min(1, heroAmmo / heroMaxAmmo)); ammoBarFill.width = ammoBarWidth * ratio; if (heroReloading) { ammoBarFill.tint = 0xF7E733; ammoTxt.setText('RELOADING...'); } else { ammoBarFill.tint = 0x3B7DD8; ammoTxt.setText(heroAmmo + ' / ' + heroMaxAmmo); } } updateAmmoBar(); // Health bar UI var healthBarBg = new Container(); var healthBarWidth = 520; var healthBarHeight = 38; var healthBarBgRect = LK.getAsset('hero', { anchorX: 0, anchorY: 0, width: healthBarWidth, height: healthBarHeight, color: 0x333333 }); healthBarBgRect.alpha = 0.7; healthBarBg.addChild(healthBarBgRect); var healthBarFill = LK.getAsset('hero', { anchorX: 0, anchorY: 0, width: healthBarWidth, height: healthBarHeight, color: 0x3FA34D }); healthBarFill.alpha = 0.92; healthBarBg.addChild(healthBarFill); healthBarBg.x = (GAME_W - healthBarWidth) / 2; healthBarBg.y = 110; LK.gui.top.addChild(healthBarBg); function updateHealthBar() { var ratio = Math.max(0, Math.min(1, heroHp / heroMaxHp)); healthBarFill.width = healthBarWidth * ratio; if (ratio > 0.6) { healthBarFill.tint = 0x3FA34D; } else if (ratio > 0.3) { healthBarFill.tint = 0xF7E733; } else { healthBarFill.tint = 0xD83318; } } updateHealthBar(); // Spawn hero at game start menuActive = false; if (hero) hero.destroy(); hero = new Hero(); hero.x = GAME_W / 2; hero.y = GAME_H / 2; game.addChild(hero); // Reset state // Move UI text declarations above their first use var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // High score text var highScoreTxt = new Text2('Best: ' + highScore, { size: 60, fill: "#fff" }); highScoreTxt.anchor.set(0.5, 0); highScoreTxt.y = 120; LK.gui.top.addChild(highScoreTxt); // Powerup text var powerupTxt = new Text2('', { size: 70, fill: 0x3B7DD8 }); powerupTxt.anchor.set(0.5, 0); powerupTxt.y = 200; LK.gui.top.addChild(powerupTxt); heroHp = heroMaxHp; updateHealthBar(); heroAmmo = heroMaxAmmo; heroReloading = false; heroReloadTicks = 0; updateAmmoBar(); score = 0; scoreTxt.setText(score); wave = 1; ticksSinceStart = 0; zombieSpawnTick = 0; zombieSpawnInterval = 90; rapidFireTicks = 0; for (var i = zombies.length - 1; i >= 0; i--) { zombies[i].destroy(); zombies.splice(i, 1); } for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].destroy(); bullets.splice(i, 1); } for (var i = powerups.length - 1; i >= 0; i--) { powerups[i].destroy(); powerups.splice(i, 1); } powerupTxt.setText(''); // --- CLASSIC MENU --- // Classic menu overlay and logic var classicMenuOverlay = new Container(); var menuBg = LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5, width: 900, height: 900, color: 0x222222 }); menuBg.alpha = 0.92; classicMenuOverlay.addChild(menuBg); classicMenuOverlay.x = GAME_W / 2; classicMenuOverlay.y = GAME_H / 2; // Title var menuTitle = new Text2('Zombie Survival', { size: 120, fill: "#fff" }); menuTitle.anchor.set(0.5, 0); menuTitle.y = -320; classicMenuOverlay.addChild(menuTitle); // Play button var playBtn = LK.getAsset('powerup', { anchorX: 0.5, anchorY: 0.5, width: 320, height: 120 }); playBtn.y = 80; classicMenuOverlay.addChild(playBtn); var playBtnText = new Text2('PLAY', { size: 90, fill: "#fff" }); playBtnText.anchor.set(0.5, 0.5); playBtnText.x = playBtn.x; playBtnText.y = playBtn.y; classicMenuOverlay.addChild(playBtnText); // Cheats tab button var cheatsBtn = LK.getAsset('powerup', { anchorX: 0.5, anchorY: 0.5, width: 320, height: 100, color: 0x4444aa }); cheatsBtn.y = 240; classicMenuOverlay.addChild(cheatsBtn); var cheatsBtnText = new Text2('CHEATS', { size: 70, fill: "#fff" }); cheatsBtnText.anchor.set(0.5, 0.5); cheatsBtnText.x = cheatsBtn.x; cheatsBtnText.y = cheatsBtn.y; classicMenuOverlay.addChild(cheatsBtnText); // High score display var menuHighScore = new Text2('Best: ' + (storage.highScore || 0), { size: 70, fill: "#fff" }); menuHighScore.anchor.set(0.5, 0); menuHighScore.y = -120; classicMenuOverlay.addChild(menuHighScore); var menuActive = true; game.addChild(classicMenuOverlay); // Cheats tab overlay var cheatsOverlay = new Container(); var cheatsBg = LK.getAsset('hero', { anchorX: 0.5, anchorY: 0.5, width: 800, height: 900, color: 0x222244 }); cheatsBg.alpha = 0.97; cheatsOverlay.addChild(cheatsBg); cheatsOverlay.x = GAME_W / 2; cheatsOverlay.y = GAME_H / 2; // Cheats title var cheatsTitle = new Text2('Cheats', { size: 100, fill: "#fff" }); cheatsTitle.anchor.set(0.5, 0); cheatsTitle.y = -340; cheatsOverlay.addChild(cheatsTitle); // Example cheat: Add 100 score var cheatScoreBtn = LK.getAsset('powerup', { anchorX: 0.5, anchorY: 0.5, width: 340, height: 110, color: 0x3B7DD8 }); cheatScoreBtn.y = -120; cheatsOverlay.addChild(cheatScoreBtn); var cheatScoreBtnText = new Text2('Add 100 Score', { size: 60, fill: "#fff" }); cheatScoreBtnText.anchor.set(0.5, 0.5); cheatScoreBtnText.x = cheatScoreBtn.x; cheatScoreBtnText.y = cheatScoreBtn.y; cheatsOverlay.addChild(cheatScoreBtnText); // Example cheat: Heal to full var cheatHealBtn = LK.getAsset('powerup', { anchorX: 0.5, anchorY: 0.5, width: 340, height: 110, color: 0x3FA34D }); cheatHealBtn.y = 40; cheatsOverlay.addChild(cheatHealBtn); var cheatHealBtnText = new Text2('Full Heal', { size: 60, fill: "#fff" }); cheatHealBtnText.anchor.set(0.5, 0.5); cheatHealBtnText.x = cheatHealBtn.x; cheatHealBtnText.y = cheatHealBtn.y; cheatsOverlay.addChild(cheatHealBtnText); // Close cheats tab var closeCheatsBtn = LK.getAsset('powerup', { anchorX: 0.5, anchorY: 0.5, width: 220, height: 90, color: 0xD83318 }); closeCheatsBtn.y = 300; cheatsOverlay.addChild(closeCheatsBtn); var closeCheatsBtnText = new Text2('CLOSE', { size: 55, fill: "#fff" }); closeCheatsBtnText.anchor.set(0.5, 0.5); closeCheatsBtnText.x = closeCheatsBtn.x; closeCheatsBtnText.y = closeCheatsBtn.y; cheatsOverlay.addChild(closeCheatsBtnText); cheatsOverlay.visible = false; game.addChild(cheatsOverlay); // Show/hide logic function showClassicMenu() { menuActive = true; classicMenuOverlay.visible = true; cheatsOverlay.visible = false; // Update high score display menuHighScore.setText('Best: ' + (storage.highScore || 0)); } function hideClassicMenu() { menuActive = false; classicMenuOverlay.visible = false; cheatsOverlay.visible = false; } // Cheats tab button handler cheatsBtn.interactive = true; cheatsBtn.down = function () { classicMenuOverlay.visible = false; cheatsOverlay.visible = true; }; // Close cheats tab handler closeCheatsBtn.interactive = true; closeCheatsBtn.down = function () { cheatsOverlay.visible = false; classicMenuOverlay.visible = true; }; // Cheat: Add 100 score cheatScoreBtn.interactive = true; cheatScoreBtn.down = function () { score += 100; scoreTxt.setText(score); if (score > highScore) { highScore = score; highScoreTxt.setText('Best: ' + highScore); storage.highScore = highScore; menuHighScore.setText('Best: ' + highScore); } }; // Cheat: Full heal cheatHealBtn.interactive = true; cheatHealBtn.down = function () { heroHp = heroMaxHp; updateHealthBar(); }; // Play button handler playBtn.interactive = true; playBtn.down = function () { hideClassicMenu(); // Reset game state if (hero) hero.destroy(); hero = new Hero(); hero.x = GAME_W / 2; hero.y = GAME_H / 2; game.addChild(hero); heroHp = heroMaxHp; updateHealthBar(); heroAmmo = heroMaxAmmo; heroReloading = false; heroReloadTicks = 0; updateAmmoBar(); score = 0; scoreTxt.setText(score); wave = 1; ticksSinceStart = 0; zombieSpawnTick = 0; zombieSpawnInterval = 90; rapidFireTicks = 0; for (var i = zombies.length - 1; i >= 0; i--) { zombies[i].destroy(); zombies.splice(i, 1); } for (var i = bullets.length - 1; i >= 0; i--) { bullets[i].destroy(); bullets.splice(i, 1); } for (var i = powerups.length - 1; i >= 0; i--) { powerups[i].destroy(); powerups.splice(i, 1); } powerupTxt.setText(''); }; showClassicMenu(); // Helper: spawn zombie at random edge function spawnZombie() { var z = new Zombie(); // Pick edge: 0=top,1=bottom,2=left,3=right var edge = Math.floor(Math.random() * 4); var margin = 80; if (edge === 0) { // top z.x = margin + Math.random() * (GAME_W - 2 * margin); z.y = -z.radius; } else if (edge === 1) { // bottom z.x = margin + Math.random() * (GAME_W - 2 * margin); z.y = GAME_H + z.radius; } else if (edge === 2) { // left z.x = -z.radius; z.y = margin + Math.random() * (GAME_H - 2 * margin); } else { // right z.x = GAME_W + z.radius; z.y = margin + Math.random() * (GAME_H - 2 * margin); } // Set wave and type if (typeof z.setWave === "function") { z.setWave(wave); } z.target = hero; zombies.push(z); game.addChild(z); } // Helper: spawn powerup function spawnPowerup() { var p = new Powerup(); p.x = 200 + Math.random() * (GAME_W - 400); p.y = 300 + Math.random() * (GAME_H - 600); powerups.push(p); game.addChild(p); } // Helper: shoot bullet towards (tx,ty) function shootBullet(tx, ty) { if (heroReloading) return; if (heroAmmo <= 0) { // Start reload if not already heroReloading = true; heroReloadTicks = 0; updateAmmoBar(); return; } if (LK.ticks - hero.lastShotTick < (rapidFireTicks > 0 ? 6 : hero.shootCooldown)) return; var b = new Bullet(); b.x = hero.x; b.y = hero.y; var dx = tx - hero.x; var dy = ty - hero.y; var dist = Math.sqrt(dx * dx + dy * dy); var speed = 38; b.vx = speed * dx / dist; b.vy = speed * dy / dist; bullets.push(b); game.addChild(b); hero.lastShotTick = LK.ticks; heroAmmo--; if (heroAmmo <= 0) { heroReloading = true; heroReloadTicks = 0; } updateAmmoBar(); LK.getSound('shoot').play(); } // Helper: distance between two objects function dist2(a, b) { var dx = a.x - b.x; var dy = a.y - b.y; return Math.sqrt(dx * dx + dy * dy); } // Dragging function handleMove(x, y, obj) { if (dragNode) { // Clamp hero inside game area var r = hero.radius; dragNode.x = Math.max(r, Math.min(GAME_W - r, x)); dragNode.y = Math.max(r, Math.min(GAME_H - r, y)); } } game.move = handleMove; game.down = function (x, y, obj) { // Only drag if menu is not active and touch is on hero if (!menuActive && dist2({ x: x, y: y }, hero) <= hero.radius * 1.2) { dragNode = hero; } }; game.up = function (x, y, obj) { dragNode = null; if (menuActive) return; // Shoot bullet towards release point if not dragging if (dist2({ x: x, y: y }, hero) > hero.radius * 1.2) { shootBullet(x, y); } }; // Main update loop game.update = function () { if (menuActive) return; // No menuActive check; game always runs ticksSinceStart++; // Increase wave every 900 ticks (~15s) if (ticksSinceStart % 900 === 0) { wave++; if (zombieSpawnInterval > 30) zombieSpawnInterval -= 8; } // Spawn zombies if (LK.ticks - zombieSpawnTick > zombieSpawnInterval) { // More zombies per spawn as wave increases var zombiesToSpawn = 1 + Math.floor(wave / 4); for (var n = 0; n < zombiesToSpawn; n++) { spawnZombie(); } zombieSpawnTick = LK.ticks; } // Maybe spawn powerup if (powerups.length === 0 && Math.random() < 0.002) { spawnPowerup(); } // Ammo reload logic if (heroReloading) { heroReloadTicks++; if (heroReloadTicks >= heroReloadTime) { heroAmmo = heroMaxAmmo; heroReloading = false; heroReloadTicks = 0; updateAmmoBar(); } else { updateAmmoBar(); } } // Update hero (nothing for now) hero.update(); // Update zombies for (var i = zombies.length - 1; i >= 0; i--) { var z = zombies[i]; z.update(); // Check collision with hero if (dist2(z, hero) < z.radius + hero.radius - 10) { LK.effects.flashScreen(0xff0000, 400); heroHp--; updateHealthBar(); if (heroHp <= 0) { if (score > highScore) { storage.highScore = score; } heroAmmo = heroMaxAmmo; heroReloading = false; heroReloadTicks = 0; updateAmmoBar(); LK.showGameOver(); showClassicMenu(); return; } // Remove zombie on hit to prevent instant double hit z.destroy(); zombies.splice(i, 1); continue; } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; b.update(); // Remove if out of bounds or expired if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100 || b.lifetime <= 0) { b.destroy(); bullets.splice(i, 1); continue; } // Check collision with zombies for (var j = zombies.length - 1; j >= 0; j--) { var z = zombies[j]; if (dist2(b, z) < b.radius + z.radius - 10) { // Damage zombie if (typeof z.hp === "number") { z.hp--; LK.effects.flashObject(z, 0xffffff, 120); if (z.hp <= 0) { LK.getSound('zombie_die').play(); score++; scoreTxt.setText(score); if (score > highScore) { highScore = score; highScoreTxt.setText('Best: ' + highScore); storage.highScore = highScore; } z.destroy(); zombies.splice(j, 1); } } else { // fallback: destroy if no hp LK.getSound('zombie_die').play(); score++; scoreTxt.setText(score); if (score > highScore) { highScore = score; highScoreTxt.setText('Best: ' + highScore); storage.highScore = highScore; } z.destroy(); zombies.splice(j, 1); } b.destroy(); bullets.splice(i, 1); break; } } } // Update powerups for (var i = powerups.length - 1; i >= 0; i--) { var p = powerups[i]; // Check collision with hero if (dist2(p, hero) < p.radius + hero.radius - 10) { LK.getSound('powerup').play(); rapidFireTicks = 360; // 6 seconds powerupTxt.setText('Rapid Fire!'); LK.effects.flashObject(hero, 0x3b7dd8, 400); p.destroy(); powerups.splice(i, 1); } } // Powerup timer if (rapidFireTicks > 0) { rapidFireTicks--; if (rapidFireTicks === 0) { powerupTxt.setText(''); } } // Update powerup text alpha for effect if (rapidFireTicks > 0) { powerupTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 6); } else { powerupTxt.alpha = 1; } }; // Start music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.7, duration: 1200 } });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletSprite.width / 2;
self.vx = 0;
self.vy = 0;
self.lifetime = 60; // frames
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.lifetime--;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroSprite = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.heroSprite = heroSprite;
// Add gun sprite to hero's hand
var gunSprite = self.attachAsset('gun', {
anchorX: 0.2,
// grip of gun
anchorY: 0.5,
width: heroSprite.width * 0.45,
height: heroSprite.height * 0.22
});
gunSprite.x = heroSprite.width * 0.32; // offset to right hand
gunSprite.y = 0;
self.radius = heroSprite.width / 2;
self.speed = 18; // pixels per move
self.lastShotTick = 0;
self.shootCooldown = 18; // frames between shots
// Allow skin change
self.setSkin = function (skinId) {
if (!skinId || skinId === "hero") {
// Already default
return;
}
// Remove old sprite
if (self.heroSprite && self.heroSprite.parent) {
self.removeChild(self.heroSprite);
}
// Add new sprite
var newSprite = self.attachAsset(skinId, {
anchorX: 0.5,
anchorY: 0.5,
width: heroSprite.width,
height: heroSprite.height
});
self.heroSprite = newSprite;
// Move gun to top
if (gunSprite && gunSprite.parent) {
self.removeChild(gunSprite);
self.addChild(gunSprite);
}
// Update radius
self.radius = newSprite.width / 2;
};
self.update = function () {};
return self;
});
// Powerup class
var Powerup = Container.expand(function () {
var self = Container.call(this);
var powerupSprite = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = powerupSprite.width / 2;
self.type = 'rapid'; // only one type for MVP
self.update = function () {};
return self;
});
// Zombie class
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Default to normal zombie asset
var zombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.zombieSprite = zombieSprite;
self.radius = zombieSprite.width / 2;
self.baseSpeed = 3 + Math.random() * 2; // base speed, will be increased by wave
self.speed = self.baseSpeed;
self.target = null; // set to hero
self.maxHp = 1;
self.hp = 1;
self.wave = 1;
self.type = "normal"; // can be "normal", "fast", "tank", "crawler"
self.setWave = function (wave) {
self.wave = wave;
// Remove old sprite if present
if (self.zombieSprite && self.zombieSprite.parent) {
self.removeChild(self.zombieSprite);
}
// Decide type
var r = Math.random();
if (wave >= 7 && r < 0.18) {
// Crawler: slow, small, hard to hit
self.type = "crawler";
self.speed = 1.7 + 0.12 * wave;
self.maxHp = 2 + Math.floor(wave / 3);
self.hp = self.maxHp;
self.zombieSprite = self.attachAsset('zombie_crawler', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x8e8e8e; // grayish
} else if (wave >= 6 && r < 0.38) {
// Tank zombie: slow but high HP
self.type = "tank";
self.speed = 2 + 0.15 * wave;
self.maxHp = 3 + Math.floor(wave / 2);
self.hp = self.maxHp;
self.zombieSprite = self.attachAsset('zombie_tank', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x6e4e2e; // brownish
} else if (wave >= 3 && r < 0.63) {
// Fast zombie: high speed, low HP
self.type = "fast";
self.speed = 5 + 0.4 * wave;
self.maxHp = 1;
self.hp = 1;
self.zombieSprite = self.attachAsset('zombie_fast', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x7cf442; // bright green
} else {
// Normal zombie
self.type = "normal";
self.speed = self.baseSpeed + (wave - 1) * 0.5;
self.maxHp = 1 + Math.floor(wave / 4);
self.hp = self.maxHp;
self.zombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = self.zombieSprite.width / 2;
self.zombieSprite.tint = 0x7bbf3f; // normal green
}
};
// Health bar setup
var healthBarW = zombieSprite.width * 0.7;
var healthBarH = 16;
var healthBarBg = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarW,
height: healthBarH,
color: 0x222222
});
healthBarBg.alpha = 0.7;
var healthBarFill = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarW,
height: healthBarH,
color: 0x3FA34D
});
healthBarFill.alpha = 0.92;
self.addChild(healthBarBg);
self.addChild(healthBarFill);
self.update = function () {
if (!self.target) return;
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += self.speed * dx / dist;
self.y += self.speed * dy / dist;
}
// Update health bar position and fill
var barY = -self.radius - 30;
healthBarBg.x = -healthBarW / 2;
healthBarBg.y = barY;
healthBarFill.x = -healthBarW / 2;
healthBarFill.y = barY;
var ratio = Math.max(0, Math.min(1, self.hp / self.maxHp));
healthBarFill.width = healthBarW * ratio;
if (ratio > 0.6) {
healthBarFill.tint = 0x3FA34D;
} else if (ratio > 0.3) {
healthBarFill.tint = 0xF7E733;
} else {
healthBarFill.tint = 0xD83318;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Game area
// Hero: red box
// Zombie: green ellipse
// Bullet: yellow box
// Powerup: blue ellipse
// Sound effects
// Music
// new gun asset
var GAME_W = 2048;
var GAME_H = 2732;
// State
var hero = null;
var zombies = [];
var bullets = [];
var powerups = [];
var dragNode = null;
var lastIntersecting = false;
var score = 0;
var highScore = storage.highScore || 0;
var wave = 1;
var ticksSinceStart = 0;
var zombieSpawnTick = 0;
var zombieSpawnInterval = 90; // frames
var rapidFireTicks = 0;
// --- HEALTH SYSTEM ---
var heroMaxHp = 5;
var heroHp = heroMaxHp;
// --- AMMO SYSTEM ---
var heroMaxAmmo = 12;
var heroAmmo = heroMaxAmmo;
var heroReloading = false;
var heroReloadTicks = 0;
var heroReloadTime = 60; // frames to reload
// Ammo bar UI
var ammoBarBg = new Container();
var ammoBarWidth = 420;
var ammoBarHeight = 28;
var ammoBarBgRect = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: ammoBarWidth,
height: ammoBarHeight,
color: 0x222222
});
ammoBarBgRect.alpha = 0.7;
ammoBarBg.addChild(ammoBarBgRect);
var ammoBarFill = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: ammoBarWidth,
height: ammoBarHeight,
color: 0x3B7DD8
});
ammoBarFill.alpha = 0.92;
ammoBarBg.addChild(ammoBarFill);
ammoBarBg.x = (GAME_W - ammoBarWidth) / 2;
ammoBarBg.y = 170;
LK.gui.top.addChild(ammoBarBg);
var ammoTxt = new Text2('', {
size: 60,
fill: "#fff"
});
ammoTxt.anchor.set(0.5, 0);
ammoTxt.x = GAME_W / 2;
ammoTxt.y = 210;
LK.gui.top.addChild(ammoTxt);
function updateAmmoBar() {
var ratio = Math.max(0, Math.min(1, heroAmmo / heroMaxAmmo));
ammoBarFill.width = ammoBarWidth * ratio;
if (heroReloading) {
ammoBarFill.tint = 0xF7E733;
ammoTxt.setText('RELOADING...');
} else {
ammoBarFill.tint = 0x3B7DD8;
ammoTxt.setText(heroAmmo + ' / ' + heroMaxAmmo);
}
}
updateAmmoBar();
// Health bar UI
var healthBarBg = new Container();
var healthBarWidth = 520;
var healthBarHeight = 38;
var healthBarBgRect = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarWidth,
height: healthBarHeight,
color: 0x333333
});
healthBarBgRect.alpha = 0.7;
healthBarBg.addChild(healthBarBgRect);
var healthBarFill = LK.getAsset('hero', {
anchorX: 0,
anchorY: 0,
width: healthBarWidth,
height: healthBarHeight,
color: 0x3FA34D
});
healthBarFill.alpha = 0.92;
healthBarBg.addChild(healthBarFill);
healthBarBg.x = (GAME_W - healthBarWidth) / 2;
healthBarBg.y = 110;
LK.gui.top.addChild(healthBarBg);
function updateHealthBar() {
var ratio = Math.max(0, Math.min(1, heroHp / heroMaxHp));
healthBarFill.width = healthBarWidth * ratio;
if (ratio > 0.6) {
healthBarFill.tint = 0x3FA34D;
} else if (ratio > 0.3) {
healthBarFill.tint = 0xF7E733;
} else {
healthBarFill.tint = 0xD83318;
}
}
updateHealthBar();
// Spawn hero at game start
menuActive = false;
if (hero) hero.destroy();
hero = new Hero();
hero.x = GAME_W / 2;
hero.y = GAME_H / 2;
game.addChild(hero);
// Reset state
// Move UI text declarations above their first use
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// High score text
var highScoreTxt = new Text2('Best: ' + highScore, {
size: 60,
fill: "#fff"
});
highScoreTxt.anchor.set(0.5, 0);
highScoreTxt.y = 120;
LK.gui.top.addChild(highScoreTxt);
// Powerup text
var powerupTxt = new Text2('', {
size: 70,
fill: 0x3B7DD8
});
powerupTxt.anchor.set(0.5, 0);
powerupTxt.y = 200;
LK.gui.top.addChild(powerupTxt);
heroHp = heroMaxHp;
updateHealthBar();
heroAmmo = heroMaxAmmo;
heroReloading = false;
heroReloadTicks = 0;
updateAmmoBar();
score = 0;
scoreTxt.setText(score);
wave = 1;
ticksSinceStart = 0;
zombieSpawnTick = 0;
zombieSpawnInterval = 90;
rapidFireTicks = 0;
for (var i = zombies.length - 1; i >= 0; i--) {
zombies[i].destroy();
zombies.splice(i, 1);
}
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
bullets.splice(i, 1);
}
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].destroy();
powerups.splice(i, 1);
}
powerupTxt.setText('');
// --- CLASSIC MENU ---
// Classic menu overlay and logic
var classicMenuOverlay = new Container();
var menuBg = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
width: 900,
height: 900,
color: 0x222222
});
menuBg.alpha = 0.92;
classicMenuOverlay.addChild(menuBg);
classicMenuOverlay.x = GAME_W / 2;
classicMenuOverlay.y = GAME_H / 2;
// Title
var menuTitle = new Text2('Zombie Survival', {
size: 120,
fill: "#fff"
});
menuTitle.anchor.set(0.5, 0);
menuTitle.y = -320;
classicMenuOverlay.addChild(menuTitle);
// Play button
var playBtn = LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 120
});
playBtn.y = 80;
classicMenuOverlay.addChild(playBtn);
var playBtnText = new Text2('PLAY', {
size: 90,
fill: "#fff"
});
playBtnText.anchor.set(0.5, 0.5);
playBtnText.x = playBtn.x;
playBtnText.y = playBtn.y;
classicMenuOverlay.addChild(playBtnText);
// Cheats tab button
var cheatsBtn = LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 100,
color: 0x4444aa
});
cheatsBtn.y = 240;
classicMenuOverlay.addChild(cheatsBtn);
var cheatsBtnText = new Text2('CHEATS', {
size: 70,
fill: "#fff"
});
cheatsBtnText.anchor.set(0.5, 0.5);
cheatsBtnText.x = cheatsBtn.x;
cheatsBtnText.y = cheatsBtn.y;
classicMenuOverlay.addChild(cheatsBtnText);
// High score display
var menuHighScore = new Text2('Best: ' + (storage.highScore || 0), {
size: 70,
fill: "#fff"
});
menuHighScore.anchor.set(0.5, 0);
menuHighScore.y = -120;
classicMenuOverlay.addChild(menuHighScore);
var menuActive = true;
game.addChild(classicMenuOverlay);
// Cheats tab overlay
var cheatsOverlay = new Container();
var cheatsBg = LK.getAsset('hero', {
anchorX: 0.5,
anchorY: 0.5,
width: 800,
height: 900,
color: 0x222244
});
cheatsBg.alpha = 0.97;
cheatsOverlay.addChild(cheatsBg);
cheatsOverlay.x = GAME_W / 2;
cheatsOverlay.y = GAME_H / 2;
// Cheats title
var cheatsTitle = new Text2('Cheats', {
size: 100,
fill: "#fff"
});
cheatsTitle.anchor.set(0.5, 0);
cheatsTitle.y = -340;
cheatsOverlay.addChild(cheatsTitle);
// Example cheat: Add 100 score
var cheatScoreBtn = LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
width: 340,
height: 110,
color: 0x3B7DD8
});
cheatScoreBtn.y = -120;
cheatsOverlay.addChild(cheatScoreBtn);
var cheatScoreBtnText = new Text2('Add 100 Score', {
size: 60,
fill: "#fff"
});
cheatScoreBtnText.anchor.set(0.5, 0.5);
cheatScoreBtnText.x = cheatScoreBtn.x;
cheatScoreBtnText.y = cheatScoreBtn.y;
cheatsOverlay.addChild(cheatScoreBtnText);
// Example cheat: Heal to full
var cheatHealBtn = LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
width: 340,
height: 110,
color: 0x3FA34D
});
cheatHealBtn.y = 40;
cheatsOverlay.addChild(cheatHealBtn);
var cheatHealBtnText = new Text2('Full Heal', {
size: 60,
fill: "#fff"
});
cheatHealBtnText.anchor.set(0.5, 0.5);
cheatHealBtnText.x = cheatHealBtn.x;
cheatHealBtnText.y = cheatHealBtn.y;
cheatsOverlay.addChild(cheatHealBtnText);
// Close cheats tab
var closeCheatsBtn = LK.getAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5,
width: 220,
height: 90,
color: 0xD83318
});
closeCheatsBtn.y = 300;
cheatsOverlay.addChild(closeCheatsBtn);
var closeCheatsBtnText = new Text2('CLOSE', {
size: 55,
fill: "#fff"
});
closeCheatsBtnText.anchor.set(0.5, 0.5);
closeCheatsBtnText.x = closeCheatsBtn.x;
closeCheatsBtnText.y = closeCheatsBtn.y;
cheatsOverlay.addChild(closeCheatsBtnText);
cheatsOverlay.visible = false;
game.addChild(cheatsOverlay);
// Show/hide logic
function showClassicMenu() {
menuActive = true;
classicMenuOverlay.visible = true;
cheatsOverlay.visible = false;
// Update high score display
menuHighScore.setText('Best: ' + (storage.highScore || 0));
}
function hideClassicMenu() {
menuActive = false;
classicMenuOverlay.visible = false;
cheatsOverlay.visible = false;
}
// Cheats tab button handler
cheatsBtn.interactive = true;
cheatsBtn.down = function () {
classicMenuOverlay.visible = false;
cheatsOverlay.visible = true;
};
// Close cheats tab handler
closeCheatsBtn.interactive = true;
closeCheatsBtn.down = function () {
cheatsOverlay.visible = false;
classicMenuOverlay.visible = true;
};
// Cheat: Add 100 score
cheatScoreBtn.interactive = true;
cheatScoreBtn.down = function () {
score += 100;
scoreTxt.setText(score);
if (score > highScore) {
highScore = score;
highScoreTxt.setText('Best: ' + highScore);
storage.highScore = highScore;
menuHighScore.setText('Best: ' + highScore);
}
};
// Cheat: Full heal
cheatHealBtn.interactive = true;
cheatHealBtn.down = function () {
heroHp = heroMaxHp;
updateHealthBar();
};
// Play button handler
playBtn.interactive = true;
playBtn.down = function () {
hideClassicMenu();
// Reset game state
if (hero) hero.destroy();
hero = new Hero();
hero.x = GAME_W / 2;
hero.y = GAME_H / 2;
game.addChild(hero);
heroHp = heroMaxHp;
updateHealthBar();
heroAmmo = heroMaxAmmo;
heroReloading = false;
heroReloadTicks = 0;
updateAmmoBar();
score = 0;
scoreTxt.setText(score);
wave = 1;
ticksSinceStart = 0;
zombieSpawnTick = 0;
zombieSpawnInterval = 90;
rapidFireTicks = 0;
for (var i = zombies.length - 1; i >= 0; i--) {
zombies[i].destroy();
zombies.splice(i, 1);
}
for (var i = bullets.length - 1; i >= 0; i--) {
bullets[i].destroy();
bullets.splice(i, 1);
}
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].destroy();
powerups.splice(i, 1);
}
powerupTxt.setText('');
};
showClassicMenu();
// Helper: spawn zombie at random edge
function spawnZombie() {
var z = new Zombie();
// Pick edge: 0=top,1=bottom,2=left,3=right
var edge = Math.floor(Math.random() * 4);
var margin = 80;
if (edge === 0) {
// top
z.x = margin + Math.random() * (GAME_W - 2 * margin);
z.y = -z.radius;
} else if (edge === 1) {
// bottom
z.x = margin + Math.random() * (GAME_W - 2 * margin);
z.y = GAME_H + z.radius;
} else if (edge === 2) {
// left
z.x = -z.radius;
z.y = margin + Math.random() * (GAME_H - 2 * margin);
} else {
// right
z.x = GAME_W + z.radius;
z.y = margin + Math.random() * (GAME_H - 2 * margin);
}
// Set wave and type
if (typeof z.setWave === "function") {
z.setWave(wave);
}
z.target = hero;
zombies.push(z);
game.addChild(z);
}
// Helper: spawn powerup
function spawnPowerup() {
var p = new Powerup();
p.x = 200 + Math.random() * (GAME_W - 400);
p.y = 300 + Math.random() * (GAME_H - 600);
powerups.push(p);
game.addChild(p);
}
// Helper: shoot bullet towards (tx,ty)
function shootBullet(tx, ty) {
if (heroReloading) return;
if (heroAmmo <= 0) {
// Start reload if not already
heroReloading = true;
heroReloadTicks = 0;
updateAmmoBar();
return;
}
if (LK.ticks - hero.lastShotTick < (rapidFireTicks > 0 ? 6 : hero.shootCooldown)) return;
var b = new Bullet();
b.x = hero.x;
b.y = hero.y;
var dx = tx - hero.x;
var dy = ty - hero.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var speed = 38;
b.vx = speed * dx / dist;
b.vy = speed * dy / dist;
bullets.push(b);
game.addChild(b);
hero.lastShotTick = LK.ticks;
heroAmmo--;
if (heroAmmo <= 0) {
heroReloading = true;
heroReloadTicks = 0;
}
updateAmmoBar();
LK.getSound('shoot').play();
}
// Helper: distance between two objects
function dist2(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Dragging
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp hero inside game area
var r = hero.radius;
dragNode.x = Math.max(r, Math.min(GAME_W - r, x));
dragNode.y = Math.max(r, Math.min(GAME_H - r, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only drag if menu is not active and touch is on hero
if (!menuActive && dist2({
x: x,
y: y
}, hero) <= hero.radius * 1.2) {
dragNode = hero;
}
};
game.up = function (x, y, obj) {
dragNode = null;
if (menuActive) return;
// Shoot bullet towards release point if not dragging
if (dist2({
x: x,
y: y
}, hero) > hero.radius * 1.2) {
shootBullet(x, y);
}
};
// Main update loop
game.update = function () {
if (menuActive) return;
// No menuActive check; game always runs
ticksSinceStart++;
// Increase wave every 900 ticks (~15s)
if (ticksSinceStart % 900 === 0) {
wave++;
if (zombieSpawnInterval > 30) zombieSpawnInterval -= 8;
}
// Spawn zombies
if (LK.ticks - zombieSpawnTick > zombieSpawnInterval) {
// More zombies per spawn as wave increases
var zombiesToSpawn = 1 + Math.floor(wave / 4);
for (var n = 0; n < zombiesToSpawn; n++) {
spawnZombie();
}
zombieSpawnTick = LK.ticks;
}
// Maybe spawn powerup
if (powerups.length === 0 && Math.random() < 0.002) {
spawnPowerup();
}
// Ammo reload logic
if (heroReloading) {
heroReloadTicks++;
if (heroReloadTicks >= heroReloadTime) {
heroAmmo = heroMaxAmmo;
heroReloading = false;
heroReloadTicks = 0;
updateAmmoBar();
} else {
updateAmmoBar();
}
}
// Update hero (nothing for now)
hero.update();
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var z = zombies[i];
z.update();
// Check collision with hero
if (dist2(z, hero) < z.radius + hero.radius - 10) {
LK.effects.flashScreen(0xff0000, 400);
heroHp--;
updateHealthBar();
if (heroHp <= 0) {
if (score > highScore) {
storage.highScore = score;
}
heroAmmo = heroMaxAmmo;
heroReloading = false;
heroReloadTicks = 0;
updateAmmoBar();
LK.showGameOver();
showClassicMenu();
return;
}
// Remove zombie on hit to prevent instant double hit
z.destroy();
zombies.splice(i, 1);
continue;
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Remove if out of bounds or expired
if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100 || b.lifetime <= 0) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with zombies
for (var j = zombies.length - 1; j >= 0; j--) {
var z = zombies[j];
if (dist2(b, z) < b.radius + z.radius - 10) {
// Damage zombie
if (typeof z.hp === "number") {
z.hp--;
LK.effects.flashObject(z, 0xffffff, 120);
if (z.hp <= 0) {
LK.getSound('zombie_die').play();
score++;
scoreTxt.setText(score);
if (score > highScore) {
highScore = score;
highScoreTxt.setText('Best: ' + highScore);
storage.highScore = highScore;
}
z.destroy();
zombies.splice(j, 1);
}
} else {
// fallback: destroy if no hp
LK.getSound('zombie_die').play();
score++;
scoreTxt.setText(score);
if (score > highScore) {
highScore = score;
highScoreTxt.setText('Best: ' + highScore);
storage.highScore = highScore;
}
z.destroy();
zombies.splice(j, 1);
}
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Update powerups
for (var i = powerups.length - 1; i >= 0; i--) {
var p = powerups[i];
// Check collision with hero
if (dist2(p, hero) < p.radius + hero.radius - 10) {
LK.getSound('powerup').play();
rapidFireTicks = 360; // 6 seconds
powerupTxt.setText('Rapid Fire!');
LK.effects.flashObject(hero, 0x3b7dd8, 400);
p.destroy();
powerups.splice(i, 1);
}
}
// Powerup timer
if (rapidFireTicks > 0) {
rapidFireTicks--;
if (rapidFireTicks === 0) {
powerupTxt.setText('');
}
}
// Update powerup text alpha for effect
if (rapidFireTicks > 0) {
powerupTxt.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 6);
} else {
powerupTxt.alpha = 1;
}
};
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.7,
duration: 1200
}
});
zombie. In-Game asset. 2d. High contrast. No shadows
bullet. In-Game asset. 2d. High contrast. No shadows
power up. In-Game asset. 2d. High contrast. No shadows
pistol. In-Game asset. 2d. High contrast. No shadows
crawler zombie. In-Game asset. 2d. High contrast. No shadows
runner zombie. In-Game asset. 2d. High contrast. No shadows
tank zombie. In-Game asset. 2d. High contrast. No shadows
man. In-Game asset. 2d. High contrast. No shadows