/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bullet class var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGfx = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bulletGfx.width / 2; self.speed = 32; // Fast bullet self.vx = 0; self.vy = 0; // Set direction self.setDirection = function (dx, dy) { var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { self.vx = dx / len * self.speed; self.vy = dy / len * self.speed; } }; self.update = function () { // Move bullet self.x += self.vx; self.y += self.vy; }; return self; }); // Hero class var Hero = Container.expand(function () { var self = Container.call(this); var heroGfx = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); // Add a gun to hero's hand // Use the new blue gun asset, positioned to the right hand var gunGfx = self.attachAsset('bluegun', { anchorX: 0.2, anchorY: 0.5, scaleX: 1.2, scaleY: 0.6, x: heroGfx.width * 0.38, // offset to right hand y: 0 }); // Track gun rotation self.gunGfx = gunGfx; self.gunAngle = 0; // For future upgrades: health, weapon, etc. self.radius = heroGfx.width / 2; return self; }); // Zombie class var Zombie = Container.expand(function () { var self = Container.call(this); var zombieGfx = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5 }); self.radius = zombieGfx.width / 2; // Speed will be set on spawn // Make zombies move at half the speed of the hero var heroBaseSpeed = 6; // Set hero base speed (if not defined elsewhere) self.speed = heroBaseSpeed * 0.5 + Math.random() * (heroBaseSpeed * 0.1); // 50% slower, small random // Direction vector self.vx = 0; self.vy = 0; // Health for zombie: 1 heart self.maxHealth = 1; self.health = self.maxHealth; // Heart icon(s) above zombie self.heartIcons = []; for (var i = 0; i < self.maxHealth; i++) { var heartIcon = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, x: (i - (self.maxHealth - 1) / 2) * 60, // center hearts y: -self.radius - 40, scaleX: 0.7, scaleY: 0.7 }); self.addChild(heartIcon); self.heartIcons.push(heartIcon); } // Set direction towards hero self.setDirection = function (targetX, targetY) { var dx = targetX - self.x; var dy = targetY - self.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { self.vx = dx / len * self.speed; self.vy = dy / len * self.speed; } }; // Move towards direction self.update = function () { self.x += self.vx; self.y += self.vy; // Keep health text above zombie if (self.healthTxt) { self.healthTxt.x = 0; self.healthTxt.y = -self.radius - 10; } }; // Method to take damage self.takeDamage = function (amount) { self.health -= amount; if (self.health < 0) self.health = 0; // Update heart icons for (var i = 0; i < self.heartIcons.length; i++) { self.heartIcons[i].visible = i < self.health; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Sound for shooting // Bullet asset: yellow box // Zombie asset: green ellipse // Hero asset: red box // Game area // New blue gun asset var GAME_W = 2048; var GAME_H = 2732; // Hero base speed for zombie speed calculation var heroBaseSpeed = 6; // Hero setup var hero = new Hero(); hero.x = GAME_W / 2; hero.y = GAME_H / 2; game.addChild(hero); // Bullets and zombies arrays var bullets = []; var zombies = []; // --- Şarjör/Magazine System --- var MAG_SIZE = 7; // şarjör kapasitesi var TOTAL_BULLETS = 20; // toplam yedek mermi var magazine = MAG_SIZE; // şarjördeki mermi var reserveBullets = TOTAL_BULLETS - MAG_SIZE; // yedek mermi var isReloading = false; var reloadTime = 3000; // ms var reloadTimeout = null; // --- Bullet Type System (auto change every 2 seconds) --- var bulletTypes = [{ id: 'bullet', speed: 32 }, // default { id: 'bullet', speed: 48 }, // fast { id: 'bullet', speed: 16 } // slow ]; var currentBulletTypeIndex = 0; var bulletTypeChangeInterval = null; // Start auto bullet type change function startBulletTypeAutoChange() { if (bulletTypeChangeInterval) LK.clearInterval(bulletTypeChangeInterval); bulletTypeChangeInterval = LK.setInterval(function () { currentBulletTypeIndex = (currentBulletTypeIndex + 1) % bulletTypes.length; }, 2000); } startBulletTypeAutoChange(); // Mermi UI - Text-based bullet count at the bottom left var bulletTxt = new Text2(magazine + " / " + reserveBullets, { size: 90, fill: "#fff" }); // Anchor to left-bottom bulletTxt.anchor.set(0, 1); bulletTxt.x = 60; bulletTxt.y = 2732 - 40; LK.gui.bottomLeft.addChild(bulletTxt); function updateBulletIcons() { bulletTxt.setText(magazine + " / " + reserveBullets); if (typeof outOfAmmoTxt !== "undefined" && outOfAmmoTxt && outOfAmmoTxt.visible) { outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets); } } updateBulletIcons(); // "Mermi bitti" UI var outOfAmmoTxt = new Text2("Mermi bitti\n" + magazine + " / " + reserveBullets, { size: 260, fill: "#fff", align: "center" }); // Bottom center: anchor to center-bottom, x=center, y=height outOfAmmoTxt.anchor.set(0.5, 1); outOfAmmoTxt.x = 2048 / 2; outOfAmmoTxt.y = 2732 - 40; outOfAmmoTxt.visible = false; LK.gui.bottom.addChild(outOfAmmoTxt); // Score var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Hearts (health) var maxHearts = 5; var hearts = []; for (var i = 0; i < maxHearts; i++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, x: 160 + i * 120, y: 80, scaleX: 1.1, scaleY: 1.1 }); LK.gui.top.addChild(heart); hearts.push(heart); } var currentHearts = maxHearts; // Dragging var dragNode = null; // Touch/move controls function handleMove(x, y, obj) { // Drag hero if (dragNode === hero) { // Clamp hero inside game area, avoid top left 100x100 var minX = hero.radius + 100; var maxX = GAME_W - hero.radius; var minY = hero.radius; var maxY = GAME_H - hero.radius; hero.x = Math.max(minX, Math.min(maxX, x)); hero.y = Math.max(minY, Math.min(maxY, y)); } } game.move = handleMove; game.down = function (x, y, obj) { // Only drag if touch is on hero var dx = x - hero.x; var dy = y - hero.y; if (dx * dx + dy * dy <= hero.radius * hero.radius) { dragNode = hero; handleMove(x, y, obj); } }; game.up = function (x, y, obj) { dragNode = null; }; // Shooting: tap anywhere not on hero to shoot game.tap = function (x, y, obj) { // Don't shoot if tap is on hero var dx = x - hero.x; var dy = y - hero.y; if (dx * dx + dy * dy <= hero.radius * hero.radius) return; // Create bullet var bullet = new Bullet(); bullet.x = hero.x; bullet.y = hero.y; // Set bullet speed/type var bulletType = bulletTypes[currentBulletTypeIndex]; bullet.speed = bulletType.speed; bullet.setDirection(dx, dy); bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); // Rotate gun towards shot direction if (typeof hero.gunGfx !== "undefined") { hero.gunGfx.rotation = Math.atan2(dy, dx); } }; game.down = function (x, y, obj) { // Drag or shoot var dx = x - hero.x; var dy = y - hero.y; if (dx * dx + dy * dy <= hero.radius * hero.radius) { dragNode = hero; handleMove(x, y, obj); } else { // --- Şarjör/Magazine Logic --- if (isReloading) { // Reloading, can't shoot return; } if (magazine > 0) { // Shoot var bullet = new Bullet(); bullet.x = hero.x; bullet.y = hero.y; // Set bullet speed/type var bulletType = bulletTypes[currentBulletTypeIndex]; bullet.speed = bulletType.speed; bullet.setDirection(dx, dy); bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); // Rotate gun towards shot direction if (typeof hero.gunGfx !== "undefined") { hero.gunGfx.rotation = Math.atan2(dy, dx); } magazine--; updateBulletIcons(); outOfAmmoTxt.visible = false; // If şarjör bitti, start reload if possible if (magazine === 0) { if (reserveBullets > 0) { isReloading = true; LK.getSound('reload').play(); // Stop reload sound after 1 second to ensure it only plays for 1s LK.setTimeout(function () { if (LK.getSound('reload').stop) LK.getSound('reload').stop(); }, 1000); outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets); outOfAmmoTxt.visible = true; reloadTimeout = LK.setTimeout(function () { // Calculate how many bullets to reload var reloadAmount = Math.min(MAG_SIZE, reserveBullets); magazine = reloadAmount; reserveBullets -= reloadAmount; updateBulletIcons(); isReloading = false; outOfAmmoTxt.visible = false; }, reloadTime); } else { // No reserve, show out of ammo outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets); outOfAmmoTxt.visible = true; } } } else { // No bullets in magazine if (!isReloading) { // If reserve bullets available, start reload if (reserveBullets > 0) { isReloading = true; LK.getSound('reload').play(); // Stop reload sound after 1 second to ensure it only plays for 1s LK.setTimeout(function () { if (LK.getSound('reload').stop) LK.getSound('reload').stop(); }, 1000); outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets); outOfAmmoTxt.visible = true; reloadTimeout = LK.setTimeout(function () { var reloadAmount = Math.min(MAG_SIZE, reserveBullets); magazine = reloadAmount; reserveBullets -= reloadAmount; updateBulletIcons(); isReloading = false; outOfAmmoTxt.visible = false; }, reloadTime); } else { // No reserve, show out of ammo outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets); outOfAmmoTxt.visible = true; } } } } }; // Zombie spawn timer var zombieSpawnInterval = 90; // frames var zombieSpawnTimer = 0; // Helper: spawn zombie at random edge function spawnZombie() { var zombie = new Zombie(); // Randomize health: 15% get 3, 30% get 2, rest get 1 var r = Math.random(); if (r < 0.15) { zombie.maxHealth = 3; } else if (r < 0.45) { zombie.maxHealth = 2; } else { zombie.maxHealth = 1; } zombie.health = zombie.maxHealth; // Remove old heart icons if any if (zombie.heartIcons && zombie.heartIcons.length > 0) { for (var h = 0; h < zombie.heartIcons.length; h++) { zombie.heartIcons[h].destroy && zombie.heartIcons[h].destroy(); } zombie.heartIcons = []; } // Add heart icons for new health for (var i = 0; i < zombie.maxHealth; i++) { var heartIcon = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, x: (i - (zombie.maxHealth - 1) / 2) * 60, y: -zombie.radius - 40, scaleX: 0.7, scaleY: 0.7 }); zombie.addChild(heartIcon); zombie.heartIcons.push(heartIcon); } // Random edge: 0=top,1=bottom,2=left,3=right var edge = Math.floor(Math.random() * 4); var margin = 80; if (edge === 0) { // top zombie.x = margin + Math.random() * (GAME_W - 2 * margin); zombie.y = -zombie.radius; } else if (edge === 1) { // bottom zombie.x = margin + Math.random() * (GAME_W - 2 * margin); zombie.y = GAME_H + zombie.radius; } else if (edge === 2) { // left zombie.x = -zombie.radius; zombie.y = margin + Math.random() * (GAME_H - 2 * margin); } else { // right zombie.x = GAME_W + zombie.radius; zombie.y = margin + Math.random() * (GAME_H - 2 * margin); } zombie.setDirection(hero.x, hero.y); zombies.push(zombie); game.addChild(zombie); } // Main update loop game.update = function () { // Update zombies for (var i = zombies.length - 1; i >= 0; i--) { var z = zombies[i]; // Always update direction towards hero z.setDirection(hero.x, hero.y); z.update(); // Check collision with hero var dx = z.x - hero.x; var dy = z.y - hero.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < z.radius + hero.radius - 10) { // Lose a heart if (currentHearts > 0) { currentHearts--; // Hide a heart visually if (hearts[currentHearts]) { hearts[currentHearts].visible = false; } LK.effects.flashObject(hero, 0xff0000, 400); // Move zombie that hit away from hero instead of destroying // Calculate vector from hero to zombie var awayDx = z.x - hero.x; var awayDy = z.y - hero.y; var awayLen = Math.sqrt(awayDx * awayDx + awayDy * awayDy); // Move zombie 400px away from hero, but clamp inside game area if (awayLen > 0) { var moveDist = 400; var newX = hero.x + awayDx / awayLen * moveDist; var newY = hero.y + awayDy / awayLen * moveDist; // Clamp to game area (with margin for zombie size) var margin = z.radius + 20; newX = Math.max(margin, Math.min(GAME_W - margin, newX)); newY = Math.max(margin, Math.min(GAME_H - margin, newY)); z.x = newX; z.y = newY; } // If no hearts left, game over if (currentHearts === 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } } } // Update bullets and check collision with zombies for (var j = bullets.length - 1; j >= 0; j--) { var b = bullets[j]; b.update && b.update(); // Remove bullet if out of bounds if (b.x < -b.radius || b.x > GAME_W + b.radius || b.y < -b.radius || b.y > GAME_H + b.radius) { b.destroy(); bullets.splice(j, 1); continue; } // Check collision with zombies for (var k = zombies.length - 1; k >= 0; k--) { var z2 = zombies[k]; var dx2 = b.x - z2.x; var dy2 = b.y - z2.y; var dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2); if (dist2 < b.radius + z2.radius - 10) { // Zombie takes damage if (typeof z2.takeDamage === "function") { z2.takeDamage(1); // If health reaches zero, destroy if (z2.health <= 0) { // 5% chance to drop a heart if (Math.random() < 0.05) { var heartDrop = new Container(); var heartGfx = heartDrop.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); heartDrop.x = z2.x; heartDrop.y = z2.y; heartDrop.radius = heartGfx.width * 0.6; heartDrop.update = function () { // Animate heart (optional: float up and down) if (typeof this.floatDir === "undefined") this.floatDir = 1; if (typeof this.floatOffset === "undefined") this.floatOffset = 0; this.floatOffset += this.floatDir * 1.2; if (this.floatOffset > 18) this.floatDir = -1; if (this.floatOffset < -18) this.floatDir = 1; this.y += this.floatDir * 0.7; // Check collision with hero var dxh = this.x - hero.x; var dyh = this.y - hero.y; var distH = Math.sqrt(dxh * dxh + dyh * dyh); if (distH < hero.radius + this.radius - 10) { // Collect heart: refill all hearts for (var h = 0; h < hearts.length; h++) { hearts[h].visible = true; } currentHearts = maxHearts; this.destroy(); // Remove from heartDrops array for (var hd = 0; hd < heartDrops.length; hd++) { if (heartDrops[hd] === this) { heartDrops.splice(hd, 1); break; } } } }; if (typeof heartDrops === "undefined") heartDrops = []; heartDrops.push(heartDrop); game.addChild(heartDrop); } // 10% chance to drop a bullet pack if (Math.random() < 0.10) { // Determine bullet amount: random between 5 and 50, but not less than 5, not more than 50 var minBullets = 5; var maxBullets = 50; // The initial TOTAL_BULLETS is 20, but drop can be more or less var bulletAmount = Math.floor(Math.random() * (maxBullets - minBullets + 1)) + minBullets; // Create bullet drop container var bulletDrop = new Container(); var bulletGfx = bulletDrop.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5 + (bulletAmount - 5) / 45, // scale from 1.5 (5 bullets) up to ~2.5 (50 bullets) scaleY: 1.5 + (bulletAmount - 5) / 45 }); bulletDrop.x = z2.x; bulletDrop.y = z2.y; bulletDrop.radius = bulletGfx.width * 0.7 * bulletGfx.scaleX; // slightly larger for easier pickup // Show bullet amount above the bullet var bulletAmtTxt = new Text2(bulletAmount + "", { size: 70, fill: 0xFFE600, align: "center" }); bulletAmtTxt.anchor.set(0.5, 1); bulletAmtTxt.x = 0; bulletAmtTxt.y = -bulletGfx.height * bulletGfx.scaleY / 2 - 10; bulletDrop.addChild(bulletAmtTxt); bulletDrop.update = function () { // Animate bullet (optional: float up and down) if (typeof this.floatDir === "undefined") this.floatDir = 1; if (typeof this.floatOffset === "undefined") this.floatOffset = 0; this.floatOffset += this.floatDir * 1.2; if (this.floatOffset > 18) this.floatDir = -1; if (this.floatOffset < -18) this.floatDir = 1; this.y += this.floatDir * 0.7; // Check collision with hero var dxh = this.x - hero.x; var dyh = this.y - hero.y; var distH = Math.sqrt(dxh * dxh + dyh * dyh); if (distH < hero.radius + this.radius - 10) { // Collect bullet: add to reserveBullets, but never above 999 reserveBullets += bulletAmount; if (reserveBullets > 999) reserveBullets = 999; updateBulletIcons(); this.destroy(); // Remove from bulletDrops array for (var bd = 0; bd < bulletDrops.length; bd++) { if (bulletDrops[bd] === this) { bulletDrops.splice(bd, 1); break; } } } }; if (typeof bulletDrops === "undefined") bulletDrops = []; bulletDrops.push(bulletDrop); game.addChild(bulletDrop); } z2.destroy(); zombies.splice(k, 1); // Score up score += 1; scoreTxt.setText(score); } } else { // fallback: destroy if no health system z2.destroy(); zombies.splice(k, 1); score += 1; scoreTxt.setText(score); } b.destroy(); bullets.splice(j, 1); break; } } } // Animate and check collision for heart drops if (typeof heartDrops !== "undefined" && heartDrops.length > 0) { for (var hd = heartDrops.length - 1; hd >= 0; hd--) { var hdrop = heartDrops[hd]; if (hdrop && typeof hdrop.update === "function") hdrop.update(); } } // Animate and check collision for bullet drops if (typeof bulletDrops !== "undefined" && bulletDrops.length > 0) { for (var bd = bulletDrops.length - 1; bd >= 0; bd--) { var bdrop = bulletDrops[bd]; if (bdrop && typeof bdrop.update === "function") bdrop.update(); } } // Spawn zombies zombieSpawnTimer++; if (zombieSpawnTimer >= zombieSpawnInterval) { // Spawn more zombies as time goes on // Calculate how many zombies to spawn: start at 1, increase by 1 every 10 kills, max 6 var zombiesToSpawn = 1 + Math.floor(score / 10); if (zombiesToSpawn > 6) zombiesToSpawn = 6; for (var s = 0; s < zombiesToSpawn; s++) { spawnZombie(); } zombieSpawnTimer = 0; // Gradually increase spawn rate (lower interval) if (zombieSpawnInterval > 30) zombieSpawnInterval -= 1; } // No random bullet spawn at corners }; // Center hero on start hero.x = GAME_W / 2; hero.y = GAME_H / 2; // Reset şarjör and reserve on game start magazine = MAG_SIZE; reserveBullets = TOTAL_BULLETS - MAG_SIZE; isReloading = false; if (reloadTimeout) { LK.clearTimeout(reloadTimeout); reloadTimeout = null; } updateBulletIcons(); outOfAmmoTxt.visible = false; // Initial zombie spawn for (var i = 0; i < 3; i++) { spawnZombie(); }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGfx = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletGfx.width / 2;
self.speed = 32; // Fast bullet
self.vx = 0;
self.vy = 0;
// Set direction
self.setDirection = function (dx, dy) {
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.vx = dx / len * self.speed;
self.vy = dy / len * self.speed;
}
};
self.update = function () {
// Move bullet
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroGfx = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
// Add a gun to hero's hand
// Use the new blue gun asset, positioned to the right hand
var gunGfx = self.attachAsset('bluegun', {
anchorX: 0.2,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 0.6,
x: heroGfx.width * 0.38,
// offset to right hand
y: 0
});
// Track gun rotation
self.gunGfx = gunGfx;
self.gunAngle = 0;
// For future upgrades: health, weapon, etc.
self.radius = heroGfx.width / 2;
return self;
});
// Zombie class
var Zombie = Container.expand(function () {
var self = Container.call(this);
var zombieGfx = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = zombieGfx.width / 2;
// Speed will be set on spawn
// Make zombies move at half the speed of the hero
var heroBaseSpeed = 6; // Set hero base speed (if not defined elsewhere)
self.speed = heroBaseSpeed * 0.5 + Math.random() * (heroBaseSpeed * 0.1); // 50% slower, small random
// Direction vector
self.vx = 0;
self.vy = 0;
// Health for zombie: 1 heart
self.maxHealth = 1;
self.health = self.maxHealth;
// Heart icon(s) above zombie
self.heartIcons = [];
for (var i = 0; i < self.maxHealth; i++) {
var heartIcon = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
x: (i - (self.maxHealth - 1) / 2) * 60,
// center hearts
y: -self.radius - 40,
scaleX: 0.7,
scaleY: 0.7
});
self.addChild(heartIcon);
self.heartIcons.push(heartIcon);
}
// Set direction towards hero
self.setDirection = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
self.vx = dx / len * self.speed;
self.vy = dy / len * self.speed;
}
};
// Move towards direction
self.update = function () {
self.x += self.vx;
self.y += self.vy;
// Keep health text above zombie
if (self.healthTxt) {
self.healthTxt.x = 0;
self.healthTxt.y = -self.radius - 10;
}
};
// Method to take damage
self.takeDamage = function (amount) {
self.health -= amount;
if (self.health < 0) self.health = 0;
// Update heart icons
for (var i = 0; i < self.heartIcons.length; i++) {
self.heartIcons[i].visible = i < self.health;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Sound for shooting
// Bullet asset: yellow box
// Zombie asset: green ellipse
// Hero asset: red box
// Game area
// New blue gun asset
var GAME_W = 2048;
var GAME_H = 2732;
// Hero base speed for zombie speed calculation
var heroBaseSpeed = 6;
// Hero setup
var hero = new Hero();
hero.x = GAME_W / 2;
hero.y = GAME_H / 2;
game.addChild(hero);
// Bullets and zombies arrays
var bullets = [];
var zombies = [];
// --- Şarjör/Magazine System ---
var MAG_SIZE = 7; // şarjör kapasitesi
var TOTAL_BULLETS = 20; // toplam yedek mermi
var magazine = MAG_SIZE; // şarjördeki mermi
var reserveBullets = TOTAL_BULLETS - MAG_SIZE; // yedek mermi
var isReloading = false;
var reloadTime = 3000; // ms
var reloadTimeout = null;
// --- Bullet Type System (auto change every 2 seconds) ---
var bulletTypes = [{
id: 'bullet',
speed: 32
},
// default
{
id: 'bullet',
speed: 48
},
// fast
{
id: 'bullet',
speed: 16
} // slow
];
var currentBulletTypeIndex = 0;
var bulletTypeChangeInterval = null;
// Start auto bullet type change
function startBulletTypeAutoChange() {
if (bulletTypeChangeInterval) LK.clearInterval(bulletTypeChangeInterval);
bulletTypeChangeInterval = LK.setInterval(function () {
currentBulletTypeIndex = (currentBulletTypeIndex + 1) % bulletTypes.length;
}, 2000);
}
startBulletTypeAutoChange();
// Mermi UI - Text-based bullet count at the bottom left
var bulletTxt = new Text2(magazine + " / " + reserveBullets, {
size: 90,
fill: "#fff"
});
// Anchor to left-bottom
bulletTxt.anchor.set(0, 1);
bulletTxt.x = 60;
bulletTxt.y = 2732 - 40;
LK.gui.bottomLeft.addChild(bulletTxt);
function updateBulletIcons() {
bulletTxt.setText(magazine + " / " + reserveBullets);
if (typeof outOfAmmoTxt !== "undefined" && outOfAmmoTxt && outOfAmmoTxt.visible) {
outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets);
}
}
updateBulletIcons();
// "Mermi bitti" UI
var outOfAmmoTxt = new Text2("Mermi bitti\n" + magazine + " / " + reserveBullets, {
size: 260,
fill: "#fff",
align: "center"
});
// Bottom center: anchor to center-bottom, x=center, y=height
outOfAmmoTxt.anchor.set(0.5, 1);
outOfAmmoTxt.x = 2048 / 2;
outOfAmmoTxt.y = 2732 - 40;
outOfAmmoTxt.visible = false;
LK.gui.bottom.addChild(outOfAmmoTxt);
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Hearts (health)
var maxHearts = 5;
var hearts = [];
for (var i = 0; i < maxHearts; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
x: 160 + i * 120,
y: 80,
scaleX: 1.1,
scaleY: 1.1
});
LK.gui.top.addChild(heart);
hearts.push(heart);
}
var currentHearts = maxHearts;
// Dragging
var dragNode = null;
// Touch/move controls
function handleMove(x, y, obj) {
// Drag hero
if (dragNode === hero) {
// Clamp hero inside game area, avoid top left 100x100
var minX = hero.radius + 100;
var maxX = GAME_W - hero.radius;
var minY = hero.radius;
var maxY = GAME_H - hero.radius;
hero.x = Math.max(minX, Math.min(maxX, x));
hero.y = Math.max(minY, Math.min(maxY, y));
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only drag if touch is on hero
var dx = x - hero.x;
var dy = y - hero.y;
if (dx * dx + dy * dy <= hero.radius * hero.radius) {
dragNode = hero;
handleMove(x, y, obj);
}
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Shooting: tap anywhere not on hero to shoot
game.tap = function (x, y, obj) {
// Don't shoot if tap is on hero
var dx = x - hero.x;
var dy = y - hero.y;
if (dx * dx + dy * dy <= hero.radius * hero.radius) return;
// Create bullet
var bullet = new Bullet();
bullet.x = hero.x;
bullet.y = hero.y;
// Set bullet speed/type
var bulletType = bulletTypes[currentBulletTypeIndex];
bullet.speed = bulletType.speed;
bullet.setDirection(dx, dy);
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Rotate gun towards shot direction
if (typeof hero.gunGfx !== "undefined") {
hero.gunGfx.rotation = Math.atan2(dy, dx);
}
};
game.down = function (x, y, obj) {
// Drag or shoot
var dx = x - hero.x;
var dy = y - hero.y;
if (dx * dx + dy * dy <= hero.radius * hero.radius) {
dragNode = hero;
handleMove(x, y, obj);
} else {
// --- Şarjör/Magazine Logic ---
if (isReloading) {
// Reloading, can't shoot
return;
}
if (magazine > 0) {
// Shoot
var bullet = new Bullet();
bullet.x = hero.x;
bullet.y = hero.y;
// Set bullet speed/type
var bulletType = bulletTypes[currentBulletTypeIndex];
bullet.speed = bulletType.speed;
bullet.setDirection(dx, dy);
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
// Rotate gun towards shot direction
if (typeof hero.gunGfx !== "undefined") {
hero.gunGfx.rotation = Math.atan2(dy, dx);
}
magazine--;
updateBulletIcons();
outOfAmmoTxt.visible = false;
// If şarjör bitti, start reload if possible
if (magazine === 0) {
if (reserveBullets > 0) {
isReloading = true;
LK.getSound('reload').play();
// Stop reload sound after 1 second to ensure it only plays for 1s
LK.setTimeout(function () {
if (LK.getSound('reload').stop) LK.getSound('reload').stop();
}, 1000);
outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets);
outOfAmmoTxt.visible = true;
reloadTimeout = LK.setTimeout(function () {
// Calculate how many bullets to reload
var reloadAmount = Math.min(MAG_SIZE, reserveBullets);
magazine = reloadAmount;
reserveBullets -= reloadAmount;
updateBulletIcons();
isReloading = false;
outOfAmmoTxt.visible = false;
}, reloadTime);
} else {
// No reserve, show out of ammo
outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets);
outOfAmmoTxt.visible = true;
}
}
} else {
// No bullets in magazine
if (!isReloading) {
// If reserve bullets available, start reload
if (reserveBullets > 0) {
isReloading = true;
LK.getSound('reload').play();
// Stop reload sound after 1 second to ensure it only plays for 1s
LK.setTimeout(function () {
if (LK.getSound('reload').stop) LK.getSound('reload').stop();
}, 1000);
outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets);
outOfAmmoTxt.visible = true;
reloadTimeout = LK.setTimeout(function () {
var reloadAmount = Math.min(MAG_SIZE, reserveBullets);
magazine = reloadAmount;
reserveBullets -= reloadAmount;
updateBulletIcons();
isReloading = false;
outOfAmmoTxt.visible = false;
}, reloadTime);
} else {
// No reserve, show out of ammo
outOfAmmoTxt.setText("Mermi bitti\n" + magazine + " / " + reserveBullets);
outOfAmmoTxt.visible = true;
}
}
}
}
};
// Zombie spawn timer
var zombieSpawnInterval = 90; // frames
var zombieSpawnTimer = 0;
// Helper: spawn zombie at random edge
function spawnZombie() {
var zombie = new Zombie();
// Randomize health: 15% get 3, 30% get 2, rest get 1
var r = Math.random();
if (r < 0.15) {
zombie.maxHealth = 3;
} else if (r < 0.45) {
zombie.maxHealth = 2;
} else {
zombie.maxHealth = 1;
}
zombie.health = zombie.maxHealth;
// Remove old heart icons if any
if (zombie.heartIcons && zombie.heartIcons.length > 0) {
for (var h = 0; h < zombie.heartIcons.length; h++) {
zombie.heartIcons[h].destroy && zombie.heartIcons[h].destroy();
}
zombie.heartIcons = [];
}
// Add heart icons for new health
for (var i = 0; i < zombie.maxHealth; i++) {
var heartIcon = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
x: (i - (zombie.maxHealth - 1) / 2) * 60,
y: -zombie.radius - 40,
scaleX: 0.7,
scaleY: 0.7
});
zombie.addChild(heartIcon);
zombie.heartIcons.push(heartIcon);
}
// Random edge: 0=top,1=bottom,2=left,3=right
var edge = Math.floor(Math.random() * 4);
var margin = 80;
if (edge === 0) {
// top
zombie.x = margin + Math.random() * (GAME_W - 2 * margin);
zombie.y = -zombie.radius;
} else if (edge === 1) {
// bottom
zombie.x = margin + Math.random() * (GAME_W - 2 * margin);
zombie.y = GAME_H + zombie.radius;
} else if (edge === 2) {
// left
zombie.x = -zombie.radius;
zombie.y = margin + Math.random() * (GAME_H - 2 * margin);
} else {
// right
zombie.x = GAME_W + zombie.radius;
zombie.y = margin + Math.random() * (GAME_H - 2 * margin);
}
zombie.setDirection(hero.x, hero.y);
zombies.push(zombie);
game.addChild(zombie);
}
// Main update loop
game.update = function () {
// Update zombies
for (var i = zombies.length - 1; i >= 0; i--) {
var z = zombies[i];
// Always update direction towards hero
z.setDirection(hero.x, hero.y);
z.update();
// Check collision with hero
var dx = z.x - hero.x;
var dy = z.y - hero.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < z.radius + hero.radius - 10) {
// Lose a heart
if (currentHearts > 0) {
currentHearts--;
// Hide a heart visually
if (hearts[currentHearts]) {
hearts[currentHearts].visible = false;
}
LK.effects.flashObject(hero, 0xff0000, 400);
// Move zombie that hit away from hero instead of destroying
// Calculate vector from hero to zombie
var awayDx = z.x - hero.x;
var awayDy = z.y - hero.y;
var awayLen = Math.sqrt(awayDx * awayDx + awayDy * awayDy);
// Move zombie 400px away from hero, but clamp inside game area
if (awayLen > 0) {
var moveDist = 400;
var newX = hero.x + awayDx / awayLen * moveDist;
var newY = hero.y + awayDy / awayLen * moveDist;
// Clamp to game area (with margin for zombie size)
var margin = z.radius + 20;
newX = Math.max(margin, Math.min(GAME_W - margin, newX));
newY = Math.max(margin, Math.min(GAME_H - margin, newY));
z.x = newX;
z.y = newY;
}
// If no hearts left, game over
if (currentHearts === 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
}
}
// Update bullets and check collision with zombies
for (var j = bullets.length - 1; j >= 0; j--) {
var b = bullets[j];
b.update && b.update();
// Remove bullet if out of bounds
if (b.x < -b.radius || b.x > GAME_W + b.radius || b.y < -b.radius || b.y > GAME_H + b.radius) {
b.destroy();
bullets.splice(j, 1);
continue;
}
// Check collision with zombies
for (var k = zombies.length - 1; k >= 0; k--) {
var z2 = zombies[k];
var dx2 = b.x - z2.x;
var dy2 = b.y - z2.y;
var dist2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
if (dist2 < b.radius + z2.radius - 10) {
// Zombie takes damage
if (typeof z2.takeDamage === "function") {
z2.takeDamage(1);
// If health reaches zero, destroy
if (z2.health <= 0) {
// 5% chance to drop a heart
if (Math.random() < 0.05) {
var heartDrop = new Container();
var heartGfx = heartDrop.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
heartDrop.x = z2.x;
heartDrop.y = z2.y;
heartDrop.radius = heartGfx.width * 0.6;
heartDrop.update = function () {
// Animate heart (optional: float up and down)
if (typeof this.floatDir === "undefined") this.floatDir = 1;
if (typeof this.floatOffset === "undefined") this.floatOffset = 0;
this.floatOffset += this.floatDir * 1.2;
if (this.floatOffset > 18) this.floatDir = -1;
if (this.floatOffset < -18) this.floatDir = 1;
this.y += this.floatDir * 0.7;
// Check collision with hero
var dxh = this.x - hero.x;
var dyh = this.y - hero.y;
var distH = Math.sqrt(dxh * dxh + dyh * dyh);
if (distH < hero.radius + this.radius - 10) {
// Collect heart: refill all hearts
for (var h = 0; h < hearts.length; h++) {
hearts[h].visible = true;
}
currentHearts = maxHearts;
this.destroy();
// Remove from heartDrops array
for (var hd = 0; hd < heartDrops.length; hd++) {
if (heartDrops[hd] === this) {
heartDrops.splice(hd, 1);
break;
}
}
}
};
if (typeof heartDrops === "undefined") heartDrops = [];
heartDrops.push(heartDrop);
game.addChild(heartDrop);
}
// 10% chance to drop a bullet pack
if (Math.random() < 0.10) {
// Determine bullet amount: random between 5 and 50, but not less than 5, not more than 50
var minBullets = 5;
var maxBullets = 50;
// The initial TOTAL_BULLETS is 20, but drop can be more or less
var bulletAmount = Math.floor(Math.random() * (maxBullets - minBullets + 1)) + minBullets;
// Create bullet drop container
var bulletDrop = new Container();
var bulletGfx = bulletDrop.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5 + (bulletAmount - 5) / 45,
// scale from 1.5 (5 bullets) up to ~2.5 (50 bullets)
scaleY: 1.5 + (bulletAmount - 5) / 45
});
bulletDrop.x = z2.x;
bulletDrop.y = z2.y;
bulletDrop.radius = bulletGfx.width * 0.7 * bulletGfx.scaleX; // slightly larger for easier pickup
// Show bullet amount above the bullet
var bulletAmtTxt = new Text2(bulletAmount + "", {
size: 70,
fill: 0xFFE600,
align: "center"
});
bulletAmtTxt.anchor.set(0.5, 1);
bulletAmtTxt.x = 0;
bulletAmtTxt.y = -bulletGfx.height * bulletGfx.scaleY / 2 - 10;
bulletDrop.addChild(bulletAmtTxt);
bulletDrop.update = function () {
// Animate bullet (optional: float up and down)
if (typeof this.floatDir === "undefined") this.floatDir = 1;
if (typeof this.floatOffset === "undefined") this.floatOffset = 0;
this.floatOffset += this.floatDir * 1.2;
if (this.floatOffset > 18) this.floatDir = -1;
if (this.floatOffset < -18) this.floatDir = 1;
this.y += this.floatDir * 0.7;
// Check collision with hero
var dxh = this.x - hero.x;
var dyh = this.y - hero.y;
var distH = Math.sqrt(dxh * dxh + dyh * dyh);
if (distH < hero.radius + this.radius - 10) {
// Collect bullet: add to reserveBullets, but never above 999
reserveBullets += bulletAmount;
if (reserveBullets > 999) reserveBullets = 999;
updateBulletIcons();
this.destroy();
// Remove from bulletDrops array
for (var bd = 0; bd < bulletDrops.length; bd++) {
if (bulletDrops[bd] === this) {
bulletDrops.splice(bd, 1);
break;
}
}
}
};
if (typeof bulletDrops === "undefined") bulletDrops = [];
bulletDrops.push(bulletDrop);
game.addChild(bulletDrop);
}
z2.destroy();
zombies.splice(k, 1);
// Score up
score += 1;
scoreTxt.setText(score);
}
} else {
// fallback: destroy if no health system
z2.destroy();
zombies.splice(k, 1);
score += 1;
scoreTxt.setText(score);
}
b.destroy();
bullets.splice(j, 1);
break;
}
}
}
// Animate and check collision for heart drops
if (typeof heartDrops !== "undefined" && heartDrops.length > 0) {
for (var hd = heartDrops.length - 1; hd >= 0; hd--) {
var hdrop = heartDrops[hd];
if (hdrop && typeof hdrop.update === "function") hdrop.update();
}
}
// Animate and check collision for bullet drops
if (typeof bulletDrops !== "undefined" && bulletDrops.length > 0) {
for (var bd = bulletDrops.length - 1; bd >= 0; bd--) {
var bdrop = bulletDrops[bd];
if (bdrop && typeof bdrop.update === "function") bdrop.update();
}
}
// Spawn zombies
zombieSpawnTimer++;
if (zombieSpawnTimer >= zombieSpawnInterval) {
// Spawn more zombies as time goes on
// Calculate how many zombies to spawn: start at 1, increase by 1 every 10 kills, max 6
var zombiesToSpawn = 1 + Math.floor(score / 10);
if (zombiesToSpawn > 6) zombiesToSpawn = 6;
for (var s = 0; s < zombiesToSpawn; s++) {
spawnZombie();
}
zombieSpawnTimer = 0;
// Gradually increase spawn rate (lower interval)
if (zombieSpawnInterval > 30) zombieSpawnInterval -= 1;
}
// No random bullet spawn at corners
};
// Center hero on start
hero.x = GAME_W / 2;
hero.y = GAME_H / 2;
// Reset şarjör and reserve on game start
magazine = MAG_SIZE;
reserveBullets = TOTAL_BULLETS - MAG_SIZE;
isReloading = false;
if (reloadTimeout) {
LK.clearTimeout(reloadTimeout);
reloadTimeout = null;
}
updateBulletIcons();
outOfAmmoTxt.visible = false;
// Initial zombie spawn
for (var i = 0; i < 3; i++) {
spawnZombie();
}
mermi. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
oyuncu,karekter. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
silah. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
zombi. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat