/**** * Plugins ****/ var tween = LK.import("@upit/tween.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.speed = 32; self.dirX = 0; self.dirY = 0; self.lastIntersecting = false; self.lastY = self.y; 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.radius = heroSprite.width / 2; // For powerup state self.powered = false; self.powerTimer = 0; // Flash effect for powerup self.flashTween = null; // Powerup visual effect self.setPowered = function (on) { if (on) { self.powered = true; // Flash hero yellow if (self.flashTween) tween.stop(heroSprite, { tint: true }); heroSprite.tint = 0xffff00; self.flashTween = tween(heroSprite, { tint: 0xd83318 }, { duration: 400, easing: tween.linear, onFinish: function onFinish() { heroSprite.tint = 0xffff00; self.flashTween = tween(heroSprite, { tint: 0xd83318 }, { duration: 400, easing: tween.linear, onFinish: function onFinish() { heroSprite.tint = 0xffff00; } }); } }); } else { self.powered = false; if (self.flashTween) tween.stop(heroSprite, { tint: true }); heroSprite.tint = 0xd83318; } }; return self; }); // MegaZombie class var MegaZombie = Container.expand(function () { var self = Container.call(this); var megaZombieSprite = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); // Make it visually bigger megaZombieSprite.scaleX = 2; megaZombieSprite.scaleY = 2; self.radius = megaZombieSprite.width; // much bigger self.speed = 1.2; // slower than normal zombies self.targetX = 0; self.targetY = 0; self.alive = true; self.hp = 8; // takes 8 hits to kill self.lastIntersecting = false; self.lastY = self.y; return self; }); // Zombie class var Zombie = Container.expand(function () { var self = Container.call(this); var zombieSprite = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.radius = zombieSprite.width / 2; self.speed = 2; // Will be set on spawn self.targetX = 0; self.targetY = 0; self.alive = true; self.lastIntersecting = false; self.lastY = self.y; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Music // Sound for shooting // Powerup: purple ellipse // Base: blue box // Bullet: yellow box // Zombie: green ellipse // Hero: red box // Game area var GAME_W = 2048; var GAME_H = 2732; // Base var base = game.addChild(LK.getAsset('base', { anchorX: 0.5, anchorY: 0.5 })); base.x = GAME_W / 2; base.y = GAME_H - 120; // Hero var hero = new Hero(); game.addChild(hero); hero.x = GAME_W / 2; hero.y = GAME_H - 350; // Score var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Bullet count text (top left, avoid 100x100 area) var bulletCountTxt = new Text2('20', { size: 90, fill: "#fff" }); bulletCountTxt.anchor.set(0, 0); LK.gui.top.addChild(bulletCountTxt); bulletCountTxt.x = 120; // leave 120px margin from left bulletCountTxt.y = 10; // leave 10px margin from top // High Score (not persistent, just for session) var highScore = 0; // Timer var elapsedFrames = 0; var timeTxt = new Text2('00:00', { size: 70, fill: "#fff" }); timeTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timeTxt); timeTxt.y = 200; // Wave var wave = 1; var waveTxt = new Text2('Wave 1', { size: 70, fill: "#fff" }); waveTxt.anchor.set(0.5, 0); LK.gui.top.addChild(waveTxt); waveTxt.y = 120; // Arrays for game objects var zombies = []; var megaZombies = []; var bullets = []; var powerups = []; // Gold var gold = 0; var goldTxt = new Text2('Gold: 0', { size: 70, fill: 0xFFE066 }); goldTxt.anchor.set(0.5, 0); LK.gui.top.addChild(goldTxt); goldTxt.y = 320; // Dragging var dragNode = null; // Shooting cooldown var shootCooldown = 0; var shootInterval = 18; // frames (0.3s at 60fps) var rapidFire = false; // Hero refresh after 10 shots var heroShots = 0; var heroNeedsRefresh = false; // Zombie spawn var zombieSpawnTimer = 0; var zombieSpawnInterval = 90; // frames (1.5s) var zombiesPerWave = 6; var zombiesSpawned = 0; var zombiesToSpawn = zombiesPerWave; // Game state var gameOver = false; // Start music LK.playMusic('bgmusic', { fade: { start: 0, end: 1, duration: 1000 } }); // Helper: clamp hero inside game area function clampHero() { var margin = hero.radius + 20; if (hero.x < margin) hero.x = margin; if (hero.x > GAME_W - margin) hero.x = GAME_W - margin; if (hero.y < margin + 100) hero.y = margin + 100; // avoid top menu if (hero.y > base.y - hero.radius - 40) hero.y = base.y - hero.radius - 40; } // Helper: spawn a zombie at random edge, target base function spawnZombie() { // Decide if this should be a mega zombie var isMega = false; // Start spawning mega zombies at wave 4, increase chance as wave rises if (wave >= 4) { // 10% at wave 4, 20% at wave 6, 30% at wave 8, max 40% var megaChance = Math.min(0.1 + 0.05 * (wave - 4), 0.4); if (Math.random() < megaChance) isMega = true; } // Random edge: 0=top, 1=left, 2=right var edge = Math.floor(Math.random() * 3); var zx, zy; if (edge === 0) { // top zx = 200 + Math.random() * (GAME_W - 400); zy = -80; } else if (edge === 1) { // left zx = -80; zy = 400 + Math.random() * (GAME_H - 1000); } else { // right zx = GAME_W + 80; zy = 400 + Math.random() * (GAME_H - 1000); } if (isMega) { var mz = new MegaZombie(); mz.x = zx; mz.y = zy; mz.targetX = base.x; mz.targetY = base.y; // Slightly increase speed with wave, but always slower than normal zombies mz.speed = 1.2 + (wave - 4) * 0.1 + Math.random() * 0.2; megaZombies.push(mz); game.addChild(mz); } else { var z = new Zombie(); z.x = zx; z.y = zy; z.targetX = base.x; z.targetY = base.y; z.speed = 2 + wave * 0.5 + Math.random(); zombies.push(z); game.addChild(z); } } // Helper: shoot bullet towards (tx, ty) function shootBullet(tx, ty) { if (shootCooldown > 0) return; if (heroNeedsRefresh) return; // Count shots and check for refresh heroShots++; bulletCountTxt.setText(20 - heroShots); // update bullet count display if (heroShots >= 20) { heroNeedsRefresh = true; // Optionally, flash hero to indicate refresh needed LK.effects.flashObject(hero, 0x888888, 800); // Automatically refresh after 20 shots for şampiyon LK.setTimeout(function () { heroNeedsRefresh = false; heroShots = 0; bulletCountTxt.setText(20); // reset bullet count display after refresh }, 800); // 800ms matches the flash duration return; } var b = new Bullet(); b.x = hero.x; b.y = hero.y; // Direction var dx = tx - hero.x; var dy = ty - hero.y; var len = Math.sqrt(dx * dx + dy * dy); if (len === 0) { b.dirX = 0; b.dirY = -1; } else { b.dirX = dx / len; b.dirY = dy / len; } // If powered, increase speed b.speed = hero.powered ? 48 : 32; bullets.push(b); game.addChild(b); shootCooldown = hero.powered ? 6 : shootInterval; // Play shoot sound every time a bullet is fired LK.getSound('shoot').play(); } // Helper: start new wave function startWave() { waveTxt.setText('Wave ' + wave); zombiesPerWave = 6 + (wave - 1) * 2; zombiesToSpawn = zombiesPerWave; zombiesSpawned = 0; zombieSpawnInterval = Math.max(40, 90 - wave * 5); // Clean up any remaining mega zombies (shouldn't be any, but for safety) for (var i = megaZombies.length - 1; i >= 0; i--) { if (megaZombies[i]) { megaZombies[i].destroy(); } } megaZombies = []; } // Move handler: drag hero, shoot on tap function handleMove(x, y, obj) { // Drag hero if dragNode is set if (dragNode === hero) { hero.x = x; hero.y = y; clampHero(); // Reset hero refresh after dragging if (heroNeedsRefresh) { heroNeedsRefresh = false; heroShots = 0; bulletCountTxt.setText(20); // reset bullet count display after drag refresh } } } // Down: start dragging hero or shoot if tap on zombie game.down = function (x, y, obj) { // Check if tap on zombie for (var i = 0; i < zombies.length; i++) { var z = zombies[i]; var dx = x - z.x; var dy = y - z.y; if (dx * dx + dy * dy < z.radius * z.radius) { // Shoot at zombie shootBullet(z.x, z.y); return; } } // Otherwise, drag hero var dxh = x - hero.x; var dyh = y - hero.y; if (dxh * dxh + dyh * dyh < hero.radius * hero.radius) { dragNode = hero; } // Also allow shooting by tapping anywhere else if (!dragNode) { shootBullet(x, y); } handleMove(x, y, obj); }; game.move = handleMove; game.up = function (x, y, obj) { dragNode = null; }; // Main update loop game.update = function () { if (gameOver) return; // Timer update elapsedFrames++; var seconds = Math.floor(elapsedFrames / 60); var min = Math.floor(seconds / 60); var sec = seconds % 60; var timeStr = (min < 10 ? "0" : "") + min + ":" + (sec < 10 ? "0" : "") + sec; timeTxt.setText(timeStr); // Every 20 seconds, increase zombies per wave and make them spawn faster if (elapsedFrames % (60 * 20) === 0 && elapsedFrames > 0) { zombiesPerWave += 2; zombieSpawnInterval = Math.max(20, zombieSpawnInterval - 5); } // Cooldowns if (shootCooldown > 0) shootCooldown--; // Powerup timer if (hero.powered) { hero.powerTimer--; if (hero.powerTimer <= 0) { hero.setPowered(false); } } // Zombie spawn if (zombiesToSpawn > 0) { zombieSpawnTimer++; if (zombieSpawnTimer >= zombieSpawnInterval) { zombieSpawnTimer = 0; spawnZombie(); zombiesToSpawn--; zombiesSpawned++; } } else if (zombies.length === 0) { // Next wave if (wave >= 10) { // End game at wave 10 LK.effects.flashScreen(0x00ff00, 1000); LK.showYouWin(); gameOver = true; return; } wave++; startWave(); waveTxt.setText('Wave ' + wave + (wave >= 4 ? ' - Mega Zombies!' : '')); } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; b.x += b.dirX * b.speed; b.y += b.dirY * b.speed; // Remove if off screen if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100) { b.destroy(); bullets.splice(i, 1); continue; } // Check collision with zombies var bulletDestroyed = false; for (var j = zombies.length - 1; j >= 0; j--) { var z = zombies[j]; var dx = b.x - z.x; var dy = b.y - z.y; var dist2 = dx * dx + dy * dy; var r2 = (b.radius + z.radius) * (b.radius + z.radius); if (dist2 < r2) { // Hit! LK.getSound('zombiehit').play(); score++; scoreTxt.setText(score); if (score > highScore) highScore = score; // Earn gold for killing zombie gold += 5; goldTxt.setText('Gold: ' + gold); // Remove zombie and bullet z.destroy(); zombies.splice(j, 1); b.destroy(); bullets.splice(i, 1); bulletDestroyed = true; break; } } if (bulletDestroyed) continue; // Check collision with mega zombies for (var j = megaZombies.length - 1; j >= 0; j--) { var mz = megaZombies[j]; var dx = b.x - mz.x; var dy = b.y - mz.y; var dist2 = dx * dx + dy * dy; var r2 = (b.radius + mz.radius) * (b.radius + mz.radius); if (dist2 < r2) { // Hit mega zombie! LK.getSound('zombiehit').play(); mz.hp--; // Flash mega zombie on hit LK.effects.flashObject(mz, 0xffff00, 200); if (mz.hp <= 0) { // Kill mega zombie score += 5; // Worth more points scoreTxt.setText(score); if (score > highScore) highScore = score; gold += 25; // More gold goldTxt.setText('Gold: ' + gold); mz.destroy(); megaZombies.splice(j, 1); } // Destroy bullet b.destroy(); bullets.splice(i, 1); break; } } } // Update zombies for (var i = zombies.length - 1; i >= 0; i--) { var z = zombies[i]; // Move towards base var dx = base.x - z.x; var dy = base.y - z.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { z.x += dx / len * z.speed; z.y += dy / len * z.speed; } // Check collision with base var dbx = z.x - base.x; var dby = z.y - base.y; var baseR = base.width / 2 + z.radius - 10; if (dbx * dbx + dby * dby < baseR * baseR) { // Game over LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); gameOver = true; return; } // Check collision with hero (for fun, just flash) var dhx = z.x - hero.x; var dhy = z.y - hero.y; var heroR = hero.radius + z.radius; if (dhx * dhx + dhy * dhy < heroR * heroR) { LK.effects.flashObject(hero, 0xff0000, 300); } } // Update mega zombies for (var i = megaZombies.length - 1; i >= 0; i--) { var mz = megaZombies[i]; // Move towards base var dx = base.x - mz.x; var dy = base.y - mz.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { mz.x += dx / len * mz.speed; mz.y += dy / len * mz.speed; } // Check collision with base var dbx = mz.x - base.x; var dby = mz.y - base.y; var baseR = base.width / 2 + mz.radius - 10; if (dbx * dbx + dby * dby < baseR * baseR) { // Game over LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); gameOver = true; return; } // Check collision with hero (for fun, just flash) var dhx = mz.x - hero.x; var dhy = mz.y - hero.y; var heroR = hero.radius + mz.radius; if (dhx * dhx + dhy * dhy < heroR * heroR) { LK.effects.flashObject(hero, 0xff0000, 300); } } }; // Start first wave startWave();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.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.speed = 32;
self.dirX = 0;
self.dirY = 0;
self.lastIntersecting = false;
self.lastY = self.y;
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.radius = heroSprite.width / 2;
// For powerup state
self.powered = false;
self.powerTimer = 0;
// Flash effect for powerup
self.flashTween = null;
// Powerup visual effect
self.setPowered = function (on) {
if (on) {
self.powered = true;
// Flash hero yellow
if (self.flashTween) tween.stop(heroSprite, {
tint: true
});
heroSprite.tint = 0xffff00;
self.flashTween = tween(heroSprite, {
tint: 0xd83318
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
heroSprite.tint = 0xffff00;
self.flashTween = tween(heroSprite, {
tint: 0xd83318
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
heroSprite.tint = 0xffff00;
}
});
}
});
} else {
self.powered = false;
if (self.flashTween) tween.stop(heroSprite, {
tint: true
});
heroSprite.tint = 0xd83318;
}
};
return self;
});
// MegaZombie class
var MegaZombie = Container.expand(function () {
var self = Container.call(this);
var megaZombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
// Make it visually bigger
megaZombieSprite.scaleX = 2;
megaZombieSprite.scaleY = 2;
self.radius = megaZombieSprite.width; // much bigger
self.speed = 1.2; // slower than normal zombies
self.targetX = 0;
self.targetY = 0;
self.alive = true;
self.hp = 8; // takes 8 hits to kill
self.lastIntersecting = false;
self.lastY = self.y;
return self;
});
// Zombie class
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = zombieSprite.width / 2;
self.speed = 2; // Will be set on spawn
self.targetX = 0;
self.targetY = 0;
self.alive = true;
self.lastIntersecting = false;
self.lastY = self.y;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Music
// Sound for shooting
// Powerup: purple ellipse
// Base: blue box
// Bullet: yellow box
// Zombie: green ellipse
// Hero: red box
// Game area
var GAME_W = 2048;
var GAME_H = 2732;
// Base
var base = game.addChild(LK.getAsset('base', {
anchorX: 0.5,
anchorY: 0.5
}));
base.x = GAME_W / 2;
base.y = GAME_H - 120;
// Hero
var hero = new Hero();
game.addChild(hero);
hero.x = GAME_W / 2;
hero.y = GAME_H - 350;
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Bullet count text (top left, avoid 100x100 area)
var bulletCountTxt = new Text2('20', {
size: 90,
fill: "#fff"
});
bulletCountTxt.anchor.set(0, 0);
LK.gui.top.addChild(bulletCountTxt);
bulletCountTxt.x = 120; // leave 120px margin from left
bulletCountTxt.y = 10; // leave 10px margin from top
// High Score (not persistent, just for session)
var highScore = 0;
// Timer
var elapsedFrames = 0;
var timeTxt = new Text2('00:00', {
size: 70,
fill: "#fff"
});
timeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timeTxt);
timeTxt.y = 200;
// Wave
var wave = 1;
var waveTxt = new Text2('Wave 1', {
size: 70,
fill: "#fff"
});
waveTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(waveTxt);
waveTxt.y = 120;
// Arrays for game objects
var zombies = [];
var megaZombies = [];
var bullets = [];
var powerups = [];
// Gold
var gold = 0;
var goldTxt = new Text2('Gold: 0', {
size: 70,
fill: 0xFFE066
});
goldTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(goldTxt);
goldTxt.y = 320;
// Dragging
var dragNode = null;
// Shooting cooldown
var shootCooldown = 0;
var shootInterval = 18; // frames (0.3s at 60fps)
var rapidFire = false;
// Hero refresh after 10 shots
var heroShots = 0;
var heroNeedsRefresh = false;
// Zombie spawn
var zombieSpawnTimer = 0;
var zombieSpawnInterval = 90; // frames (1.5s)
var zombiesPerWave = 6;
var zombiesSpawned = 0;
var zombiesToSpawn = zombiesPerWave;
// Game state
var gameOver = false;
// Start music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
// Helper: clamp hero inside game area
function clampHero() {
var margin = hero.radius + 20;
if (hero.x < margin) hero.x = margin;
if (hero.x > GAME_W - margin) hero.x = GAME_W - margin;
if (hero.y < margin + 100) hero.y = margin + 100; // avoid top menu
if (hero.y > base.y - hero.radius - 40) hero.y = base.y - hero.radius - 40;
}
// Helper: spawn a zombie at random edge, target base
function spawnZombie() {
// Decide if this should be a mega zombie
var isMega = false;
// Start spawning mega zombies at wave 4, increase chance as wave rises
if (wave >= 4) {
// 10% at wave 4, 20% at wave 6, 30% at wave 8, max 40%
var megaChance = Math.min(0.1 + 0.05 * (wave - 4), 0.4);
if (Math.random() < megaChance) isMega = true;
}
// Random edge: 0=top, 1=left, 2=right
var edge = Math.floor(Math.random() * 3);
var zx, zy;
if (edge === 0) {
// top
zx = 200 + Math.random() * (GAME_W - 400);
zy = -80;
} else if (edge === 1) {
// left
zx = -80;
zy = 400 + Math.random() * (GAME_H - 1000);
} else {
// right
zx = GAME_W + 80;
zy = 400 + Math.random() * (GAME_H - 1000);
}
if (isMega) {
var mz = new MegaZombie();
mz.x = zx;
mz.y = zy;
mz.targetX = base.x;
mz.targetY = base.y;
// Slightly increase speed with wave, but always slower than normal zombies
mz.speed = 1.2 + (wave - 4) * 0.1 + Math.random() * 0.2;
megaZombies.push(mz);
game.addChild(mz);
} else {
var z = new Zombie();
z.x = zx;
z.y = zy;
z.targetX = base.x;
z.targetY = base.y;
z.speed = 2 + wave * 0.5 + Math.random();
zombies.push(z);
game.addChild(z);
}
}
// Helper: shoot bullet towards (tx, ty)
function shootBullet(tx, ty) {
if (shootCooldown > 0) return;
if (heroNeedsRefresh) return;
// Count shots and check for refresh
heroShots++;
bulletCountTxt.setText(20 - heroShots); // update bullet count display
if (heroShots >= 20) {
heroNeedsRefresh = true;
// Optionally, flash hero to indicate refresh needed
LK.effects.flashObject(hero, 0x888888, 800);
// Automatically refresh after 20 shots for şampiyon
LK.setTimeout(function () {
heroNeedsRefresh = false;
heroShots = 0;
bulletCountTxt.setText(20); // reset bullet count display after refresh
}, 800); // 800ms matches the flash duration
return;
}
var b = new Bullet();
b.x = hero.x;
b.y = hero.y;
// Direction
var dx = tx - hero.x;
var dy = ty - hero.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) {
b.dirX = 0;
b.dirY = -1;
} else {
b.dirX = dx / len;
b.dirY = dy / len;
}
// If powered, increase speed
b.speed = hero.powered ? 48 : 32;
bullets.push(b);
game.addChild(b);
shootCooldown = hero.powered ? 6 : shootInterval;
// Play shoot sound every time a bullet is fired
LK.getSound('shoot').play();
}
// Helper: start new wave
function startWave() {
waveTxt.setText('Wave ' + wave);
zombiesPerWave = 6 + (wave - 1) * 2;
zombiesToSpawn = zombiesPerWave;
zombiesSpawned = 0;
zombieSpawnInterval = Math.max(40, 90 - wave * 5);
// Clean up any remaining mega zombies (shouldn't be any, but for safety)
for (var i = megaZombies.length - 1; i >= 0; i--) {
if (megaZombies[i]) {
megaZombies[i].destroy();
}
}
megaZombies = [];
}
// Move handler: drag hero, shoot on tap
function handleMove(x, y, obj) {
// Drag hero if dragNode is set
if (dragNode === hero) {
hero.x = x;
hero.y = y;
clampHero();
// Reset hero refresh after dragging
if (heroNeedsRefresh) {
heroNeedsRefresh = false;
heroShots = 0;
bulletCountTxt.setText(20); // reset bullet count display after drag refresh
}
}
}
// Down: start dragging hero or shoot if tap on zombie
game.down = function (x, y, obj) {
// Check if tap on zombie
for (var i = 0; i < zombies.length; i++) {
var z = zombies[i];
var dx = x - z.x;
var dy = y - z.y;
if (dx * dx + dy * dy < z.radius * z.radius) {
// Shoot at zombie
shootBullet(z.x, z.y);
return;
}
}
// Otherwise, drag hero
var dxh = x - hero.x;
var dyh = y - hero.y;
if (dxh * dxh + dyh * dyh < hero.radius * hero.radius) {
dragNode = hero;
}
// Also allow shooting by tapping anywhere else
if (!dragNode) {
shootBullet(x, y);
}
handleMove(x, y, obj);
};
game.move = handleMove;
game.up = function (x, y, obj) {
dragNode = null;
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Timer update
elapsedFrames++;
var seconds = Math.floor(elapsedFrames / 60);
var min = Math.floor(seconds / 60);
var sec = seconds % 60;
var timeStr = (min < 10 ? "0" : "") + min + ":" + (sec < 10 ? "0" : "") + sec;
timeTxt.setText(timeStr);
// Every 20 seconds, increase zombies per wave and make them spawn faster
if (elapsedFrames % (60 * 20) === 0 && elapsedFrames > 0) {
zombiesPerWave += 2;
zombieSpawnInterval = Math.max(20, zombieSpawnInterval - 5);
}
// Cooldowns
if (shootCooldown > 0) shootCooldown--;
// Powerup timer
if (hero.powered) {
hero.powerTimer--;
if (hero.powerTimer <= 0) {
hero.setPowered(false);
}
}
// Zombie spawn
if (zombiesToSpawn > 0) {
zombieSpawnTimer++;
if (zombieSpawnTimer >= zombieSpawnInterval) {
zombieSpawnTimer = 0;
spawnZombie();
zombiesToSpawn--;
zombiesSpawned++;
}
} else if (zombies.length === 0) {
// Next wave
if (wave >= 10) {
// End game at wave 10
LK.effects.flashScreen(0x00ff00, 1000);
LK.showYouWin();
gameOver = true;
return;
}
wave++;
startWave();
waveTxt.setText('Wave ' + wave + (wave >= 4 ? ' - Mega Zombies!' : ''));
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.x += b.dirX * b.speed;
b.y += b.dirY * b.speed;
// Remove if off screen
if (b.x < -100 || b.x > GAME_W + 100 || b.y < -100 || b.y > GAME_H + 100) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with zombies
var bulletDestroyed = false;
for (var j = zombies.length - 1; j >= 0; j--) {
var z = zombies[j];
var dx = b.x - z.x;
var dy = b.y - z.y;
var dist2 = dx * dx + dy * dy;
var r2 = (b.radius + z.radius) * (b.radius + z.radius);
if (dist2 < r2) {
// Hit!
LK.getSound('zombiehit').play();
score++;
scoreTxt.setText(score);
if (score > highScore) highScore = score;
// Earn gold for killing zombie
gold += 5;
goldTxt.setText('Gold: ' + gold);
// Remove zombie and bullet
z.destroy();
zombies.splice(j, 1);
b.destroy();
bullets.splice(i, 1);
bulletDestroyed = true;
break;
}
}
if (bulletDestroyed) continue;
// Check collision with mega zombies
for (var j = megaZombies.length - 1; j >= 0; j--) {
var mz = megaZombies[j];
var dx = b.x - mz.x;
var dy = b.y - mz.y;
var dist2 = dx * dx + dy * dy;
var r2 = (b.radius + mz.radius) * (b.radius + mz.radius);
if (dist2 < r2) {
// Hit mega zombie!
LK.getSound('zombiehit').play();
mz.hp--;
// Flash mega zombie on hit
LK.effects.flashObject(mz, 0xffff00, 200);
if (mz.hp <= 0) {
// Kill mega zombie
score += 5; // Worth more points
scoreTxt.setText(score);
if (score > highScore) highScore = score;
gold += 25; // More gold
goldTxt.setText('Gold: ' + gold);
mz.destroy();
megaZombies.splice(j, 1);
}
// Destroy bullet
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var z = zombies[i];
// Move towards base
var dx = base.x - z.x;
var dy = base.y - z.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
z.x += dx / len * z.speed;
z.y += dy / len * z.speed;
}
// Check collision with base
var dbx = z.x - base.x;
var dby = z.y - base.y;
var baseR = base.width / 2 + z.radius - 10;
if (dbx * dbx + dby * dby < baseR * baseR) {
// Game over
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
return;
}
// Check collision with hero (for fun, just flash)
var dhx = z.x - hero.x;
var dhy = z.y - hero.y;
var heroR = hero.radius + z.radius;
if (dhx * dhx + dhy * dhy < heroR * heroR) {
LK.effects.flashObject(hero, 0xff0000, 300);
}
}
// Update mega zombies
for (var i = megaZombies.length - 1; i >= 0; i--) {
var mz = megaZombies[i];
// Move towards base
var dx = base.x - mz.x;
var dy = base.y - mz.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
mz.x += dx / len * mz.speed;
mz.y += dy / len * mz.speed;
}
// Check collision with base
var dbx = mz.x - base.x;
var dby = mz.y - base.y;
var baseR = base.width / 2 + mz.radius - 10;
if (dbx * dbx + dby * dby < baseR * baseR) {
// Game over
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
return;
}
// Check collision with hero (for fun, just flash)
var dhx = mz.x - hero.x;
var dhy = mz.y - hero.y;
var heroR = hero.radius + mz.radius;
if (dhx * dhx + dhy * dhy < heroR * heroR) {
LK.effects.flashObject(hero, 0xff0000, 300);
}
}
};
// Start first wave
startWave();
zombie. In-Game asset. 2d. High contrast. No shadows
straight line. In-Game asset. 2d. High contrast. No shadows
Create a game character with a crossbow in hand. In-Game asset. 2d. High contrast. No shadows
purple potion bottle filled with purple liquid. In-Game asset. 2d. High contrast. No shadows
mermi. In-Game asset. 2d. High contrast. No shadows