Code edit (1 edits merged)
Please save this source code
User prompt
Zombie Wave Defense: Base Builder
Initial prompt
bana kendi alanımızın oldugu ve o alana saldıran zombilerin oldugu bi oyun yap ama zombiler dalga dalga gelcek ve her oldurdugumuz zıombinin içnden paralar çıkacak o paralar ile de kendi alanımıza yapılar inşaa edip zombilerin gelmesini zorlaştırcagız ve yıne o paralar ile karakterimizin elindeki silahı güçlendireceğiz ve karakterimiz zombileri tek merimide oldurebilecek ancak zombiler karaterimizi tek vuruşta olduremeyecek e az 5 vuruşta öldürebilece
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // BossZombie class var BossZombie = Container.expand(function () { var self = Container.call(this); var bossSprite = self.attachAsset('zombie', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.7, scaleY: 1.7, color: 0x8e44ad // purple tint for boss }); self.radius = bossSprite.width / 2; var bossPowerLevel = Math.floor((currentWave - 1) / 3); self.speed = 1.5 + bossPowerLevel * 0.5; self.hp = 10 + bossPowerLevel * 5; self.maxHp = self.hp; self.target = null; self.lastHitTick = 0; self.update = function () { // Move towards player var tx = player.x; var ty = player.y; // If wall/trap/tower is closer, target that var minDist = Math.sqrt((self.x - tx) * (self.x - tx) + (self.y - ty) * (self.y - ty)); var targetObj = player; for (var i = 0; i < defenses.length; i++) { var d = defenses[i]; var dist = Math.sqrt((self.x - d.x) * (self.x - d.x) + (self.y - d.y) * (self.y - d.y)); if (dist < minDist) { minDist = dist; targetObj = d; } } self.target = targetObj; var dx = targetObj.x - self.x; var dy = targetObj.y - self.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { dx /= len; dy /= len; self.x += dx * self.speed; self.y += dy * self.speed; } // Attack if close if (len < self.radius + targetObj.radius + 10) { if (LK.ticks - self.lastHitTick > 30) { if (targetObj === player) { player.takeDamage(2); // Boss does more damage } else if (targetObj.takeDamage) { targetObj.takeDamage(2); } self.lastHitTick = LK.ticks; } } }; self.takeDamage = function (amount) { self.hp -= amount; if (self.hp <= 0) { self.die(); } else { LK.effects.flashObject(self, 0xff00ff, 300); } }; self.die = function () { LK.getSound('zombie_die').play(); // Drop 5 coins var coinCount = 5; if (typeof coinMultiplierEnabled !== "undefined" && coinMultiplierEnabled) { coinCount = Math.round(5 * 1.5); } for (var i = 0; i < coinCount; i++) { var coin = new Coin(); // Spread coins a bit var angle = Math.PI * 2 * i / coinCount; coin.x = self.x + Math.cos(angle) * 60; coin.y = self.y + Math.sin(angle) * 60; coins.push(coin); game.addChild(coin); } self.destroy(); var idx = zombies.indexOf(self); if (idx !== -1) zombies.splice(idx, 1); zombiesKilledThisWave++; }; return self; }); // 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.damage = 1; self.update = function () { self.x += self.vx; self.y += self.vy; }; return self; }); // Coin class var Coin = Container.expand(function () { var self = Container.call(this); var coinSprite = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.radius = coinSprite.width * 1.5; // Even further increase hitbox radius for much easier collection // Remove vy and falling logic so coin stays at spawn position self.collected = false; self.update = function () { // No falling, coin stays at its spawn position }; self.collect = function () { if (self.collected) return; self.collected = true; LK.getSound('coin').play(); // Animate to coinText var guiPos = LK.gui.topRight.toLocal(game.toGlobal({ x: self.x, y: self.y })); var startX = self.x, startY = self.y; var endX = 2048 - 200, endY = 80; tween(self, { x: endX, y: endY, alpha: 0 }, { duration: 400, easing: tween.cubicIn, onFinish: function onFinish() { self.destroy(); var idx = coins.indexOf(self); if (idx !== -1) coins.splice(idx, 1); if (typeof coinMultiplierLevel !== "undefined" && coinMultiplierLevel > 0) { coinsCollected += Math.pow(2, coinMultiplierLevel); } else { coinsCollected++; } coinsCollected = Math.floor(coinsCollected); updateCoinText(); } }); }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.radius = playerSprite.width / 2; self.hp = 10; self.maxHp = 10; self.shootCooldown = 0; self.shootDelay = 20; // frames self.weaponLevel = 1; self.lastShotTick = 0; // For drag self.down = function (x, y, obj) {}; self.up = function (x, y, obj) {}; self.update = function () { if (self.shootCooldown > 0) self.shootCooldown--; }; // Shoot method self.shoot = function (targetX, targetY) { if (self.shootCooldown > 0) return; var dx = targetX - self.x; var dy = targetY - self.y; var len = Math.sqrt(dx * dx + dy * dy); if (len === 0) return; dx /= len; dy /= len; var bullet = new Bullet(); bullet.x = self.x + dx * (self.radius + 30); bullet.y = self.y + dy * (self.radius + 30); bullet.vx = dx * (22 + self.weaponLevel * 2); bullet.vy = dy * (22 + self.weaponLevel * 2); bullet.damage = self.weaponLevel; // Set bullet rotation so the tip faces the direction of movement if (bullet.children && bullet.children.length > 0) { bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx); } bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); self.shootCooldown = self.shootDelay; }; // Take damage self.takeDamage = function (amount) { self.hp -= amount; if (self.hp < 0) self.hp = 0; LK.effects.flashObject(self, 0xff0000, 300); updateHpBar(); if (self.hp <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } }; return self; }); // Tower class var Tower = Container.expand(function () { var self = Container.call(this); var towerSprite = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); self.radius = Math.max(towerSprite.width, towerSprite.height) / 2; self.hp = 20; self.maxHp = 20; self.shootCooldown = 0; self.shootDelay = 40; self.takeDamage = function (amount) { self.hp -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.hp <= 0) { self.destroy(); var idx = defenses.indexOf(self); if (idx !== -1) defenses.splice(idx, 1); } }; self.update = function () { if (self.shootCooldown > 0) self.shootCooldown--; // Find nearest zombie var nearest = null, minDist = 99999; for (var i = 0; i < zombies.length; i++) { var z = zombies[i]; var dx = z.x - self.x; var dy = z.y - self.y; var dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; nearest = z; } } if (nearest && self.shootCooldown <= 0) { // Shoot at nearest zombie var dx = nearest.x - self.x; var dy = nearest.y - self.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { dx /= len; dy /= len; var bullet = new Bullet(); bullet.x = self.x + dx * (self.radius + 20); bullet.y = self.y + dy * (self.radius + 20); var towerWeaponLevel = typeof self.weaponLevel !== "undefined" ? self.weaponLevel : 1; bullet.vx = dx * (18 + towerWeaponLevel * 2); bullet.vy = dy * (18 + towerWeaponLevel * 2); bullet.damage = towerWeaponLevel; // Set bullet rotation so the tip faces the direction of movement if (bullet.children && bullet.children.length > 0) { bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx); } bullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); self.shootCooldown = self.shootDelay; } } }; return self; }); // Trap class var Trap = Container.expand(function () { var self = Container.call(this); var trapSprite = self.attachAsset('trap', { anchorX: 0.5, anchorY: 0.5 }); self.radius = Math.max(trapSprite.width, trapSprite.height) / 2; self.hp = 2; self.maxHp = 2; self.takeDamage = function (amount) { self.hp -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.hp <= 0) { self.destroy(); var idx = defenses.indexOf(self); if (idx !== -1) defenses.splice(idx, 1); } }; return self; }); // Wall class var Wall = Container.expand(function () { var self = Container.call(this); var wallSprite = self.attachAsset('wall', { anchorX: 0.5, anchorY: 0.5 }); self.radius = Math.max(wallSprite.width, wallSprite.height) / 2; self.hp = 6; self.maxHp = 6; self.takeDamage = function (amount) { self.hp -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.hp <= 0) { self.destroy(); var idx = defenses.indexOf(self); if (idx !== -1) defenses.splice(idx, 1); } }; 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; // Zombies get stronger only every 3 waves var zombiePowerLevel = Math.floor((currentWave - 1) / 3); self.speed = 2 + zombiePowerLevel * 0.9; self.hp = 1 + zombiePowerLevel; self.maxHp = self.hp; self.target = null; self.lastHitTick = 0; self.update = function () { // Move towards player var tx = player.x; var ty = player.y; // If wall/trap/tower is closer, target that var minDist = Math.sqrt((self.x - tx) * (self.x - tx) + (self.y - ty) * (self.y - ty)); var targetObj = player; for (var i = 0; i < defenses.length; i++) { var d = defenses[i]; var dist = Math.sqrt((self.x - d.x) * (self.x - d.x) + (self.y - d.y) * (self.y - d.y)); if (dist < minDist) { minDist = dist; targetObj = d; } } self.target = targetObj; var dx = targetObj.x - self.x; var dy = targetObj.y - self.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { dx /= len; dy /= len; self.x += dx * self.speed; self.y += dy * self.speed; } // Attack if close if (len < self.radius + targetObj.radius + 10) { if (LK.ticks - self.lastHitTick > 30) { if (targetObj === player) { player.takeDamage(1); } else if (targetObj.takeDamage) { targetObj.takeDamage(1); } self.lastHitTick = LK.ticks; } } }; self.takeDamage = function (amount) { self.hp -= amount; if (self.hp <= 0) { self.die(); } else { LK.effects.flashObject(self, 0xff0000, 200); } }; self.die = function () { LK.getSound('zombie_die').play(); // Drop coin var coin = new Coin(); coin.x = self.x; coin.y = self.y; coins.push(coin); game.addChild(coin); self.destroy(); var idx = zombies.indexOf(self); if (idx !== -1) zombies.splice(idx, 1); zombiesKilledThisWave++; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Sounds // Tower // Trap // Wall // Coin // Bullet // Zombie // Character (player) // Global variables var player; var zombies = []; var bullets = []; var coins = []; var defenses = []; var coinsCollected = 0; var autoCollectEnabled = false; var coinMultiplierEnabled = false; var coinMultiplier2Enabled = false; var currentWave = 1; var zombiesToSpawn = 0; var zombiesSpawnedThisWave = 0; var zombiesKilledThisWave = 0; var waveInProgress = false; var dragNode = null; var buildMode = null; // 'wall', 'trap', 'tower', 'upgrade', 'autocollect', 'coinmult' var buildPreview = null; var buildCost = { wall: 5, trap: 7, tower: 30, upgrade: 10, autocollect: 20, coinmult: 50 }; var buildNames = { wall: "Duvar", trap: "Tuzak", tower: "Kule", upgrade: "Silah+", autocollect: "Oto-Para", coinmult: "Para x2" }; // Track current coin multiplier and its cost var coinMultiplierLevel = 1; // 1 means 2x, 2 means 4x, etc. var coinMultiplierCost = 100; // Cost for next multiplier (starts at 100 for 2x) // buildDesc removed var hpBar, coinText, waveText, buildPanel, buildButtons = []; var lastTouchX = 0, lastTouchY = 0; // UI: HP Bar function updateHpBar() { if (!hpBar) return; var txt = ""; for (var i = 0; i < player.maxHp; i++) { txt += i < player.hp ? "♥ " : "♡ "; } hpBar.setText(txt); } // UI: Coin Text function updateCoinText() { if (coinText) coinText.setText("₺ " + coinsCollected); // Rebuild build panel to update overlays/locks if it exists if (typeof buildPanel !== "undefined" && buildPanel) { buildPanel.destroy(); buildButtons = []; createBuildPanel(); } } // UI: Wave Text function updateWaveText() { if (waveText) waveText.setText("Dalga: " + currentWave); } // UI: Build Panel function createBuildPanel() { buildPanel = new Container(); buildPanel.y = 0; buildPanel.x = 0; var btnW = 140, btnH = 56, gap = 18; // Dynamically build types array based on upgrade state var types = ['wall', 'trap', 'tower', 'upgrade', 'heal']; if (!autoCollectEnabled) { types.push('autocollect'); } else { types.push('coinmult'); } // If coinmult is present, we will handle its label and cost dynamically below for (var i = 0; i < types.length; i++) { (function (type, idx) { var btn = new Container(); // Use original images for each type except for upgrade/autocollect/coinmult/checkpoint var assetId = 'wall'; if (type === 'trap') assetId = 'trap';else if (type === 'tower') assetId = 'tower'; // Remove blue color, always use gray for button backgrounds var bg = LK.getAsset(assetId, { width: btnW, height: btnH, color: 0x888888, anchorX: 0, anchorY: 0 }); btn.addChild(bg); var label, cost; if (type === 'coinmult') { // Show next multiplier (2x, 4x, 8x, ...) and its cost var nextMult = Math.pow(2, coinMultiplierLevel + 1); label = "Para x" + nextMult; cost = coinMultiplierCost; } else if (type === 'heal') { label = "Can +1"; cost = 75; } else { label = buildNames[type] ? buildNames[type] : type; cost = buildCost[type] ? buildCost[type] : 0; } var txt = new Text2(label + "\n₺" + cost, { size: 32, fill: "#fff" }); txt.x = btnW / 2; txt.y = 12; txt.anchor.set(0.5, 0); btn.addChild(txt); btn.x = idx * (btnW + gap) - 10; // Move each button 10px to the left (was 100px) btn.y = 0; btn.type = type; // Check if player can afford this item var canAfford = coinsCollected >= cost; if (!canAfford) { // Add a semi-transparent black overlay // Remove blue color, always use gray for lock overlays var overlay = LK.getAsset('wall', { width: btnW, height: btnH, color: 0x888888, anchorX: 0, anchorY: 0 }); overlay.alpha = 0.45; btn.addChild(overlay); // Add a lock icon (use a Text2 lock emoji for simplicity) var lockIcon = new Text2("🔒", { size: 38, fill: "#fff" }); lockIcon.x = btnW / 2; lockIcon.y = btnH / 2 - 18; lockIcon.anchor.set(0.5, 0.5); btn.addChild(lockIcon); // Disable interaction btn.down = function () {}; } else { btn.down = function (x, y, obj) { setBuildMode(type); }; } buildPanel.addChild(btn); buildButtons.push(btn); })(types[i], i); } buildPanel.y = -120; // Move the build panel further up by 120px (closer to bottom) buildPanel.x = -320; // Move the build panel to the right (was -540) LK.gui.bottom.addChild(buildPanel); } // Set build mode function setBuildMode(type) { buildMode = type; if (buildPreview) { buildPreview.destroy(); buildPreview = null; } if (type === 'wall') { buildPreview = new Wall(); } else if (type === 'trap') { buildPreview = new Trap(); } else if (type === 'tower') { buildPreview = new Tower(); } else if (type === 'upgrade' || type === 'autocollect') { buildPreview = null; } if (buildPreview) { buildPreview.alpha = 0.5; game.addChild(buildPreview); } } // Remove build preview function clearBuildPreview() { if (buildPreview) { buildPreview.destroy(); buildPreview = null; } buildMode = null; } // Start new wave function startWave() { zombiesToSpawn = 5 + currentWave * 2; zombiesSpawnedThisWave = 0; zombiesKilledThisWave = 0; waveInProgress = true; updateWaveText(); // Spawn boss every 3 waves if (currentWave % 3 === 0) { // Boss will be spawned after all regular zombies zombiesToSpawn += 1; } } // Spawn zombie function spawnZombie() { // Spawn at random edge var edge = Math.floor(Math.random() * 4); var zx, zy; if (edge === 0) { // top zx = 200 + Math.random() * (2048 - 400); zy = -60; } else if (edge === 1) { // right zx = 2048 + 60; zy = 200 + Math.random() * (2732 - 400); } else if (edge === 2) { // bottom zx = 200 + Math.random() * (2048 - 400); zy = 2732 + 60; } else { // left zx = -60; zy = 200 + Math.random() * (2732 - 400); } // If this is a boss wave and this is the last zombie, spawn boss var isBossWave = currentWave % 3 === 0; var isLastZombie = zombiesSpawnedThisWave === zombiesToSpawn - 1 && isBossWave; var z; if (isLastZombie) { z = new BossZombie(); } else { z = new Zombie(); } z.x = zx; z.y = zy; zombies.push(z); game.addChild(z); zombiesSpawnedThisWave++; } // Build defense function buildDefense(type, x, y) { if (coinsCollected < buildCost[type]) return false; var obj; if (type === 'wall') { obj = new Wall(); } else if (type === 'trap') { obj = new Trap(); } else if (type === 'tower') { obj = new Tower(); } if (obj) { obj.x = x; obj.y = y; defenses.push(obj); game.addChild(obj); coinsCollected -= buildCost[type]; updateCoinText(); LK.getSound('build').play(); return true; } return false; } // Upgrade weapon function upgradeWeapon() { if (coinsCollected < buildCost.upgrade) return false; player.weaponLevel++; // Upgrade all towers' shoot power for (var i = 0; i < defenses.length; i++) { if (defenses[i] && defenses[i] instanceof Tower) { // Increase tower bullet damage and speed with each upgrade if (typeof defenses[i].weaponLevel === "undefined") { defenses[i].weaponLevel = 1; } defenses[i].weaponLevel++; } } coinsCollected -= buildCost.upgrade; updateCoinText(); LK.effects.flashObject(player, 0xffff00, 600); LK.getSound('build').play(); return true; } // Handle move (drag, build preview, collect coin) function handleMove(x, y, obj) { lastTouchX = x; lastTouchY = y; // Drag player if (dragNode === player) { // Clamp to game area var r = player.radius + 10; player.x = Math.max(r, Math.min(2048 - r, x)); player.y = Math.max(r, Math.min(2732 - r, y)); } // Build preview follows finger if (buildPreview) { buildPreview.x = x; buildPreview.y = y; } // Coin collection for (var i = coins.length - 1; i >= 0; i--) { var c = coins[i]; var dx = c.x - x, dy = c.y - y; if (!c.collected && dx * dx + dy * dy < c.radius * c.radius + 1200) { c.collect(); } } } // Handle down (start drag, build, upgrade) game.down = function (x, y, obj) { // If build mode is active if (buildMode) { if (buildMode === 'upgrade') { if (upgradeWeapon()) { clearBuildPreview(); } } else if (buildMode === 'autocollect') { if (!autoCollectEnabled && coinsCollected >= buildCost.autocollect) { coinsCollected -= buildCost.autocollect; updateCoinText(); autoCollectEnabled = true; LK.effects.flashObject(player, 0x00ffcc, 600); LK.getSound('build').play(); clearBuildPreview(); // Recreate build panel to remove autocollect and add coinmult if (buildPanel) { buildPanel.destroy(); buildButtons = []; createBuildPanel(); } } } else if (buildMode === 'coinmult') { if (coinsCollected >= coinMultiplierCost) { coinsCollected -= coinMultiplierCost; updateCoinText(); coinMultiplierLevel++; coinMultiplierCost += 100; LK.effects.flashObject(player, 0xffe066, 600); LK.getSound('build').play(); clearBuildPreview(); // Recreate build panel to update multiplier label/cost if (buildPanel) { buildPanel.destroy(); buildButtons = []; createBuildPanel(); } } } else if (buildMode === 'heal') { if (coinsCollected >= 75 && player.hp < player.maxHp) { coinsCollected -= 75; player.hp += 1; if (player.hp > player.maxHp) player.hp = player.maxHp; updateCoinText(); updateHpBar(); LK.effects.flashObject(player, 0x00ff44, 600); LK.getSound('build').play(); clearBuildPreview(); } } else { // Place defense if possible if (buildPreview) { // Don't allow build on player or on top of other defenses var canBuild = true; var dx = player.x - x, dy = player.y - y; if (dx * dx + dy * dy < (player.radius + buildPreview.radius + 40) * (player.radius + buildPreview.radius + 40)) { canBuild = false; } for (var i = 0; i < defenses.length; i++) { var d = defenses[i]; var ddx = d.x - x, ddy = d.y - y; if (ddx * ddx + ddy * ddy < (d.radius + buildPreview.radius + 30) * (d.radius + buildPreview.radius + 30)) { canBuild = false; break; } } if (canBuild && buildDefense(buildMode, x, y)) { clearBuildPreview(); } } } return; } // Only handle mouse/touch events with event object var event = obj && obj.event ? obj.event : null; var isLeftClick = false; var isMiddleClick = false; // Mouse events if (event && typeof event.button !== "undefined") { // 0: left, 1: middle if (event.button === 0) isLeftClick = true; if (event.button === 1) isMiddleClick = true; } else { // Touch events: treat as left click (drag/shoot) isLeftClick = true; } if (isMiddleClick) { // Teleport player to clicked position, clamp to game area var r = player.radius + 10; player.x = Math.max(r, Math.min(2048 - r, x)); player.y = Math.max(r, Math.min(2732 - r, y)); } else if (isLeftClick) { // If click/touch is on player, start drag var dx = player.x - x, dy = player.y - y; if (dx * dx + dy * dy < player.radius * player.radius + 2000) { dragNode = player; } else { // Otherwise, shoot player.shoot(x, y); } } handleMove(x, y, obj); }; // Prevent context menu on right click for the canvas if (typeof window !== "undefined" && typeof document !== "undefined") { var canvas = document.querySelector('canvas'); if (canvas) { canvas.oncontextmenu = function (e) { e.preventDefault(); return false; }; } } // Handle up (stop drag, clear build preview) game.up = function (x, y, obj) { dragNode = null; // If build preview exists, clear it if not placed if (buildPreview && buildMode) { clearBuildPreview(); } }; // Handle move game.move = handleMove; // Game update game.update = function () { // --- Virtual Joystick Movement --- var moveSpeed = 18 + player.weaponLevel * 2; if (typeof vbtns !== "undefined") { var dx = 0, dy = 0; if (vbtns.up && vbtns.up.isDown) dy -= 1; if (vbtns.down && vbtns.down.isDown) dy += 1; if (vbtns.left && vbtns.left.isDown) dx -= 1; if (vbtns.right && vbtns.right.isDown) dx += 1; if (dx !== 0 || dy !== 0) { var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { dx /= len; dy /= len; // Clamp to game area var r = player.radius + 10; player.x = Math.max(r, Math.min(2048 - r, player.x + dx * moveSpeed)); player.y = Math.max(r, Math.min(2732 - r, player.y + dy * moveSpeed)); } } } // Player update player.update(); // Zombies update for (var i = zombies.length - 1; i >= 0; i--) { zombies[i].update(); } // Bullets update for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; b.update(); // Remove if out of bounds if (b.x < -100 || b.x > 2148 || b.y < -100 || b.y > 2832) { b.destroy(); bullets.splice(i, 1); continue; } // Check collision with zombies for (var j = zombies.length - 1; j >= 0; j--) { var z = zombies[j]; var dx = z.x - b.x, dy = z.y - b.y; if (dx * dx + dy * dy < (z.radius + b.radius) * (z.radius + b.radius)) { z.takeDamage(b.damage); b.destroy(); bullets.splice(i, 1); break; } } } // Coins update for (var i = coins.length - 1; i >= 0; i--) { coins[i].update(); if (autoCollectEnabled && !coins[i].collected) { coins[i].collect(); } } // Defenses update for (var i = defenses.length - 1; i >= 0; i--) { if (defenses[i].update) defenses[i].update(); } // Wave logic if (waveInProgress) { // Spawn zombies if (zombiesSpawnedThisWave < zombiesToSpawn && LK.ticks % Math.max(20, 60 - currentWave * 2) === 0) { spawnZombie(); } // Check wave end if (zombiesKilledThisWave >= zombiesToSpawn && zombies.length === 0) { waveInProgress = false; // Give 5 coins every 5 waves at the end of the wave if (currentWave % 5 === 0) { coinsCollected += 5; updateCoinText(); } // Next wave after 4 seconds (4000 ms) LK.setTimeout(function () { currentWave++; startWave(); }, 4000); } } }; // --- Game Initialization --- // Add forest background (drawn as a large green rectangle with tree-like ellipses) var forestBg = new Container(); var bgRect = LK.getAsset('military_bg', { width: 2048, height: 2732, color: 0x2e4d1a, // deep forest green anchorX: 0, anchorY: 0, x: 0, y: 0 }); forestBg.addChild(bgRect); // Add some tree ellipses for a forest look for (var i = 0; i < 18; i++) { var tree = LK.getAsset('military_bg', { width: 180 + Math.floor(Math.random() * 80), height: 320 + Math.floor(Math.random() * 60), color: 0x3e7d2a + Math.floor(Math.random() * 0x002000), shape: 'ellipse', anchorX: 0.5, anchorY: 1, x: 120 + Math.random() * 1800, y: 400 + Math.random() * 2100 }); tree.alpha = 0.22 + Math.random() * 0.18; forestBg.addChild(tree); } game.addChild(forestBg); // Place player at center player = new Player(); player.x = 2048 / 2; player.y = 2732 / 2; game.addChild(player); // --- Virtual Joystick Buttons --- // Button size and margin var vbtnSize = 160; var vbtnMargin = 40; var vbtnAlpha = 0.45; var vbtnColor = 0x888888; var vbtns = {}; // Helper to create a button function createVBtn(label, x, y, dir) { var btn = new Container(); // Remove blue color, always use gray for joystick button backgrounds var bg = LK.getAsset('wall', { width: vbtnSize, height: vbtnSize, color: 0x888888, anchorX: 0.5, anchorY: 0.5 }); bg.alpha = vbtnAlpha; btn.addChild(bg); var txt = new Text2(label, { size: 80, fill: "#fff" }); txt.anchor.set(0.5, 0.5); txt.x = 0; txt.y = 0; btn.addChild(txt); btn.x = x; btn.y = y; btn.dir = dir; btn.isDown = false; btn.down = function () { btn.isDown = true; }; btn.up = function () { btn.isDown = false; }; LK.gui.bottomLeft.addChild(btn); return btn; } // Positioning for bottom left corner var baseX = vbtnSize + vbtnMargin; var baseY = -vbtnSize - vbtnMargin; // Up vbtns.up = createVBtn("▲", baseX, baseY - vbtnSize, "up"); // Down vbtns.down = createVBtn("▼", baseX, baseY + vbtnSize, "down"); // Left vbtns.left = createVBtn("◀", baseX - vbtnSize, baseY, "left"); // Right vbtns.right = createVBtn("▶", baseX + vbtnSize, baseY, "right"); // UI: HP Bar hpBar = new Text2("", { size: 38, fill: 0xFF4444 }); // Move hearts to the absolute left (x=0), keep y=20 hpBar.anchor.set(0, 0); LK.gui.top.addChild(hpBar); hpBar.x = 0; hpBar.y = 20; updateHpBar(); // UI: Coin Text coinText = new Text2("₺ 0", { size: 70, fill: 0xFFE066 }); coinText.anchor.set(1, 0); LK.gui.topRight.addChild(coinText); coinText.x = -40; coinText.y = 20; // UI: Wave Text waveText = new Text2("Dalga: 1", { size: 60, fill: "#fff" }); waveText.anchor.set(0, 0); LK.gui.top.addChild(waveText); waveText.x = 60; waveText.y = 120; // UI: Build Panel createBuildPanel(); // Start first wave startWave(); updateCoinText(); updateWaveText();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// BossZombie class
var BossZombie = Container.expand(function () {
var self = Container.call(this);
var bossSprite = self.attachAsset('zombie', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.7,
scaleY: 1.7,
color: 0x8e44ad // purple tint for boss
});
self.radius = bossSprite.width / 2;
var bossPowerLevel = Math.floor((currentWave - 1) / 3);
self.speed = 1.5 + bossPowerLevel * 0.5;
self.hp = 10 + bossPowerLevel * 5;
self.maxHp = self.hp;
self.target = null;
self.lastHitTick = 0;
self.update = function () {
// Move towards player
var tx = player.x;
var ty = player.y;
// If wall/trap/tower is closer, target that
var minDist = Math.sqrt((self.x - tx) * (self.x - tx) + (self.y - ty) * (self.y - ty));
var targetObj = player;
for (var i = 0; i < defenses.length; i++) {
var d = defenses[i];
var dist = Math.sqrt((self.x - d.x) * (self.x - d.x) + (self.y - d.y) * (self.y - d.y));
if (dist < minDist) {
minDist = dist;
targetObj = d;
}
}
self.target = targetObj;
var dx = targetObj.x - self.x;
var dy = targetObj.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
dx /= len;
dy /= len;
self.x += dx * self.speed;
self.y += dy * self.speed;
}
// Attack if close
if (len < self.radius + targetObj.radius + 10) {
if (LK.ticks - self.lastHitTick > 30) {
if (targetObj === player) {
player.takeDamage(2); // Boss does more damage
} else if (targetObj.takeDamage) {
targetObj.takeDamage(2);
}
self.lastHitTick = LK.ticks;
}
}
};
self.takeDamage = function (amount) {
self.hp -= amount;
if (self.hp <= 0) {
self.die();
} else {
LK.effects.flashObject(self, 0xff00ff, 300);
}
};
self.die = function () {
LK.getSound('zombie_die').play();
// Drop 5 coins
var coinCount = 5;
if (typeof coinMultiplierEnabled !== "undefined" && coinMultiplierEnabled) {
coinCount = Math.round(5 * 1.5);
}
for (var i = 0; i < coinCount; i++) {
var coin = new Coin();
// Spread coins a bit
var angle = Math.PI * 2 * i / coinCount;
coin.x = self.x + Math.cos(angle) * 60;
coin.y = self.y + Math.sin(angle) * 60;
coins.push(coin);
game.addChild(coin);
}
self.destroy();
var idx = zombies.indexOf(self);
if (idx !== -1) zombies.splice(idx, 1);
zombiesKilledThisWave++;
};
return self;
});
// 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.damage = 1;
self.update = function () {
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinSprite = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = coinSprite.width * 1.5; // Even further increase hitbox radius for much easier collection
// Remove vy and falling logic so coin stays at spawn position
self.collected = false;
self.update = function () {
// No falling, coin stays at its spawn position
};
self.collect = function () {
if (self.collected) return;
self.collected = true;
LK.getSound('coin').play();
// Animate to coinText
var guiPos = LK.gui.topRight.toLocal(game.toGlobal({
x: self.x,
y: self.y
}));
var startX = self.x,
startY = self.y;
var endX = 2048 - 200,
endY = 80;
tween(self, {
x: endX,
y: endY,
alpha: 0
}, {
duration: 400,
easing: tween.cubicIn,
onFinish: function onFinish() {
self.destroy();
var idx = coins.indexOf(self);
if (idx !== -1) coins.splice(idx, 1);
if (typeof coinMultiplierLevel !== "undefined" && coinMultiplierLevel > 0) {
coinsCollected += Math.pow(2, coinMultiplierLevel);
} else {
coinsCollected++;
}
coinsCollected = Math.floor(coinsCollected);
updateCoinText();
}
});
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = playerSprite.width / 2;
self.hp = 10;
self.maxHp = 10;
self.shootCooldown = 0;
self.shootDelay = 20; // frames
self.weaponLevel = 1;
self.lastShotTick = 0;
// For drag
self.down = function (x, y, obj) {};
self.up = function (x, y, obj) {};
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
};
// Shoot method
self.shoot = function (targetX, targetY) {
if (self.shootCooldown > 0) return;
var dx = targetX - self.x;
var dy = targetY - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len === 0) return;
dx /= len;
dy /= len;
var bullet = new Bullet();
bullet.x = self.x + dx * (self.radius + 30);
bullet.y = self.y + dy * (self.radius + 30);
bullet.vx = dx * (22 + self.weaponLevel * 2);
bullet.vy = dy * (22 + self.weaponLevel * 2);
bullet.damage = self.weaponLevel;
// Set bullet rotation so the tip faces the direction of movement
if (bullet.children && bullet.children.length > 0) {
bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx);
}
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = self.shootDelay;
};
// Take damage
self.takeDamage = function (amount) {
self.hp -= amount;
if (self.hp < 0) self.hp = 0;
LK.effects.flashObject(self, 0xff0000, 300);
updateHpBar();
if (self.hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
};
return self;
});
// Tower class
var Tower = Container.expand(function () {
var self = Container.call(this);
var towerSprite = self.attachAsset('tower', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = Math.max(towerSprite.width, towerSprite.height) / 2;
self.hp = 20;
self.maxHp = 20;
self.shootCooldown = 0;
self.shootDelay = 40;
self.takeDamage = function (amount) {
self.hp -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.hp <= 0) {
self.destroy();
var idx = defenses.indexOf(self);
if (idx !== -1) defenses.splice(idx, 1);
}
};
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
// Find nearest zombie
var nearest = null,
minDist = 99999;
for (var i = 0; i < zombies.length; i++) {
var z = zombies[i];
var dx = z.x - self.x;
var dy = z.y - self.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
nearest = z;
}
}
if (nearest && self.shootCooldown <= 0) {
// Shoot at nearest zombie
var dx = nearest.x - self.x;
var dy = nearest.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
dx /= len;
dy /= len;
var bullet = new Bullet();
bullet.x = self.x + dx * (self.radius + 20);
bullet.y = self.y + dy * (self.radius + 20);
var towerWeaponLevel = typeof self.weaponLevel !== "undefined" ? self.weaponLevel : 1;
bullet.vx = dx * (18 + towerWeaponLevel * 2);
bullet.vy = dy * (18 + towerWeaponLevel * 2);
bullet.damage = towerWeaponLevel;
// Set bullet rotation so the tip faces the direction of movement
if (bullet.children && bullet.children.length > 0) {
bullet.children[0].rotation = Math.atan2(bullet.vy, bullet.vx);
}
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.shootCooldown = self.shootDelay;
}
}
};
return self;
});
// Trap class
var Trap = Container.expand(function () {
var self = Container.call(this);
var trapSprite = self.attachAsset('trap', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = Math.max(trapSprite.width, trapSprite.height) / 2;
self.hp = 2;
self.maxHp = 2;
self.takeDamage = function (amount) {
self.hp -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.hp <= 0) {
self.destroy();
var idx = defenses.indexOf(self);
if (idx !== -1) defenses.splice(idx, 1);
}
};
return self;
});
// Wall class
var Wall = Container.expand(function () {
var self = Container.call(this);
var wallSprite = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = Math.max(wallSprite.width, wallSprite.height) / 2;
self.hp = 6;
self.maxHp = 6;
self.takeDamage = function (amount) {
self.hp -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
if (self.hp <= 0) {
self.destroy();
var idx = defenses.indexOf(self);
if (idx !== -1) defenses.splice(idx, 1);
}
};
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;
// Zombies get stronger only every 3 waves
var zombiePowerLevel = Math.floor((currentWave - 1) / 3);
self.speed = 2 + zombiePowerLevel * 0.9;
self.hp = 1 + zombiePowerLevel;
self.maxHp = self.hp;
self.target = null;
self.lastHitTick = 0;
self.update = function () {
// Move towards player
var tx = player.x;
var ty = player.y;
// If wall/trap/tower is closer, target that
var minDist = Math.sqrt((self.x - tx) * (self.x - tx) + (self.y - ty) * (self.y - ty));
var targetObj = player;
for (var i = 0; i < defenses.length; i++) {
var d = defenses[i];
var dist = Math.sqrt((self.x - d.x) * (self.x - d.x) + (self.y - d.y) * (self.y - d.y));
if (dist < minDist) {
minDist = dist;
targetObj = d;
}
}
self.target = targetObj;
var dx = targetObj.x - self.x;
var dy = targetObj.y - self.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
dx /= len;
dy /= len;
self.x += dx * self.speed;
self.y += dy * self.speed;
}
// Attack if close
if (len < self.radius + targetObj.radius + 10) {
if (LK.ticks - self.lastHitTick > 30) {
if (targetObj === player) {
player.takeDamage(1);
} else if (targetObj.takeDamage) {
targetObj.takeDamage(1);
}
self.lastHitTick = LK.ticks;
}
}
};
self.takeDamage = function (amount) {
self.hp -= amount;
if (self.hp <= 0) {
self.die();
} else {
LK.effects.flashObject(self, 0xff0000, 200);
}
};
self.die = function () {
LK.getSound('zombie_die').play();
// Drop coin
var coin = new Coin();
coin.x = self.x;
coin.y = self.y;
coins.push(coin);
game.addChild(coin);
self.destroy();
var idx = zombies.indexOf(self);
if (idx !== -1) zombies.splice(idx, 1);
zombiesKilledThisWave++;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Sounds
// Tower
// Trap
// Wall
// Coin
// Bullet
// Zombie
// Character (player)
// Global variables
var player;
var zombies = [];
var bullets = [];
var coins = [];
var defenses = [];
var coinsCollected = 0;
var autoCollectEnabled = false;
var coinMultiplierEnabled = false;
var coinMultiplier2Enabled = false;
var currentWave = 1;
var zombiesToSpawn = 0;
var zombiesSpawnedThisWave = 0;
var zombiesKilledThisWave = 0;
var waveInProgress = false;
var dragNode = null;
var buildMode = null; // 'wall', 'trap', 'tower', 'upgrade', 'autocollect', 'coinmult'
var buildPreview = null;
var buildCost = {
wall: 5,
trap: 7,
tower: 30,
upgrade: 10,
autocollect: 20,
coinmult: 50
};
var buildNames = {
wall: "Duvar",
trap: "Tuzak",
tower: "Kule",
upgrade: "Silah+",
autocollect: "Oto-Para",
coinmult: "Para x2"
};
// Track current coin multiplier and its cost
var coinMultiplierLevel = 1; // 1 means 2x, 2 means 4x, etc.
var coinMultiplierCost = 100; // Cost for next multiplier (starts at 100 for 2x)
// buildDesc removed
var hpBar,
coinText,
waveText,
buildPanel,
buildButtons = [];
var lastTouchX = 0,
lastTouchY = 0;
// UI: HP Bar
function updateHpBar() {
if (!hpBar) return;
var txt = "";
for (var i = 0; i < player.maxHp; i++) {
txt += i < player.hp ? "♥ " : "♡ ";
}
hpBar.setText(txt);
}
// UI: Coin Text
function updateCoinText() {
if (coinText) coinText.setText("₺ " + coinsCollected);
// Rebuild build panel to update overlays/locks if it exists
if (typeof buildPanel !== "undefined" && buildPanel) {
buildPanel.destroy();
buildButtons = [];
createBuildPanel();
}
}
// UI: Wave Text
function updateWaveText() {
if (waveText) waveText.setText("Dalga: " + currentWave);
}
// UI: Build Panel
function createBuildPanel() {
buildPanel = new Container();
buildPanel.y = 0;
buildPanel.x = 0;
var btnW = 140,
btnH = 56,
gap = 18;
// Dynamically build types array based on upgrade state
var types = ['wall', 'trap', 'tower', 'upgrade', 'heal'];
if (!autoCollectEnabled) {
types.push('autocollect');
} else {
types.push('coinmult');
}
// If coinmult is present, we will handle its label and cost dynamically below
for (var i = 0; i < types.length; i++) {
(function (type, idx) {
var btn = new Container();
// Use original images for each type except for upgrade/autocollect/coinmult/checkpoint
var assetId = 'wall';
if (type === 'trap') assetId = 'trap';else if (type === 'tower') assetId = 'tower';
// Remove blue color, always use gray for button backgrounds
var bg = LK.getAsset(assetId, {
width: btnW,
height: btnH,
color: 0x888888,
anchorX: 0,
anchorY: 0
});
btn.addChild(bg);
var label, cost;
if (type === 'coinmult') {
// Show next multiplier (2x, 4x, 8x, ...) and its cost
var nextMult = Math.pow(2, coinMultiplierLevel + 1);
label = "Para x" + nextMult;
cost = coinMultiplierCost;
} else if (type === 'heal') {
label = "Can +1";
cost = 75;
} else {
label = buildNames[type] ? buildNames[type] : type;
cost = buildCost[type] ? buildCost[type] : 0;
}
var txt = new Text2(label + "\n₺" + cost, {
size: 32,
fill: "#fff"
});
txt.x = btnW / 2;
txt.y = 12;
txt.anchor.set(0.5, 0);
btn.addChild(txt);
btn.x = idx * (btnW + gap) - 10; // Move each button 10px to the left (was 100px)
btn.y = 0;
btn.type = type;
// Check if player can afford this item
var canAfford = coinsCollected >= cost;
if (!canAfford) {
// Add a semi-transparent black overlay
// Remove blue color, always use gray for lock overlays
var overlay = LK.getAsset('wall', {
width: btnW,
height: btnH,
color: 0x888888,
anchorX: 0,
anchorY: 0
});
overlay.alpha = 0.45;
btn.addChild(overlay);
// Add a lock icon (use a Text2 lock emoji for simplicity)
var lockIcon = new Text2("🔒", {
size: 38,
fill: "#fff"
});
lockIcon.x = btnW / 2;
lockIcon.y = btnH / 2 - 18;
lockIcon.anchor.set(0.5, 0.5);
btn.addChild(lockIcon);
// Disable interaction
btn.down = function () {};
} else {
btn.down = function (x, y, obj) {
setBuildMode(type);
};
}
buildPanel.addChild(btn);
buildButtons.push(btn);
})(types[i], i);
}
buildPanel.y = -120; // Move the build panel further up by 120px (closer to bottom)
buildPanel.x = -320; // Move the build panel to the right (was -540)
LK.gui.bottom.addChild(buildPanel);
}
// Set build mode
function setBuildMode(type) {
buildMode = type;
if (buildPreview) {
buildPreview.destroy();
buildPreview = null;
}
if (type === 'wall') {
buildPreview = new Wall();
} else if (type === 'trap') {
buildPreview = new Trap();
} else if (type === 'tower') {
buildPreview = new Tower();
} else if (type === 'upgrade' || type === 'autocollect') {
buildPreview = null;
}
if (buildPreview) {
buildPreview.alpha = 0.5;
game.addChild(buildPreview);
}
}
// Remove build preview
function clearBuildPreview() {
if (buildPreview) {
buildPreview.destroy();
buildPreview = null;
}
buildMode = null;
}
// Start new wave
function startWave() {
zombiesToSpawn = 5 + currentWave * 2;
zombiesSpawnedThisWave = 0;
zombiesKilledThisWave = 0;
waveInProgress = true;
updateWaveText();
// Spawn boss every 3 waves
if (currentWave % 3 === 0) {
// Boss will be spawned after all regular zombies
zombiesToSpawn += 1;
}
}
// Spawn zombie
function spawnZombie() {
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
var zx, zy;
if (edge === 0) {
// top
zx = 200 + Math.random() * (2048 - 400);
zy = -60;
} else if (edge === 1) {
// right
zx = 2048 + 60;
zy = 200 + Math.random() * (2732 - 400);
} else if (edge === 2) {
// bottom
zx = 200 + Math.random() * (2048 - 400);
zy = 2732 + 60;
} else {
// left
zx = -60;
zy = 200 + Math.random() * (2732 - 400);
}
// If this is a boss wave and this is the last zombie, spawn boss
var isBossWave = currentWave % 3 === 0;
var isLastZombie = zombiesSpawnedThisWave === zombiesToSpawn - 1 && isBossWave;
var z;
if (isLastZombie) {
z = new BossZombie();
} else {
z = new Zombie();
}
z.x = zx;
z.y = zy;
zombies.push(z);
game.addChild(z);
zombiesSpawnedThisWave++;
}
// Build defense
function buildDefense(type, x, y) {
if (coinsCollected < buildCost[type]) return false;
var obj;
if (type === 'wall') {
obj = new Wall();
} else if (type === 'trap') {
obj = new Trap();
} else if (type === 'tower') {
obj = new Tower();
}
if (obj) {
obj.x = x;
obj.y = y;
defenses.push(obj);
game.addChild(obj);
coinsCollected -= buildCost[type];
updateCoinText();
LK.getSound('build').play();
return true;
}
return false;
}
// Upgrade weapon
function upgradeWeapon() {
if (coinsCollected < buildCost.upgrade) return false;
player.weaponLevel++;
// Upgrade all towers' shoot power
for (var i = 0; i < defenses.length; i++) {
if (defenses[i] && defenses[i] instanceof Tower) {
// Increase tower bullet damage and speed with each upgrade
if (typeof defenses[i].weaponLevel === "undefined") {
defenses[i].weaponLevel = 1;
}
defenses[i].weaponLevel++;
}
}
coinsCollected -= buildCost.upgrade;
updateCoinText();
LK.effects.flashObject(player, 0xffff00, 600);
LK.getSound('build').play();
return true;
}
// Handle move (drag, build preview, collect coin)
function handleMove(x, y, obj) {
lastTouchX = x;
lastTouchY = y;
// Drag player
if (dragNode === player) {
// Clamp to game area
var r = player.radius + 10;
player.x = Math.max(r, Math.min(2048 - r, x));
player.y = Math.max(r, Math.min(2732 - r, y));
}
// Build preview follows finger
if (buildPreview) {
buildPreview.x = x;
buildPreview.y = y;
}
// Coin collection
for (var i = coins.length - 1; i >= 0; i--) {
var c = coins[i];
var dx = c.x - x,
dy = c.y - y;
if (!c.collected && dx * dx + dy * dy < c.radius * c.radius + 1200) {
c.collect();
}
}
}
// Handle down (start drag, build, upgrade)
game.down = function (x, y, obj) {
// If build mode is active
if (buildMode) {
if (buildMode === 'upgrade') {
if (upgradeWeapon()) {
clearBuildPreview();
}
} else if (buildMode === 'autocollect') {
if (!autoCollectEnabled && coinsCollected >= buildCost.autocollect) {
coinsCollected -= buildCost.autocollect;
updateCoinText();
autoCollectEnabled = true;
LK.effects.flashObject(player, 0x00ffcc, 600);
LK.getSound('build').play();
clearBuildPreview();
// Recreate build panel to remove autocollect and add coinmult
if (buildPanel) {
buildPanel.destroy();
buildButtons = [];
createBuildPanel();
}
}
} else if (buildMode === 'coinmult') {
if (coinsCollected >= coinMultiplierCost) {
coinsCollected -= coinMultiplierCost;
updateCoinText();
coinMultiplierLevel++;
coinMultiplierCost += 100;
LK.effects.flashObject(player, 0xffe066, 600);
LK.getSound('build').play();
clearBuildPreview();
// Recreate build panel to update multiplier label/cost
if (buildPanel) {
buildPanel.destroy();
buildButtons = [];
createBuildPanel();
}
}
} else if (buildMode === 'heal') {
if (coinsCollected >= 75 && player.hp < player.maxHp) {
coinsCollected -= 75;
player.hp += 1;
if (player.hp > player.maxHp) player.hp = player.maxHp;
updateCoinText();
updateHpBar();
LK.effects.flashObject(player, 0x00ff44, 600);
LK.getSound('build').play();
clearBuildPreview();
}
} else {
// Place defense if possible
if (buildPreview) {
// Don't allow build on player or on top of other defenses
var canBuild = true;
var dx = player.x - x,
dy = player.y - y;
if (dx * dx + dy * dy < (player.radius + buildPreview.radius + 40) * (player.radius + buildPreview.radius + 40)) {
canBuild = false;
}
for (var i = 0; i < defenses.length; i++) {
var d = defenses[i];
var ddx = d.x - x,
ddy = d.y - y;
if (ddx * ddx + ddy * ddy < (d.radius + buildPreview.radius + 30) * (d.radius + buildPreview.radius + 30)) {
canBuild = false;
break;
}
}
if (canBuild && buildDefense(buildMode, x, y)) {
clearBuildPreview();
}
}
}
return;
}
// Only handle mouse/touch events with event object
var event = obj && obj.event ? obj.event : null;
var isLeftClick = false;
var isMiddleClick = false;
// Mouse events
if (event && typeof event.button !== "undefined") {
// 0: left, 1: middle
if (event.button === 0) isLeftClick = true;
if (event.button === 1) isMiddleClick = true;
} else {
// Touch events: treat as left click (drag/shoot)
isLeftClick = true;
}
if (isMiddleClick) {
// Teleport player to clicked position, clamp to game area
var r = player.radius + 10;
player.x = Math.max(r, Math.min(2048 - r, x));
player.y = Math.max(r, Math.min(2732 - r, y));
} else if (isLeftClick) {
// If click/touch is on player, start drag
var dx = player.x - x,
dy = player.y - y;
if (dx * dx + dy * dy < player.radius * player.radius + 2000) {
dragNode = player;
} else {
// Otherwise, shoot
player.shoot(x, y);
}
}
handleMove(x, y, obj);
};
// Prevent context menu on right click for the canvas
if (typeof window !== "undefined" && typeof document !== "undefined") {
var canvas = document.querySelector('canvas');
if (canvas) {
canvas.oncontextmenu = function (e) {
e.preventDefault();
return false;
};
}
}
// Handle up (stop drag, clear build preview)
game.up = function (x, y, obj) {
dragNode = null;
// If build preview exists, clear it if not placed
if (buildPreview && buildMode) {
clearBuildPreview();
}
};
// Handle move
game.move = handleMove;
// Game update
game.update = function () {
// --- Virtual Joystick Movement ---
var moveSpeed = 18 + player.weaponLevel * 2;
if (typeof vbtns !== "undefined") {
var dx = 0,
dy = 0;
if (vbtns.up && vbtns.up.isDown) dy -= 1;
if (vbtns.down && vbtns.down.isDown) dy += 1;
if (vbtns.left && vbtns.left.isDown) dx -= 1;
if (vbtns.right && vbtns.right.isDown) dx += 1;
if (dx !== 0 || dy !== 0) {
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
dx /= len;
dy /= len;
// Clamp to game area
var r = player.radius + 10;
player.x = Math.max(r, Math.min(2048 - r, player.x + dx * moveSpeed));
player.y = Math.max(r, Math.min(2732 - r, player.y + dy * moveSpeed));
}
}
}
// Player update
player.update();
// Zombies update
for (var i = zombies.length - 1; i >= 0; i--) {
zombies[i].update();
}
// Bullets update
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
b.update();
// Remove if out of bounds
if (b.x < -100 || b.x > 2148 || b.y < -100 || b.y > 2832) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with zombies
for (var j = zombies.length - 1; j >= 0; j--) {
var z = zombies[j];
var dx = z.x - b.x,
dy = z.y - b.y;
if (dx * dx + dy * dy < (z.radius + b.radius) * (z.radius + b.radius)) {
z.takeDamage(b.damage);
b.destroy();
bullets.splice(i, 1);
break;
}
}
}
// Coins update
for (var i = coins.length - 1; i >= 0; i--) {
coins[i].update();
if (autoCollectEnabled && !coins[i].collected) {
coins[i].collect();
}
}
// Defenses update
for (var i = defenses.length - 1; i >= 0; i--) {
if (defenses[i].update) defenses[i].update();
}
// Wave logic
if (waveInProgress) {
// Spawn zombies
if (zombiesSpawnedThisWave < zombiesToSpawn && LK.ticks % Math.max(20, 60 - currentWave * 2) === 0) {
spawnZombie();
}
// Check wave end
if (zombiesKilledThisWave >= zombiesToSpawn && zombies.length === 0) {
waveInProgress = false;
// Give 5 coins every 5 waves at the end of the wave
if (currentWave % 5 === 0) {
coinsCollected += 5;
updateCoinText();
}
// Next wave after 4 seconds (4000 ms)
LK.setTimeout(function () {
currentWave++;
startWave();
}, 4000);
}
}
};
// --- Game Initialization ---
// Add forest background (drawn as a large green rectangle with tree-like ellipses)
var forestBg = new Container();
var bgRect = LK.getAsset('military_bg', {
width: 2048,
height: 2732,
color: 0x2e4d1a,
// deep forest green
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
forestBg.addChild(bgRect);
// Add some tree ellipses for a forest look
for (var i = 0; i < 18; i++) {
var tree = LK.getAsset('military_bg', {
width: 180 + Math.floor(Math.random() * 80),
height: 320 + Math.floor(Math.random() * 60),
color: 0x3e7d2a + Math.floor(Math.random() * 0x002000),
shape: 'ellipse',
anchorX: 0.5,
anchorY: 1,
x: 120 + Math.random() * 1800,
y: 400 + Math.random() * 2100
});
tree.alpha = 0.22 + Math.random() * 0.18;
forestBg.addChild(tree);
}
game.addChild(forestBg);
// Place player at center
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// --- Virtual Joystick Buttons ---
// Button size and margin
var vbtnSize = 160;
var vbtnMargin = 40;
var vbtnAlpha = 0.45;
var vbtnColor = 0x888888;
var vbtns = {};
// Helper to create a button
function createVBtn(label, x, y, dir) {
var btn = new Container();
// Remove blue color, always use gray for joystick button backgrounds
var bg = LK.getAsset('wall', {
width: vbtnSize,
height: vbtnSize,
color: 0x888888,
anchorX: 0.5,
anchorY: 0.5
});
bg.alpha = vbtnAlpha;
btn.addChild(bg);
var txt = new Text2(label, {
size: 80,
fill: "#fff"
});
txt.anchor.set(0.5, 0.5);
txt.x = 0;
txt.y = 0;
btn.addChild(txt);
btn.x = x;
btn.y = y;
btn.dir = dir;
btn.isDown = false;
btn.down = function () {
btn.isDown = true;
};
btn.up = function () {
btn.isDown = false;
};
LK.gui.bottomLeft.addChild(btn);
return btn;
}
// Positioning for bottom left corner
var baseX = vbtnSize + vbtnMargin;
var baseY = -vbtnSize - vbtnMargin;
// Up
vbtns.up = createVBtn("▲", baseX, baseY - vbtnSize, "up");
// Down
vbtns.down = createVBtn("▼", baseX, baseY + vbtnSize, "down");
// Left
vbtns.left = createVBtn("◀", baseX - vbtnSize, baseY, "left");
// Right
vbtns.right = createVBtn("▶", baseX + vbtnSize, baseY, "right");
// UI: HP Bar
hpBar = new Text2("", {
size: 38,
fill: 0xFF4444
});
// Move hearts to the absolute left (x=0), keep y=20
hpBar.anchor.set(0, 0);
LK.gui.top.addChild(hpBar);
hpBar.x = 0;
hpBar.y = 20;
updateHpBar();
// UI: Coin Text
coinText = new Text2("₺ 0", {
size: 70,
fill: 0xFFE066
});
coinText.anchor.set(1, 0);
LK.gui.topRight.addChild(coinText);
coinText.x = -40;
coinText.y = 20;
// UI: Wave Text
waveText = new Text2("Dalga: 1", {
size: 60,
fill: "#fff"
});
waveText.anchor.set(0, 0);
LK.gui.top.addChild(waveText);
waveText.x = 60;
waveText.y = 120;
// UI: Build Panel
createBuildPanel();
// Start first wave
startWave();
updateCoinText();
updateWaveText();
9mm ammo. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
içinde c yazan sarı renkli bir madeni para. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
clash of clans klan kalesi. In-Game asset. 2d. High contrast. No shadows
the zombie. In-Game asset. 2d. High contrast. No shadows
gray block. In-Game asset. 2d. High contrast. No shadows
trap. In-Game asset. 2d. High contrast. No shadows
arazi pxelart sadece yeşillikle dolu. In-Game asset. 2d. High contrast. No shadows
elinde silahı olan bir asker. In-Game asset. 2d. High contrast. No shadows