/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Best Demon (Ultimate Enemy) var BestDemon = Container.expand(function () { var self = Container.call(this); var bestDemonSprite = self.attachAsset('bestdemon', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bestDemonSprite.width * 0.5; self.hp = 120; self.shootCooldown = 0; self.speed = 2.5; self.groundSmashTimer = 0; self.weapons = []; // Array to hold weapon sprites // Create visual weapons (bestdemongun) for best demon self.updateWeapons = function () { // Remove any existing weapons for (var i = 0; i < self.weapons.length; i++) { self.weapons[i].destroy(); } self.weapons = []; // Create 2 bestdemonguns for best demon (double shot) for (var i = -0.5; i <= 0.5; i++) { var weapon = LK.getAsset('bestdemongun', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Position bestdemonguns around best demon var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.3; var radius = self.radius * 1.2; weapon.x = Math.cos(angle) * radius; weapon.y = Math.sin(angle) * radius; weapon.rotation = angle + Math.PI / 2; self.addChild(weapon); self.weapons.push(weapon); } }; self.update = function () { // Move toward marine if (marine && !self.dead) { var dx = marine.x - self.x; var dy = marine.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 1) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } // Update weapon positions to match firing direction if (self.weapons.length === 0) { self.updateWeapons(); } else { for (var i = 0; i < self.weapons.length; i++) { var weapon = self.weapons[i]; var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + (i - 0.5) * 0.3; var radius = self.radius * 1.2; weapon.x = Math.cos(angle) * radius; weapon.y = Math.sin(angle) * radius; weapon.rotation = angle + Math.PI / 2; } } // Update health bar if (!self.healthBar) { // Create health bar background self.healthBarBg = LK.getAsset('marineBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 5, scaleY: 0.5 }); self.healthBarBg.tint = 0x000000; self.addChild(self.healthBarBg); // Create health bar fill self.healthBar = LK.getAsset('marineBullet', { anchorX: 0, anchorY: 0.5, scaleX: 5, scaleY: 0.45 }); self.healthBar.tint = 0x00ff00; self.addChild(self.healthBar); } // Position health bar above best demon self.healthBarBg.x = 0; self.healthBarBg.y = -self.radius - 30; self.healthBar.x = -self.radius * 2.5; self.healthBar.y = -self.radius - 30; // Update health bar width based on current HP var healthPercent = Math.max(0, self.hp / 120); self.healthBar.scaleX = 5 * healthPercent; // Change color based on health if (healthPercent > 0.6) { self.healthBar.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { self.healthBar.tint = 0xffff00; // Yellow } else { self.healthBar.tint = 0xff0000; // Red } // Shoot bestdemonmermi at marine (double shot) if (self.shootCooldown > 0) self.shootCooldown--;else if (dist < 1500) { for (var i = -0.5; i <= 0.5; i++) { var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.2; var tx = self.x + Math.cos(angle) * 100; var ty = self.y + Math.sin(angle) * 100; spawnBestDemonBullet(self.x, self.y, tx, ty, 12); } self.shootCooldown = 40; } } }; self.hit = function () { self.hp--; tween(bestDemonSprite, { tint: 0xffffff }, { duration: 60, onFinish: function onFinish() { tween(bestDemonSprite, { tint: 0x8e7602 }, { duration: 100 }); } }); if (self.hp <= 0) { self.dead = true; // Add points to score - best demons give triple points var points = doublePointsActive ? 300 : 150; score += points; updateUI(); // Fade out best demon tween(self, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); } }; return self; }); // Best Demon Bullet var BestDemonBullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('bestdemonmermi', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bulletSprite.width * 0.5; self.speed = 16; self.dx = 0; self.dy = 1; self.update = function () { self.x += self.dx * self.speed; self.y += self.dy * self.speed; }; return self; }); // Boss Demon var Boss = Container.expand(function () { var self = Container.call(this); var bossSprite = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bossSprite.width * 0.5; self.hp = 80; self.shootCooldown = 0; self.speed = 3.5; self.groundSmashTimer = 0; // Add this property to track smash cooldown self.weapons = []; // Array to hold weapon sprites // Create visual weapons (tridents) for boss self.updateWeapons = function () { // Remove any existing weapons for (var i = 0; i < self.weapons.length; i++) { self.weapons[i].destroy(); } self.weapons = []; // Create 3 tridents for boss (triple shot) for (var i = -1; i <= 1; i++) { var weapon = LK.getAsset('furca', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 }); // Position tridents around boss var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.18; var radius = self.radius * 1.1; weapon.x = Math.cos(angle) * radius; weapon.y = Math.sin(angle) * radius; weapon.rotation = angle + Math.PI / 2; self.addChild(weapon); self.weapons.push(weapon); } }; self.update = function () { // Move toward marine if (marine && !self.dead) { var dx = marine.x - self.x; var dy = marine.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 1) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } // Update weapon positions to match firing direction if (self.weapons.length === 0) { self.updateWeapons(); } else { for (var i = 0; i < self.weapons.length; i++) { var weapon = self.weapons[i]; var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + (i - 1) * 0.18; var radius = self.radius * 1.1; weapon.x = Math.cos(angle) * radius; weapon.y = Math.sin(angle) * radius; weapon.rotation = angle + Math.PI / 2; } } // Update health bar if (!self.healthBar) { // Create health bar background self.healthBarBg = LK.getAsset('marineBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 0.4 }); self.healthBarBg.tint = 0x000000; self.addChild(self.healthBarBg); // Create health bar fill self.healthBar = LK.getAsset('marineBullet', { anchorX: 0, anchorY: 0.5, scaleX: 4, scaleY: 0.35 }); self.healthBar.tint = 0x00ff00; self.addChild(self.healthBar); } // Position health bar above boss self.healthBarBg.x = 0; self.healthBarBg.y = -self.radius - 25; self.healthBar.x = -self.radius * 2; self.healthBar.y = -self.radius - 25; // Update health bar width based on current HP var healthPercent = Math.max(0, self.hp / 80); self.healthBar.scaleX = 4 * healthPercent; // Change color based on health if (healthPercent > 0.6) { self.healthBar.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { self.healthBar.tint = 0xffff00; // Yellow } else { self.healthBar.tint = 0xff0000; // Red } // Boss ground smash attack every 8 seconds (480 ticks) if (typeof self.groundSmashTimer === "undefined") self.groundSmashTimer = 0; self.groundSmashTimer++; // 3 seconds (180 ticks) before smash, start color warning if (self.groundSmashTimer === 300) { // Flash red for 1.5 seconds, then orange for 1.5 seconds tween(bossSprite, { tint: 0xff0000 }, { duration: 90, onFinish: function onFinish() { tween(bossSprite, { tint: 0xffa500 }, { duration: 90, onFinish: function onFinish() { // Do nothing, color will be reset after smash } }); } }); } if (self.groundSmashTimer >= 480) { self.groundSmashTimer = 0; // Visual effect: flash boss and screen tween(bossSprite, { tint: 0xffffff }, { duration: 80, onFinish: function onFinish() { tween(bossSprite, { tint: 0x8e44ad }, { duration: 120 }); } }); LK.effects.flashScreen(0xccccff, 200); // Damage marine if close to boss (melee range) if (marine && !marine.dead && marine.invuln === 0) { var smashDist = bossSprite.width * 1.1; var mdx = marine.x - self.x; var mdy = marine.y - self.y; if (mdx * mdx + mdy * mdy < smashDist * smashDist) { marine.hp -= 2; if (marine.hp < 0) marine.hp = 0; marine.invuln = 60; marine.flash(); updateUI(); if (marine.hp <= 0) { // Game over gameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } } // Shoot at marine (triple shot) if (self.shootCooldown > 0) self.shootCooldown--;else if (dist < 1200) { for (var i = -1; i <= 1; i++) { var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.18; var tx = self.x + Math.cos(angle) * 100; var ty = self.y + Math.sin(angle) * 100; spawnDemonBullet(self.x, self.y, tx, ty, 10); } self.shootCooldown = 50; } } }; self.hit = function () { self.hp--; tween(bossSprite, { tint: 0xffffff }, { duration: 60, onFinish: function onFinish() { tween(bossSprite, { tint: 0x8e44ad }, { duration: 100 }); } }); if (self.hp <= 0) { self.dead = true; // Spawn a shield at boss position var shield = new Shield(); shield.x = self.x; shield.y = self.y; game.addChild(shield); // Add points to score - bosses give double points when health pack effect is active var points = doublePointsActive ? 100 : 50; score += points; updateUI(); // Create visual effect for shield drop tween(shield, { y: self.y + 20 }, { duration: 60, onFinish: function onFinish() { tween(shield, { y: self.y }, { duration: 40 }); } }); // Fade out boss tween(self, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); } }; return self; }); // Demon (Enemy) var Demon = Container.expand(function () { var self = Container.call(this); var demonSprite = self.attachAsset('demon', { anchorX: 0.5, anchorY: 0.5 }); self.radius = demonSprite.width * 0.5; self.hp = 4; self.shootCooldown = 0; self.speed = 4 + Math.random() * 2; self.weapon = null; // Weapon sprite // Create visual weapon (trident) for demon self.updateWeapon = function () { // Remove existing weapon if any if (self.weapon) { self.weapon.destroy(); } // Create a furca (trident) for demon self.weapon = LK.getAsset('furca', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); // Position trident in front of demon if (marine) { var angle = Math.atan2(marine.y - self.y, marine.x - self.x); var radius = self.radius * 1.1; self.weapon.x = Math.cos(angle) * radius; self.weapon.y = Math.sin(angle) * radius; self.weapon.rotation = angle + Math.PI / 2; } else { self.weapon.x = self.radius; self.weapon.y = 0; } self.addChild(self.weapon); }; self.update = function () { // Move toward marine if (marine && !self.dead) { var dx = marine.x - self.x; var dy = marine.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 1) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } // Create weapon if it doesn't exist if (!self.weapon) { self.updateWeapon(); } // Update weapon position to match firing direction if (self.weapon) { var angle = Math.atan2(marine.y - self.y, marine.x - self.x); var radius = self.radius * 1.1; self.weapon.x = Math.cos(angle) * radius; self.weapon.y = Math.sin(angle) * radius; self.weapon.rotation = angle + Math.PI / 2; } // Update health bar if (!self.healthBar) { // Create health bar background self.healthBarBg = LK.getAsset('marineBullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 0.3 }); self.healthBarBg.tint = 0x000000; self.addChild(self.healthBarBg); // Create health bar fill self.healthBar = LK.getAsset('marineBullet', { anchorX: 0, anchorY: 0.5, scaleX: 2, scaleY: 0.25 }); self.healthBar.tint = 0x00ff00; self.addChild(self.healthBar); } // Position health bar above demon self.healthBarBg.x = 0; self.healthBarBg.y = -self.radius - 15; self.healthBar.x = -self.radius; self.healthBar.y = -self.radius - 15; // Update health bar width based on current HP var healthPercent = Math.max(0, self.hp / 4); self.healthBar.scaleX = 2 * healthPercent; // Change color based on health if (healthPercent > 0.6) { self.healthBar.tint = 0x00ff00; // Green } else if (healthPercent > 0.3) { self.healthBar.tint = 0xffff00; // Yellow } else { self.healthBar.tint = 0xff0000; // Red } // Shoot at marine if (self.shootCooldown > 0) self.shootCooldown--;else if (dist < 900) { spawnDemonBullet(self.x, self.y, marine.x, marine.y); self.shootCooldown = 90 + Math.floor(Math.random() * 30); } } }; self.hit = function () { self.hp--; tween(demonSprite, { tint: 0xffffff }, { duration: 60, onFinish: function onFinish() { tween(demonSprite, { tint: 0xc0392b }, { duration: 100 }); } }); if (self.hp <= 0) { self.dead = true; // If the demon has a weapon, fade it out too if (self.weapon) { tween(self.weapon, { alpha: 0 }, { duration: 200 }); } tween(self, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { self.destroy(); } }); } }; return self; }); // Demon Bullet var DemonBullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('demonBullet', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bulletSprite.width * 0.5; self.speed = 14; self.dx = 0; self.dy = 1; self.update = function () { self.x += self.dx * self.speed; self.y += self.dy * self.speed; }; return self; }); // FireParticle for hell effect var FireParticle = Container.expand(function () { var self = Container.call(this); var particleSprite = self.attachAsset('fireParticle', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1 + Math.random() * 3; self.lifetime = 60 + Math.random() * 120; self.initialAlpha = 0.4 + Math.random() * 0.6; self.alpha = self.initialAlpha; self.scaleValue = 0.3 + Math.random() * 0.7; particleSprite.scale.set(self.scaleValue, self.scaleValue); // Random tint between orange and red var tintValue = Math.random(); if (tintValue < 0.3) { particleSprite.tint = 0xFF4500; // Orange-Red } else if (tintValue < 0.6) { particleSprite.tint = 0xFF6347; // Tomato } else if (tintValue < 0.9) { particleSprite.tint = 0xFF0000; // Red } else { particleSprite.tint = 0xFFD700; // Gold } self.update = function () { self.y -= self.speed; self.lifetime--; // Fade out as lifetime decreases self.alpha = self.initialAlpha * (self.lifetime / 180); if (self.lifetime <= 0) { self.destroy(); } }; return self; }); // Health Pack var HealthPack = Container.expand(function () { var self = Container.call(this); var hpSprite = self.attachAsset('healthPack', { anchorX: 0.5, anchorY: 0.5 }); self.radius = hpSprite.width * 0.5; return self; }); // HellRock for background decoration var HellRock = Container.expand(function () { var self = Container.call(this); var rockSprite = self.attachAsset('hellRock', { anchorX: 0.5, anchorY: 0.5 }); // Randomly adjust rock appearance self.scale.set(0.8 + Math.random() * 0.5, 0.8 + Math.random() * 0.5); rockSprite.rotation = Math.random() * Math.PI * 2; // Random dark colors for rocks var darkColors = [0x3D0C02, 0x2F0A02, 0x3B1001, 0x330000]; rockSprite.tint = darkColors[Math.floor(Math.random() * darkColors.length)]; return self; }); // Marine (Player) var Marine = Container.expand(function () { var self = Container.call(this); var marineSprite = self.attachAsset('marine', { anchorX: 0.5, anchorY: 0.5 }); self.radius = marineSprite.width * 0.5; self.hp = 1; self.invuln = 0; // invulnerability frames self.weapons = []; // Array to hold weapon sprites // Create visual weapons based on bullet count self.updateWeapons = function (bulletCount) { // Remove any existing weapons for (var i = 0; i < self.weapons.length; i++) { self.weapons[i].destroy(); } self.weapons = []; // Create AK47 weapon var weapon = LK.getAsset('ak47', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); // Position weapon at marine's side var radius = self.radius * 1.3; weapon.x = radius; weapon.y = 0; self.addChild(weapon); self.weapons.push(weapon); }; // Define findNearestTarget method at the top of the class self.findNearestTarget = function () { var minDist = 999999; var tx = self.x; var ty = self.y - 200; // Default target ahead of marine // Check demons first if (demons && demons.length > 0) { for (var i = 0; i < demons.length; ++i) { var d = demons[i]; if (!d.dead) { var dx = d.x - self.x; var dy = d.y - self.y; var dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; tx = d.x; ty = d.y; } } } } // Check boss if (boss && !boss.dead) { var dx = boss.x - self.x; var dy = boss.y - self.y; var dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; tx = boss.x; ty = boss.y; } } // Check best demon if (bestDemon && !bestDemon.dead) { var dx = bestDemon.x - self.x; var dy = bestDemon.y - self.y; var dist = dx * dx + dy * dy; if (dist < minDist) { minDist = dist; tx = bestDemon.x; ty = bestDemon.y; } } return { x: tx, y: ty, distance: Math.sqrt(minDist) }; }; self.update = function () { if (self.invuln > 0) self.invuln--; // Update weapon positions to match bullet fire positions if (self.weapons && self.weapons.length > 0) { // Get the target for bullet direction var target = self.findNearestTarget(); var tx = target.x; var ty = target.y; // Calculate direction var dx = tx - self.x; var dy = ty - self.y; var baseAngle = Math.atan2(dy, dx); // Position AK47 in firing position if (self.weapons.length > 0) { var weapon = self.weapons[0]; // Position AK47 pointing at target var angle = baseAngle; // Position weapon at firing direction var radius = self.radius * 1.1; // Slightly outside marine weapon.x = Math.cos(angle) * radius; weapon.y = Math.sin(angle) * radius; // Rotate weapon to face firing direction weapon.rotation = angle + Math.PI / 2; } } }; // Flash when hit self.flash = function () { tween(marineSprite, { tint: 0xffffff }, { duration: 80, onFinish: function onFinish() { tween(marineSprite, { tint: 0x3a9ad9 }, { duration: 120 }); } }); }; return self; }); // Marine Bullet var MarineBullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('healtbar', { anchorX: 0.5, anchorY: 0.5 }); self.radius = bulletSprite.width * 0.5; self.speed = 32; self.dx = 0; self.dy = -1; self.update = function () { self.x += self.dx * self.speed; self.y += self.dy * self.speed; }; return self; }); // Shield (Protection) var Shield = Container.expand(function () { var self = Container.call(this); var shieldSprite = self.attachAsset('powerUp', { anchorX: 0.5, anchorY: 0.5 }); // Use a different tint for the shield shieldSprite.tint = 0x3498db; self.radius = shieldSprite.width * 1.1; self.duration = 600; // 10 seconds self.update = function () { self.duration--; // Flash purple and green when 5 seconds (300 ticks) remaining if (self.duration === 300) { // Start color transition sequence tween(shieldSprite, { tint: 0x9b59b6 // Purple }, { duration: 150, onFinish: function onFinish() { tween(shieldSprite, { tint: 0x2ecc71 // Green }, { duration: 150, onFinish: function onFinish() { // Repeat the color cycle until shield expires var flashInterval = LK.setInterval(function () { tween(shieldSprite, { tint: 0x9b59b6 // Purple }, { duration: 150, onFinish: function onFinish() { tween(shieldSprite, { tint: 0x2ecc71 // Green }, { duration: 150 }); } }); }, 300); // Store interval ID on shield for cleanup self.flashInterval = flashInterval; } }); } }); } if (self.duration <= 0) { // Clear flash interval if it exists if (self.flashInterval) { LK.clearInterval(self.flashInterval); } tween(self, { alpha: 0 }, { duration: 120, onFinish: function onFinish() { self.destroy(); } }); } // Follow the marine if (marine) { self.x = marine.x; self.y = marine.y; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x660000 }); /**** * Game Code ****/ // Hell background elements // Marine (player) // Demon (enemy) // Boss Demon // Marine bullet // Demon bullet // Health pack // Power-up var fireParticles = []; var hellRocks = []; var particleSpawnTimer = 0; // Create lava ground background var lavaGround = LK.getAsset('lavaGround', { anchorX: 0, anchorY: 0 }); game.addChild(lavaGround); // Generate initial rocks for hell landscape function generateHellLandscape() { // Create rocks scattered around the arena for (var i = 0; i < 15; i++) { var rock = new HellRock(); rock.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT); rock.y = ARENA_TOP + Math.random() * (ARENA_BOTTOM - ARENA_TOP); hellRocks.push(rock); game.addChild(rock); } } // Arena bounds var ARENA_MARGIN = 80; var ARENA_LEFT = ARENA_MARGIN; var ARENA_TOP = 180; var ARENA_RIGHT = 2048 - ARENA_MARGIN; var ARENA_BOTTOM = 2732 - ARENA_MARGIN; // Game state var marine = null; var demons = []; var boss = null; var bestDemon = null; var marineBullets = []; var demonBullets = []; var bestDemonBullets = []; var healthPacks = []; var powerUps = []; var shields = []; var wave = 1; var waveTimer = 0; var spawnTimer = 0; var bossActive = false; var dragging = false; var dragOffsetX = 0; var dragOffsetY = 0; var lastMoveX = 0; var lastMoveY = 0; var score = 0; var marineFireTimer = 0; var marineFireRate = 12; // ticks between shots var marinePowerTimer = 0; var marineMaxHP = 1; var gameOver = false; var doublePointsActive = false; var doublePointsTimer = 0; var doublePointsDuration = 300; // 5 seconds at 60 FPS var doublePointsIndicator = null; // Visual indicator for double points // UI var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var hpTxt = new Text2('♥♥♥', { size: 90, fill: 0xE74C3C }); hpTxt.anchor.set(0.5, 0); LK.gui.top.addChild(hpTxt); hpTxt.y = 120; hpTxt.x = 2048 / 2; // Timer display in top right corner var gameStartTime = 0; var timerTxt = new Text2('00:00', { size: 80, fill: 0xFFFFFF }); timerTxt.anchor.set(1, 0); // Right align LK.gui.topRight.addChild(timerTxt); timerTxt.x = -20; // Small margin from right edge timerTxt.y = 20; // Small margin from top // Center arena function centerArenaX() { return (ARENA_LEFT + ARENA_RIGHT) / 2; } function centerArenaY() { return (ARENA_TOP + ARENA_BOTTOM) / 2; } // Spawn marine function spawnMarine() { marine = new Marine(); marine.x = centerArenaX(); marine.y = ARENA_BOTTOM - 220; game.addChild(marine); } // Spawn demon function spawnDemon() { var d = new Demon(); // Spawn at random edge var edge = Math.floor(Math.random() * 4); if (edge === 0) { // top d.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT); d.y = ARENA_TOP + 40; } else if (edge === 1) { // bottom d.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT); d.y = ARENA_BOTTOM - 40; } else if (edge === 2) { // left d.x = ARENA_LEFT + 40; d.y = ARENA_TOP + Math.random() * (ARENA_BOTTOM - ARENA_TOP); } else { // right d.x = ARENA_RIGHT - 40; d.y = ARENA_TOP + Math.random() * (ARENA_BOTTOM - ARENA_TOP); } demons.push(d); game.addChild(d); // Initialize weapon for the demon d.updateWeapon(); } // Spawn boss function spawnBoss() { boss = new Boss(); boss.x = centerArenaX(); boss.y = ARENA_TOP + 200; game.addChild(boss); bossActive = true; // Initialize weapons for the boss boss.updateWeapons(); } // Spawn best demon function spawnBestDemon() { bestDemon = new BestDemon(); bestDemon.x = centerArenaX(); bestDemon.y = ARENA_TOP + 150; game.addChild(bestDemon); // Initialize weapons for the best demon bestDemon.updateWeapons(); } // Spawn marine bullet function spawnMarineBullet(x, y, dx, dy) { var b = new MarineBullet(); b.x = x; b.y = y; var mag = Math.sqrt(dx * dx + dy * dy); b.dx = dx / mag; b.dy = dy / mag; marineBullets.push(b); game.addChild(b); } // Spawn demon bullet function spawnDemonBullet(x, y, tx, ty, speed) { var b = new DemonBullet(); b.x = x; b.y = y; var dx = tx - x; var dy = ty - y; var mag = Math.sqrt(dx * dx + dy * dy); b.dx = dx / mag; b.dy = dy / mag; if (speed) b.speed = speed; demonBullets.push(b); game.addChild(b); } // Spawn best demon bullet function spawnBestDemonBullet(x, y, tx, ty, speed) { var b = new BestDemonBullet(); b.x = x; b.y = y; var dx = tx - x; var dy = ty - y; var mag = Math.sqrt(dx * dx + dy * dy); b.dx = dx / mag; b.dy = dy / mag; if (speed) b.speed = speed; bestDemonBullets.push(b); game.addChild(b); } // Spawn health pack function spawnHealthPack() { var hp = new HealthPack(); hp.x = ARENA_LEFT + 100 + Math.random() * (ARENA_RIGHT - ARENA_LEFT - 200); hp.y = ARENA_TOP + 100 + Math.random() * (ARENA_BOTTOM - ARENA_TOP - 200); healthPacks.push(hp); game.addChild(hp); } // Power-ups have been removed function spawnPowerUp() { // Function kept for compatibility but does nothing } // Update UI function updateUI() { scoreTxt.setText(score); var hpStr = ''; for (var i = 0; i < marineMaxHP; ++i) { hpStr += i < marine.hp ? '♥' : '♡'; } hpTxt.setText(hpStr); } // Start game function startGame() { score = 0; wave = 1; waveTimer = 0; spawnTimer = 0; bossActive = false; marineFireTimer = 0; gameOver = false; doublePointsActive = false; doublePointsTimer = 0; // Clean up double points indicator if (doublePointsIndicator) { doublePointsIndicator.destroy(); doublePointsIndicator = null; } gameStartTime = LK.ticks; // Record game start time // Remove all for (var i = 0; i < demons.length; ++i) demons[i].destroy(); for (var i = 0; i < marineBullets.length; ++i) marineBullets[i].destroy(); for (var i = 0; i < demonBullets.length; ++i) demonBullets[i].destroy(); for (var i = 0; i < bestDemonBullets.length; ++i) bestDemonBullets[i].destroy(); for (var i = 0; i < healthPacks.length; ++i) healthPacks[i].destroy(); // Remove fire particles and rocks for (var i = 0; i < fireParticles.length; ++i) fireParticles[i].destroy(); for (var i = 0; i < hellRocks.length; ++i) hellRocks[i].destroy(); fireParticles = []; hellRocks = []; // Remove any active shields for (var i = 0; i < game.children.length; i++) { if (game.children[i] instanceof Shield) { game.children[i].destroy(); } } if (marine) marine.destroy(); if (boss) boss.destroy(); if (bestDemon) bestDemon.destroy(); demons = []; marineBullets = []; demonBullets = []; bestDemonBullets = []; healthPacks = []; shields = []; boss = null; bestDemon = null; spawnMarine(); // Initialize marine with 1 weapon if (marine) { marine.updateWeapons(1); } updateUI(); } // Generate initial hell landscape generateHellLandscape(); startGame(); // Touch controls game.down = function (x, y, obj) { if (gameOver) return; // Only drag if touch is inside marine var dx = x - marine.x; var dy = y - marine.y; if (dx * dx + dy * dy < marine.radius * marine.radius * 1.2) { dragging = true; dragOffsetX = marine.x - x; dragOffsetY = marine.y - y; lastMoveX = x; lastMoveY = y; } }; game.up = function (x, y, obj) { dragging = false; }; game.move = function (x, y, obj) { if (gameOver) return; // Always follow finger/mouse, clamp to arena if (marine) { var nx = x; var ny = y; nx = Math.max(ARENA_LEFT + marine.radius, Math.min(ARENA_RIGHT - marine.radius, nx)); ny = Math.max(ARENA_TOP + marine.radius, Math.min(ARENA_BOTTOM - marine.radius, ny)); marine.x = nx; marine.y = ny; lastMoveX = x; lastMoveY = y; } }; // Main update loop game.update = function () { if (gameOver) return; // Update timer display var elapsedTicks = LK.ticks - gameStartTime; var elapsedSeconds = Math.floor(elapsedTicks / 60); var minutes = Math.floor(elapsedSeconds / 60); var seconds = elapsedSeconds % 60; var timeStr = (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds; timerTxt.setText(timeStr); // Update fire particles (hellish effect) particleSpawnTimer--; if (particleSpawnTimer <= 0) { // Spawn new fire particles for (var i = 0; i < 3; i++) { var particle = new FireParticle(); particle.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT); particle.y = ARENA_BOTTOM - Math.random() * 100; fireParticles.push(particle); game.addChild(particle); } particleSpawnTimer = 2 + Math.floor(Math.random() * 3); } // Update existing fire particles for (var i = fireParticles.length - 1; i >= 0; i--) { fireParticles[i].update(); if (fireParticles[i].lifetime <= 0) { fireParticles.splice(i, 1); } } // Update marine if (marine) marine.update(); // Update double points timer if (doublePointsActive) { // Create indicator if it doesn't exist if (!doublePointsIndicator) { doublePointsIndicator = LK.getAsset('2xpo', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); game.addChild(doublePointsIndicator); } // Position indicator next to marine if (marine && doublePointsIndicator) { doublePointsIndicator.x = marine.x + marine.radius * 1.5; doublePointsIndicator.y = marine.y - marine.radius * 0.5; } doublePointsTimer--; if (doublePointsTimer <= 0) { doublePointsActive = false; doublePointsTimer = 0; // Reset timer to ensure clean state // Remove indicator when effect ends if (doublePointsIndicator) { doublePointsIndicator.destroy(); doublePointsIndicator = null; } } } else { // Ensure indicator is removed when double points is not active if (doublePointsIndicator) { doublePointsIndicator.destroy(); doublePointsIndicator = null; } } // Update demons for (var i = demons.length - 1; i >= 0; --i) { var d = demons[i]; d.update(); if (d.dead) { demons.splice(i, 1); var points = doublePointsActive ? 20 : 10; // Double points if active score += points; updateUI(); // Chance to drop health if (Math.random() < 0.08) spawnHealthPack(); } } // Update boss if (boss) { boss.update(); if (boss.dead) { bossActive = false; boss = null; // Score is now added in the boss.hit method wave++; waveTimer = 0; } // Marine takes damage when touching boss if (marine && !marine.dead && marine.invuln === 0 && !boss.dead) { var dx = marine.x - boss.x; var dy = marine.y - boss.y; var r = marine.radius + boss.radius; if (dx * dx + dy * dy < r * r) { marine.hp--; if (marine.hp < 0) marine.hp = 0; marine.invuln = 60; marine.flash(); updateUI(); if (marine.hp <= 0) { gameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } } // Update best demon if (bestDemon) { bestDemon.update(); if (bestDemon.dead) { bestDemon = null; wave++; waveTimer = 0; } // Marine takes damage when touching best demon if (marine && !marine.dead && marine.invuln === 0 && !bestDemon.dead) { var dx = marine.x - bestDemon.x; var dy = marine.y - bestDemon.y; var r = marine.radius + bestDemon.radius; if (dx * dx + dy * dy < r * r) { marine.hp--; if (marine.hp < 0) marine.hp = 0; marine.invuln = 60; marine.flash(); updateUI(); if (marine.hp <= 0) { gameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } } // Spawn best demon when score reaches 500 or more if (!bestDemon && score >= 500) { spawnBestDemon(); } // Update marine bullets for (var i = marineBullets.length - 1; i >= 0; --i) { var b = marineBullets[i]; b.update(); // Remove if out of bounds if (b.x < ARENA_LEFT - 60 || b.x > ARENA_RIGHT + 60 || b.y < ARENA_TOP - 60 || b.y > ARENA_BOTTOM + 60) { b.destroy(); marineBullets.splice(i, 1); continue; } // Hit demons var hit = false; for (var j = demons.length - 1; j >= 0; --j) { var d = demons[j]; var dx = b.x - d.x; var dy = b.y - d.y; var r = b.radius + d.radius; if (dx * dx + dy * dy < r * r) { d.hit(); hit = true; break; } } // Hit boss if (!hit && boss) { var dx = b.x - boss.x; var dy = b.y - boss.y; var r = b.radius + boss.radius; if (dx * dx + dy * dy < r * r) { boss.hit(); hit = true; } } // Hit best demon if (!hit && bestDemon) { var dx = b.x - bestDemon.x; var dy = b.y - bestDemon.y; var r = b.radius + bestDemon.radius; if (dx * dx + dy * dy < r * r) { bestDemon.hit(); hit = true; } } if (hit) { b.destroy(); marineBullets.splice(i, 1); } } // Update demon bullets for (var i = demonBullets.length - 1; i >= 0; --i) { var b = demonBullets[i]; b.update(); // Remove if out of bounds if (b.x < ARENA_LEFT - 60 || b.x > ARENA_RIGHT + 60 || b.y < ARENA_TOP - 60 || b.y > ARENA_BOTTOM + 60) { b.destroy(); demonBullets.splice(i, 1); continue; } // Hit marine if (marine && marine.invuln === 0) { var dx = b.x - marine.x; var dy = b.y - marine.y; var r = b.radius + marine.radius; if (dx * dx + dy * dy < r * r) { b.destroy(); demonBullets.splice(i, 1); // Check if a shield is protecting the marine var hasShield = false; for (var j = 0; j < game.children.length; j++) { if (game.children[j] instanceof Shield) { hasShield = true; // Destroy shield immediately when hit if (game.children[j].flashInterval) { LK.clearInterval(game.children[j].flashInterval); } // Flash shield effect and destroy it tween(game.children[j], { alpha: 0 }, { duration: 120, onFinish: function onFinish(obj) { obj.destroy(); } }); break; } } if (!hasShield) { marine.hp--; marine.invuln = 60; marine.flash(); updateUI(); if (marine.hp <= 0) { // Game over gameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } } } // Update best demon bullets for (var i = bestDemonBullets.length - 1; i >= 0; --i) { var b = bestDemonBullets[i]; b.update(); // Remove if out of bounds if (b.x < ARENA_LEFT - 60 || b.x > ARENA_RIGHT + 60 || b.y < ARENA_TOP - 60 || b.y > ARENA_BOTTOM + 60) { b.destroy(); bestDemonBullets.splice(i, 1); continue; } // Hit marine if (marine && marine.invuln === 0) { var dx = b.x - marine.x; var dy = b.y - marine.y; var r = b.radius + marine.radius; if (dx * dx + dy * dy < r * r) { b.destroy(); bestDemonBullets.splice(i, 1); // Check if a shield is protecting the marine var hasShield = false; for (var j = 0; j < game.children.length; j++) { if (game.children[j] instanceof Shield) { hasShield = true; // Destroy shield immediately when hit if (game.children[j].flashInterval) { LK.clearInterval(game.children[j].flashInterval); } // Flash shield effect and destroy it tween(game.children[j], { alpha: 0 }, { duration: 120, onFinish: function onFinish(obj) { obj.destroy(); } }); break; } } if (!hasShield) { marine.hp--; marine.invuln = 60; marine.flash(); updateUI(); if (marine.hp <= 0) { // Game over gameOver = true; LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } } } // Update health packs for (var i = healthPacks.length - 1; i >= 0; --i) { var hp = healthPacks[i]; if (marine) { var dx = hp.x - marine.x; var dy = hp.y - marine.y; var r = hp.radius + marine.radius; if (dx * dx + dy * dy < r * r) { if (marine.hp < marineMaxHP) { marine.hp++; } // Activate double points for 10 seconds doublePointsActive = true; doublePointsTimer = doublePointsDuration; updateUI(); hp.destroy(); healthPacks.splice(i, 1); } } } // Power-ups have been removed // Marine auto-fire if (marine) { marineFireTimer--; if (marineFireTimer <= 0) { marineFireTimer = marineFireRate; // Auto-targeting: find nearest enemy var target = marine.findNearestTarget(); var tx = target.x; var ty = target.y; // Fire // Calculate bullets based on score - 1 bullet base + 1 extra per 500 points var totalBullets = 1 + Math.floor(score / 500); // Make sure marine has an AK47 if (marine.weapons.length === 0) { marine.updateWeapons(1); } var dx = tx - marine.x; var dy = ty - marine.y; var baseAngle = Math.atan2(dy, dx); for (var n = 0; n < totalBullets; ++n) { // Spread out extra bullets a bit var offset = (n - (totalBullets - 1) / 2) * 0.12; var angle = baseAngle + offset; var ddx = Math.cos(angle); var ddy = Math.sin(angle); spawnMarineBullet(marine.x, marine.y, ddx, ddy, false); } } } // Wave logic if (!bossActive) { // Spawn demons if (demons.length < Math.min(3 + wave, 8)) { spawnTimer--; if (spawnTimer <= 0) { spawnDemon(); spawnTimer = 120 + Math.floor(Math.random() * 60); // Increased timer for slower demon spawning } } // Next wave if (demons.length === 0 && !boss && waveTimer > 60) { if (score >= 100 && score < 490 && Math.floor(score / 100) > Math.floor((score - (doublePointsActive ? 20 : 10)) / 100)) { spawnBoss(); } else { wave++; waveTimer = 0; } } waveTimer++; } else { // Wait for boss defeat if (boss && boss.dead) { bossActive = false; boss = null; wave++; waveTimer = 0; } } // Randomly spawn health packs if (LK.ticks % 420 === 0 && Math.random() < 0.5) { spawnHealthPack(); } // Pulsate lava ground for hellish effect if (LK.ticks % 120 === 0) { var targetTint = Math.random() < 0.5 ? 0x990000 : 0x660000; tween(lavaGround, { tint: targetTint }, { duration: 1000, easing: tween.easeInOut }); } }; // Reset game on game over LK.on('gameover', function () { startGame(); });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Best Demon (Ultimate Enemy)
var BestDemon = Container.expand(function () {
var self = Container.call(this);
var bestDemonSprite = self.attachAsset('bestdemon', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bestDemonSprite.width * 0.5;
self.hp = 120;
self.shootCooldown = 0;
self.speed = 2.5;
self.groundSmashTimer = 0;
self.weapons = []; // Array to hold weapon sprites
// Create visual weapons (bestdemongun) for best demon
self.updateWeapons = function () {
// Remove any existing weapons
for (var i = 0; i < self.weapons.length; i++) {
self.weapons[i].destroy();
}
self.weapons = [];
// Create 2 bestdemonguns for best demon (double shot)
for (var i = -0.5; i <= 0.5; i++) {
var weapon = LK.getAsset('bestdemongun', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Position bestdemonguns around best demon
var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.3;
var radius = self.radius * 1.2;
weapon.x = Math.cos(angle) * radius;
weapon.y = Math.sin(angle) * radius;
weapon.rotation = angle + Math.PI / 2;
self.addChild(weapon);
self.weapons.push(weapon);
}
};
self.update = function () {
// Move toward marine
if (marine && !self.dead) {
var dx = marine.x - self.x;
var dy = marine.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 1) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
// Update weapon positions to match firing direction
if (self.weapons.length === 0) {
self.updateWeapons();
} else {
for (var i = 0; i < self.weapons.length; i++) {
var weapon = self.weapons[i];
var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + (i - 0.5) * 0.3;
var radius = self.radius * 1.2;
weapon.x = Math.cos(angle) * radius;
weapon.y = Math.sin(angle) * radius;
weapon.rotation = angle + Math.PI / 2;
}
}
// Update health bar
if (!self.healthBar) {
// Create health bar background
self.healthBarBg = LK.getAsset('marineBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 5,
scaleY: 0.5
});
self.healthBarBg.tint = 0x000000;
self.addChild(self.healthBarBg);
// Create health bar fill
self.healthBar = LK.getAsset('marineBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 5,
scaleY: 0.45
});
self.healthBar.tint = 0x00ff00;
self.addChild(self.healthBar);
}
// Position health bar above best demon
self.healthBarBg.x = 0;
self.healthBarBg.y = -self.radius - 30;
self.healthBar.x = -self.radius * 2.5;
self.healthBar.y = -self.radius - 30;
// Update health bar width based on current HP
var healthPercent = Math.max(0, self.hp / 120);
self.healthBar.scaleX = 5 * healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xffff00; // Yellow
} else {
self.healthBar.tint = 0xff0000; // Red
}
// Shoot bestdemonmermi at marine (double shot)
if (self.shootCooldown > 0) self.shootCooldown--;else if (dist < 1500) {
for (var i = -0.5; i <= 0.5; i++) {
var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.2;
var tx = self.x + Math.cos(angle) * 100;
var ty = self.y + Math.sin(angle) * 100;
spawnBestDemonBullet(self.x, self.y, tx, ty, 12);
}
self.shootCooldown = 40;
}
}
};
self.hit = function () {
self.hp--;
tween(bestDemonSprite, {
tint: 0xffffff
}, {
duration: 60,
onFinish: function onFinish() {
tween(bestDemonSprite, {
tint: 0x8e7602
}, {
duration: 100
});
}
});
if (self.hp <= 0) {
self.dead = true;
// Add points to score - best demons give triple points
var points = doublePointsActive ? 300 : 150;
score += points;
updateUI();
// Fade out best demon
tween(self, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
return self;
});
// Best Demon Bullet
var BestDemonBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('bestdemonmermi', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletSprite.width * 0.5;
self.speed = 16;
self.dx = 0;
self.dy = 1;
self.update = function () {
self.x += self.dx * self.speed;
self.y += self.dy * self.speed;
};
return self;
});
// Boss Demon
var Boss = Container.expand(function () {
var self = Container.call(this);
var bossSprite = self.attachAsset('boss', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bossSprite.width * 0.5;
self.hp = 80;
self.shootCooldown = 0;
self.speed = 3.5;
self.groundSmashTimer = 0; // Add this property to track smash cooldown
self.weapons = []; // Array to hold weapon sprites
// Create visual weapons (tridents) for boss
self.updateWeapons = function () {
// Remove any existing weapons
for (var i = 0; i < self.weapons.length; i++) {
self.weapons[i].destroy();
}
self.weapons = [];
// Create 3 tridents for boss (triple shot)
for (var i = -1; i <= 1; i++) {
var weapon = LK.getAsset('furca', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
});
// Position tridents around boss
var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.18;
var radius = self.radius * 1.1;
weapon.x = Math.cos(angle) * radius;
weapon.y = Math.sin(angle) * radius;
weapon.rotation = angle + Math.PI / 2;
self.addChild(weapon);
self.weapons.push(weapon);
}
};
self.update = function () {
// Move toward marine
if (marine && !self.dead) {
var dx = marine.x - self.x;
var dy = marine.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 1) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
// Update weapon positions to match firing direction
if (self.weapons.length === 0) {
self.updateWeapons();
} else {
for (var i = 0; i < self.weapons.length; i++) {
var weapon = self.weapons[i];
var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + (i - 1) * 0.18;
var radius = self.radius * 1.1;
weapon.x = Math.cos(angle) * radius;
weapon.y = Math.sin(angle) * radius;
weapon.rotation = angle + Math.PI / 2;
}
}
// Update health bar
if (!self.healthBar) {
// Create health bar background
self.healthBarBg = LK.getAsset('marineBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.4
});
self.healthBarBg.tint = 0x000000;
self.addChild(self.healthBarBg);
// Create health bar fill
self.healthBar = LK.getAsset('marineBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 4,
scaleY: 0.35
});
self.healthBar.tint = 0x00ff00;
self.addChild(self.healthBar);
}
// Position health bar above boss
self.healthBarBg.x = 0;
self.healthBarBg.y = -self.radius - 25;
self.healthBar.x = -self.radius * 2;
self.healthBar.y = -self.radius - 25;
// Update health bar width based on current HP
var healthPercent = Math.max(0, self.hp / 80);
self.healthBar.scaleX = 4 * healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xffff00; // Yellow
} else {
self.healthBar.tint = 0xff0000; // Red
}
// Boss ground smash attack every 8 seconds (480 ticks)
if (typeof self.groundSmashTimer === "undefined") self.groundSmashTimer = 0;
self.groundSmashTimer++;
// 3 seconds (180 ticks) before smash, start color warning
if (self.groundSmashTimer === 300) {
// Flash red for 1.5 seconds, then orange for 1.5 seconds
tween(bossSprite, {
tint: 0xff0000
}, {
duration: 90,
onFinish: function onFinish() {
tween(bossSprite, {
tint: 0xffa500
}, {
duration: 90,
onFinish: function onFinish() {
// Do nothing, color will be reset after smash
}
});
}
});
}
if (self.groundSmashTimer >= 480) {
self.groundSmashTimer = 0;
// Visual effect: flash boss and screen
tween(bossSprite, {
tint: 0xffffff
}, {
duration: 80,
onFinish: function onFinish() {
tween(bossSprite, {
tint: 0x8e44ad
}, {
duration: 120
});
}
});
LK.effects.flashScreen(0xccccff, 200);
// Damage marine if close to boss (melee range)
if (marine && !marine.dead && marine.invuln === 0) {
var smashDist = bossSprite.width * 1.1;
var mdx = marine.x - self.x;
var mdy = marine.y - self.y;
if (mdx * mdx + mdy * mdy < smashDist * smashDist) {
marine.hp -= 2;
if (marine.hp < 0) marine.hp = 0;
marine.invuln = 60;
marine.flash();
updateUI();
if (marine.hp <= 0) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
}
// Shoot at marine (triple shot)
if (self.shootCooldown > 0) self.shootCooldown--;else if (dist < 1200) {
for (var i = -1; i <= 1; i++) {
var angle = Math.atan2(marine.y - self.y, marine.x - self.x) + i * 0.18;
var tx = self.x + Math.cos(angle) * 100;
var ty = self.y + Math.sin(angle) * 100;
spawnDemonBullet(self.x, self.y, tx, ty, 10);
}
self.shootCooldown = 50;
}
}
};
self.hit = function () {
self.hp--;
tween(bossSprite, {
tint: 0xffffff
}, {
duration: 60,
onFinish: function onFinish() {
tween(bossSprite, {
tint: 0x8e44ad
}, {
duration: 100
});
}
});
if (self.hp <= 0) {
self.dead = true;
// Spawn a shield at boss position
var shield = new Shield();
shield.x = self.x;
shield.y = self.y;
game.addChild(shield);
// Add points to score - bosses give double points when health pack effect is active
var points = doublePointsActive ? 100 : 50;
score += points;
updateUI();
// Create visual effect for shield drop
tween(shield, {
y: self.y + 20
}, {
duration: 60,
onFinish: function onFinish() {
tween(shield, {
y: self.y
}, {
duration: 40
});
}
});
// Fade out boss
tween(self, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
return self;
});
// Demon (Enemy)
var Demon = Container.expand(function () {
var self = Container.call(this);
var demonSprite = self.attachAsset('demon', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = demonSprite.width * 0.5;
self.hp = 4;
self.shootCooldown = 0;
self.speed = 4 + Math.random() * 2;
self.weapon = null; // Weapon sprite
// Create visual weapon (trident) for demon
self.updateWeapon = function () {
// Remove existing weapon if any
if (self.weapon) {
self.weapon.destroy();
}
// Create a furca (trident) for demon
self.weapon = LK.getAsset('furca', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Position trident in front of demon
if (marine) {
var angle = Math.atan2(marine.y - self.y, marine.x - self.x);
var radius = self.radius * 1.1;
self.weapon.x = Math.cos(angle) * radius;
self.weapon.y = Math.sin(angle) * radius;
self.weapon.rotation = angle + Math.PI / 2;
} else {
self.weapon.x = self.radius;
self.weapon.y = 0;
}
self.addChild(self.weapon);
};
self.update = function () {
// Move toward marine
if (marine && !self.dead) {
var dx = marine.x - self.x;
var dy = marine.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 1) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
// Create weapon if it doesn't exist
if (!self.weapon) {
self.updateWeapon();
}
// Update weapon position to match firing direction
if (self.weapon) {
var angle = Math.atan2(marine.y - self.y, marine.x - self.x);
var radius = self.radius * 1.1;
self.weapon.x = Math.cos(angle) * radius;
self.weapon.y = Math.sin(angle) * radius;
self.weapon.rotation = angle + Math.PI / 2;
}
// Update health bar
if (!self.healthBar) {
// Create health bar background
self.healthBarBg = LK.getAsset('marineBullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.3
});
self.healthBarBg.tint = 0x000000;
self.addChild(self.healthBarBg);
// Create health bar fill
self.healthBar = LK.getAsset('marineBullet', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2,
scaleY: 0.25
});
self.healthBar.tint = 0x00ff00;
self.addChild(self.healthBar);
}
// Position health bar above demon
self.healthBarBg.x = 0;
self.healthBarBg.y = -self.radius - 15;
self.healthBar.x = -self.radius;
self.healthBar.y = -self.radius - 15;
// Update health bar width based on current HP
var healthPercent = Math.max(0, self.hp / 4);
self.healthBar.scaleX = 2 * healthPercent;
// Change color based on health
if (healthPercent > 0.6) {
self.healthBar.tint = 0x00ff00; // Green
} else if (healthPercent > 0.3) {
self.healthBar.tint = 0xffff00; // Yellow
} else {
self.healthBar.tint = 0xff0000; // Red
}
// Shoot at marine
if (self.shootCooldown > 0) self.shootCooldown--;else if (dist < 900) {
spawnDemonBullet(self.x, self.y, marine.x, marine.y);
self.shootCooldown = 90 + Math.floor(Math.random() * 30);
}
}
};
self.hit = function () {
self.hp--;
tween(demonSprite, {
tint: 0xffffff
}, {
duration: 60,
onFinish: function onFinish() {
tween(demonSprite, {
tint: 0xc0392b
}, {
duration: 100
});
}
});
if (self.hp <= 0) {
self.dead = true;
// If the demon has a weapon, fade it out too
if (self.weapon) {
tween(self.weapon, {
alpha: 0
}, {
duration: 200
});
}
tween(self, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.destroy();
}
});
}
};
return self;
});
// Demon Bullet
var DemonBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('demonBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletSprite.width * 0.5;
self.speed = 14;
self.dx = 0;
self.dy = 1;
self.update = function () {
self.x += self.dx * self.speed;
self.y += self.dy * self.speed;
};
return self;
});
// FireParticle for hell effect
var FireParticle = Container.expand(function () {
var self = Container.call(this);
var particleSprite = self.attachAsset('fireParticle', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 3;
self.lifetime = 60 + Math.random() * 120;
self.initialAlpha = 0.4 + Math.random() * 0.6;
self.alpha = self.initialAlpha;
self.scaleValue = 0.3 + Math.random() * 0.7;
particleSprite.scale.set(self.scaleValue, self.scaleValue);
// Random tint between orange and red
var tintValue = Math.random();
if (tintValue < 0.3) {
particleSprite.tint = 0xFF4500; // Orange-Red
} else if (tintValue < 0.6) {
particleSprite.tint = 0xFF6347; // Tomato
} else if (tintValue < 0.9) {
particleSprite.tint = 0xFF0000; // Red
} else {
particleSprite.tint = 0xFFD700; // Gold
}
self.update = function () {
self.y -= self.speed;
self.lifetime--;
// Fade out as lifetime decreases
self.alpha = self.initialAlpha * (self.lifetime / 180);
if (self.lifetime <= 0) {
self.destroy();
}
};
return self;
});
// Health Pack
var HealthPack = Container.expand(function () {
var self = Container.call(this);
var hpSprite = self.attachAsset('healthPack', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = hpSprite.width * 0.5;
return self;
});
// HellRock for background decoration
var HellRock = Container.expand(function () {
var self = Container.call(this);
var rockSprite = self.attachAsset('hellRock', {
anchorX: 0.5,
anchorY: 0.5
});
// Randomly adjust rock appearance
self.scale.set(0.8 + Math.random() * 0.5, 0.8 + Math.random() * 0.5);
rockSprite.rotation = Math.random() * Math.PI * 2;
// Random dark colors for rocks
var darkColors = [0x3D0C02, 0x2F0A02, 0x3B1001, 0x330000];
rockSprite.tint = darkColors[Math.floor(Math.random() * darkColors.length)];
return self;
});
// Marine (Player)
var Marine = Container.expand(function () {
var self = Container.call(this);
var marineSprite = self.attachAsset('marine', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = marineSprite.width * 0.5;
self.hp = 1;
self.invuln = 0; // invulnerability frames
self.weapons = []; // Array to hold weapon sprites
// Create visual weapons based on bullet count
self.updateWeapons = function (bulletCount) {
// Remove any existing weapons
for (var i = 0; i < self.weapons.length; i++) {
self.weapons[i].destroy();
}
self.weapons = [];
// Create AK47 weapon
var weapon = LK.getAsset('ak47', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Position weapon at marine's side
var radius = self.radius * 1.3;
weapon.x = radius;
weapon.y = 0;
self.addChild(weapon);
self.weapons.push(weapon);
};
// Define findNearestTarget method at the top of the class
self.findNearestTarget = function () {
var minDist = 999999;
var tx = self.x;
var ty = self.y - 200; // Default target ahead of marine
// Check demons first
if (demons && demons.length > 0) {
for (var i = 0; i < demons.length; ++i) {
var d = demons[i];
if (!d.dead) {
var dx = d.x - self.x;
var dy = d.y - self.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
tx = d.x;
ty = d.y;
}
}
}
}
// Check boss
if (boss && !boss.dead) {
var dx = boss.x - self.x;
var dy = boss.y - self.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
tx = boss.x;
ty = boss.y;
}
}
// Check best demon
if (bestDemon && !bestDemon.dead) {
var dx = bestDemon.x - self.x;
var dy = bestDemon.y - self.y;
var dist = dx * dx + dy * dy;
if (dist < minDist) {
minDist = dist;
tx = bestDemon.x;
ty = bestDemon.y;
}
}
return {
x: tx,
y: ty,
distance: Math.sqrt(minDist)
};
};
self.update = function () {
if (self.invuln > 0) self.invuln--;
// Update weapon positions to match bullet fire positions
if (self.weapons && self.weapons.length > 0) {
// Get the target for bullet direction
var target = self.findNearestTarget();
var tx = target.x;
var ty = target.y;
// Calculate direction
var dx = tx - self.x;
var dy = ty - self.y;
var baseAngle = Math.atan2(dy, dx);
// Position AK47 in firing position
if (self.weapons.length > 0) {
var weapon = self.weapons[0];
// Position AK47 pointing at target
var angle = baseAngle;
// Position weapon at firing direction
var radius = self.radius * 1.1; // Slightly outside marine
weapon.x = Math.cos(angle) * radius;
weapon.y = Math.sin(angle) * radius;
// Rotate weapon to face firing direction
weapon.rotation = angle + Math.PI / 2;
}
}
};
// Flash when hit
self.flash = function () {
tween(marineSprite, {
tint: 0xffffff
}, {
duration: 80,
onFinish: function onFinish() {
tween(marineSprite, {
tint: 0x3a9ad9
}, {
duration: 120
});
}
});
};
return self;
});
// Marine Bullet
var MarineBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('healtbar', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = bulletSprite.width * 0.5;
self.speed = 32;
self.dx = 0;
self.dy = -1;
self.update = function () {
self.x += self.dx * self.speed;
self.y += self.dy * self.speed;
};
return self;
});
// Shield (Protection)
var Shield = Container.expand(function () {
var self = Container.call(this);
var shieldSprite = self.attachAsset('powerUp', {
anchorX: 0.5,
anchorY: 0.5
});
// Use a different tint for the shield
shieldSprite.tint = 0x3498db;
self.radius = shieldSprite.width * 1.1;
self.duration = 600; // 10 seconds
self.update = function () {
self.duration--;
// Flash purple and green when 5 seconds (300 ticks) remaining
if (self.duration === 300) {
// Start color transition sequence
tween(shieldSprite, {
tint: 0x9b59b6 // Purple
}, {
duration: 150,
onFinish: function onFinish() {
tween(shieldSprite, {
tint: 0x2ecc71 // Green
}, {
duration: 150,
onFinish: function onFinish() {
// Repeat the color cycle until shield expires
var flashInterval = LK.setInterval(function () {
tween(shieldSprite, {
tint: 0x9b59b6 // Purple
}, {
duration: 150,
onFinish: function onFinish() {
tween(shieldSprite, {
tint: 0x2ecc71 // Green
}, {
duration: 150
});
}
});
}, 300);
// Store interval ID on shield for cleanup
self.flashInterval = flashInterval;
}
});
}
});
}
if (self.duration <= 0) {
// Clear flash interval if it exists
if (self.flashInterval) {
LK.clearInterval(self.flashInterval);
}
tween(self, {
alpha: 0
}, {
duration: 120,
onFinish: function onFinish() {
self.destroy();
}
});
}
// Follow the marine
if (marine) {
self.x = marine.x;
self.y = marine.y;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x660000
});
/****
* Game Code
****/
// Hell background elements
// Marine (player)
// Demon (enemy)
// Boss Demon
// Marine bullet
// Demon bullet
// Health pack
// Power-up
var fireParticles = [];
var hellRocks = [];
var particleSpawnTimer = 0;
// Create lava ground background
var lavaGround = LK.getAsset('lavaGround', {
anchorX: 0,
anchorY: 0
});
game.addChild(lavaGround);
// Generate initial rocks for hell landscape
function generateHellLandscape() {
// Create rocks scattered around the arena
for (var i = 0; i < 15; i++) {
var rock = new HellRock();
rock.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT);
rock.y = ARENA_TOP + Math.random() * (ARENA_BOTTOM - ARENA_TOP);
hellRocks.push(rock);
game.addChild(rock);
}
}
// Arena bounds
var ARENA_MARGIN = 80;
var ARENA_LEFT = ARENA_MARGIN;
var ARENA_TOP = 180;
var ARENA_RIGHT = 2048 - ARENA_MARGIN;
var ARENA_BOTTOM = 2732 - ARENA_MARGIN;
// Game state
var marine = null;
var demons = [];
var boss = null;
var bestDemon = null;
var marineBullets = [];
var demonBullets = [];
var bestDemonBullets = [];
var healthPacks = [];
var powerUps = [];
var shields = [];
var wave = 1;
var waveTimer = 0;
var spawnTimer = 0;
var bossActive = false;
var dragging = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
var lastMoveX = 0;
var lastMoveY = 0;
var score = 0;
var marineFireTimer = 0;
var marineFireRate = 12; // ticks between shots
var marinePowerTimer = 0;
var marineMaxHP = 1;
var gameOver = false;
var doublePointsActive = false;
var doublePointsTimer = 0;
var doublePointsDuration = 300; // 5 seconds at 60 FPS
var doublePointsIndicator = null; // Visual indicator for double points
// UI
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var hpTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xE74C3C
});
hpTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(hpTxt);
hpTxt.y = 120;
hpTxt.x = 2048 / 2;
// Timer display in top right corner
var gameStartTime = 0;
var timerTxt = new Text2('00:00', {
size: 80,
fill: 0xFFFFFF
});
timerTxt.anchor.set(1, 0); // Right align
LK.gui.topRight.addChild(timerTxt);
timerTxt.x = -20; // Small margin from right edge
timerTxt.y = 20; // Small margin from top
// Center arena
function centerArenaX() {
return (ARENA_LEFT + ARENA_RIGHT) / 2;
}
function centerArenaY() {
return (ARENA_TOP + ARENA_BOTTOM) / 2;
}
// Spawn marine
function spawnMarine() {
marine = new Marine();
marine.x = centerArenaX();
marine.y = ARENA_BOTTOM - 220;
game.addChild(marine);
}
// Spawn demon
function spawnDemon() {
var d = new Demon();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// top
d.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT);
d.y = ARENA_TOP + 40;
} else if (edge === 1) {
// bottom
d.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT);
d.y = ARENA_BOTTOM - 40;
} else if (edge === 2) {
// left
d.x = ARENA_LEFT + 40;
d.y = ARENA_TOP + Math.random() * (ARENA_BOTTOM - ARENA_TOP);
} else {
// right
d.x = ARENA_RIGHT - 40;
d.y = ARENA_TOP + Math.random() * (ARENA_BOTTOM - ARENA_TOP);
}
demons.push(d);
game.addChild(d);
// Initialize weapon for the demon
d.updateWeapon();
}
// Spawn boss
function spawnBoss() {
boss = new Boss();
boss.x = centerArenaX();
boss.y = ARENA_TOP + 200;
game.addChild(boss);
bossActive = true;
// Initialize weapons for the boss
boss.updateWeapons();
}
// Spawn best demon
function spawnBestDemon() {
bestDemon = new BestDemon();
bestDemon.x = centerArenaX();
bestDemon.y = ARENA_TOP + 150;
game.addChild(bestDemon);
// Initialize weapons for the best demon
bestDemon.updateWeapons();
}
// Spawn marine bullet
function spawnMarineBullet(x, y, dx, dy) {
var b = new MarineBullet();
b.x = x;
b.y = y;
var mag = Math.sqrt(dx * dx + dy * dy);
b.dx = dx / mag;
b.dy = dy / mag;
marineBullets.push(b);
game.addChild(b);
}
// Spawn demon bullet
function spawnDemonBullet(x, y, tx, ty, speed) {
var b = new DemonBullet();
b.x = x;
b.y = y;
var dx = tx - x;
var dy = ty - y;
var mag = Math.sqrt(dx * dx + dy * dy);
b.dx = dx / mag;
b.dy = dy / mag;
if (speed) b.speed = speed;
demonBullets.push(b);
game.addChild(b);
}
// Spawn best demon bullet
function spawnBestDemonBullet(x, y, tx, ty, speed) {
var b = new BestDemonBullet();
b.x = x;
b.y = y;
var dx = tx - x;
var dy = ty - y;
var mag = Math.sqrt(dx * dx + dy * dy);
b.dx = dx / mag;
b.dy = dy / mag;
if (speed) b.speed = speed;
bestDemonBullets.push(b);
game.addChild(b);
}
// Spawn health pack
function spawnHealthPack() {
var hp = new HealthPack();
hp.x = ARENA_LEFT + 100 + Math.random() * (ARENA_RIGHT - ARENA_LEFT - 200);
hp.y = ARENA_TOP + 100 + Math.random() * (ARENA_BOTTOM - ARENA_TOP - 200);
healthPacks.push(hp);
game.addChild(hp);
}
// Power-ups have been removed
function spawnPowerUp() {
// Function kept for compatibility but does nothing
}
// Update UI
function updateUI() {
scoreTxt.setText(score);
var hpStr = '';
for (var i = 0; i < marineMaxHP; ++i) {
hpStr += i < marine.hp ? '♥' : '♡';
}
hpTxt.setText(hpStr);
}
// Start game
function startGame() {
score = 0;
wave = 1;
waveTimer = 0;
spawnTimer = 0;
bossActive = false;
marineFireTimer = 0;
gameOver = false;
doublePointsActive = false;
doublePointsTimer = 0;
// Clean up double points indicator
if (doublePointsIndicator) {
doublePointsIndicator.destroy();
doublePointsIndicator = null;
}
gameStartTime = LK.ticks; // Record game start time
// Remove all
for (var i = 0; i < demons.length; ++i) demons[i].destroy();
for (var i = 0; i < marineBullets.length; ++i) marineBullets[i].destroy();
for (var i = 0; i < demonBullets.length; ++i) demonBullets[i].destroy();
for (var i = 0; i < bestDemonBullets.length; ++i) bestDemonBullets[i].destroy();
for (var i = 0; i < healthPacks.length; ++i) healthPacks[i].destroy();
// Remove fire particles and rocks
for (var i = 0; i < fireParticles.length; ++i) fireParticles[i].destroy();
for (var i = 0; i < hellRocks.length; ++i) hellRocks[i].destroy();
fireParticles = [];
hellRocks = [];
// Remove any active shields
for (var i = 0; i < game.children.length; i++) {
if (game.children[i] instanceof Shield) {
game.children[i].destroy();
}
}
if (marine) marine.destroy();
if (boss) boss.destroy();
if (bestDemon) bestDemon.destroy();
demons = [];
marineBullets = [];
demonBullets = [];
bestDemonBullets = [];
healthPacks = [];
shields = [];
boss = null;
bestDemon = null;
spawnMarine();
// Initialize marine with 1 weapon
if (marine) {
marine.updateWeapons(1);
}
updateUI();
}
// Generate initial hell landscape
generateHellLandscape();
startGame();
// Touch controls
game.down = function (x, y, obj) {
if (gameOver) return;
// Only drag if touch is inside marine
var dx = x - marine.x;
var dy = y - marine.y;
if (dx * dx + dy * dy < marine.radius * marine.radius * 1.2) {
dragging = true;
dragOffsetX = marine.x - x;
dragOffsetY = marine.y - y;
lastMoveX = x;
lastMoveY = y;
}
};
game.up = function (x, y, obj) {
dragging = false;
};
game.move = function (x, y, obj) {
if (gameOver) return;
// Always follow finger/mouse, clamp to arena
if (marine) {
var nx = x;
var ny = y;
nx = Math.max(ARENA_LEFT + marine.radius, Math.min(ARENA_RIGHT - marine.radius, nx));
ny = Math.max(ARENA_TOP + marine.radius, Math.min(ARENA_BOTTOM - marine.radius, ny));
marine.x = nx;
marine.y = ny;
lastMoveX = x;
lastMoveY = y;
}
};
// Main update loop
game.update = function () {
if (gameOver) return;
// Update timer display
var elapsedTicks = LK.ticks - gameStartTime;
var elapsedSeconds = Math.floor(elapsedTicks / 60);
var minutes = Math.floor(elapsedSeconds / 60);
var seconds = elapsedSeconds % 60;
var timeStr = (minutes < 10 ? '0' : '') + minutes + ':' + (seconds < 10 ? '0' : '') + seconds;
timerTxt.setText(timeStr);
// Update fire particles (hellish effect)
particleSpawnTimer--;
if (particleSpawnTimer <= 0) {
// Spawn new fire particles
for (var i = 0; i < 3; i++) {
var particle = new FireParticle();
particle.x = ARENA_LEFT + Math.random() * (ARENA_RIGHT - ARENA_LEFT);
particle.y = ARENA_BOTTOM - Math.random() * 100;
fireParticles.push(particle);
game.addChild(particle);
}
particleSpawnTimer = 2 + Math.floor(Math.random() * 3);
}
// Update existing fire particles
for (var i = fireParticles.length - 1; i >= 0; i--) {
fireParticles[i].update();
if (fireParticles[i].lifetime <= 0) {
fireParticles.splice(i, 1);
}
}
// Update marine
if (marine) marine.update();
// Update double points timer
if (doublePointsActive) {
// Create indicator if it doesn't exist
if (!doublePointsIndicator) {
doublePointsIndicator = LK.getAsset('2xpo', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
game.addChild(doublePointsIndicator);
}
// Position indicator next to marine
if (marine && doublePointsIndicator) {
doublePointsIndicator.x = marine.x + marine.radius * 1.5;
doublePointsIndicator.y = marine.y - marine.radius * 0.5;
}
doublePointsTimer--;
if (doublePointsTimer <= 0) {
doublePointsActive = false;
doublePointsTimer = 0; // Reset timer to ensure clean state
// Remove indicator when effect ends
if (doublePointsIndicator) {
doublePointsIndicator.destroy();
doublePointsIndicator = null;
}
}
} else {
// Ensure indicator is removed when double points is not active
if (doublePointsIndicator) {
doublePointsIndicator.destroy();
doublePointsIndicator = null;
}
}
// Update demons
for (var i = demons.length - 1; i >= 0; --i) {
var d = demons[i];
d.update();
if (d.dead) {
demons.splice(i, 1);
var points = doublePointsActive ? 20 : 10; // Double points if active
score += points;
updateUI();
// Chance to drop health
if (Math.random() < 0.08) spawnHealthPack();
}
}
// Update boss
if (boss) {
boss.update();
if (boss.dead) {
bossActive = false;
boss = null;
// Score is now added in the boss.hit method
wave++;
waveTimer = 0;
}
// Marine takes damage when touching boss
if (marine && !marine.dead && marine.invuln === 0 && !boss.dead) {
var dx = marine.x - boss.x;
var dy = marine.y - boss.y;
var r = marine.radius + boss.radius;
if (dx * dx + dy * dy < r * r) {
marine.hp--;
if (marine.hp < 0) marine.hp = 0;
marine.invuln = 60;
marine.flash();
updateUI();
if (marine.hp <= 0) {
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
}
// Update best demon
if (bestDemon) {
bestDemon.update();
if (bestDemon.dead) {
bestDemon = null;
wave++;
waveTimer = 0;
}
// Marine takes damage when touching best demon
if (marine && !marine.dead && marine.invuln === 0 && !bestDemon.dead) {
var dx = marine.x - bestDemon.x;
var dy = marine.y - bestDemon.y;
var r = marine.radius + bestDemon.radius;
if (dx * dx + dy * dy < r * r) {
marine.hp--;
if (marine.hp < 0) marine.hp = 0;
marine.invuln = 60;
marine.flash();
updateUI();
if (marine.hp <= 0) {
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
}
// Spawn best demon when score reaches 500 or more
if (!bestDemon && score >= 500) {
spawnBestDemon();
}
// Update marine bullets
for (var i = marineBullets.length - 1; i >= 0; --i) {
var b = marineBullets[i];
b.update();
// Remove if out of bounds
if (b.x < ARENA_LEFT - 60 || b.x > ARENA_RIGHT + 60 || b.y < ARENA_TOP - 60 || b.y > ARENA_BOTTOM + 60) {
b.destroy();
marineBullets.splice(i, 1);
continue;
}
// Hit demons
var hit = false;
for (var j = demons.length - 1; j >= 0; --j) {
var d = demons[j];
var dx = b.x - d.x;
var dy = b.y - d.y;
var r = b.radius + d.radius;
if (dx * dx + dy * dy < r * r) {
d.hit();
hit = true;
break;
}
}
// Hit boss
if (!hit && boss) {
var dx = b.x - boss.x;
var dy = b.y - boss.y;
var r = b.radius + boss.radius;
if (dx * dx + dy * dy < r * r) {
boss.hit();
hit = true;
}
}
// Hit best demon
if (!hit && bestDemon) {
var dx = b.x - bestDemon.x;
var dy = b.y - bestDemon.y;
var r = b.radius + bestDemon.radius;
if (dx * dx + dy * dy < r * r) {
bestDemon.hit();
hit = true;
}
}
if (hit) {
b.destroy();
marineBullets.splice(i, 1);
}
}
// Update demon bullets
for (var i = demonBullets.length - 1; i >= 0; --i) {
var b = demonBullets[i];
b.update();
// Remove if out of bounds
if (b.x < ARENA_LEFT - 60 || b.x > ARENA_RIGHT + 60 || b.y < ARENA_TOP - 60 || b.y > ARENA_BOTTOM + 60) {
b.destroy();
demonBullets.splice(i, 1);
continue;
}
// Hit marine
if (marine && marine.invuln === 0) {
var dx = b.x - marine.x;
var dy = b.y - marine.y;
var r = b.radius + marine.radius;
if (dx * dx + dy * dy < r * r) {
b.destroy();
demonBullets.splice(i, 1);
// Check if a shield is protecting the marine
var hasShield = false;
for (var j = 0; j < game.children.length; j++) {
if (game.children[j] instanceof Shield) {
hasShield = true;
// Destroy shield immediately when hit
if (game.children[j].flashInterval) {
LK.clearInterval(game.children[j].flashInterval);
}
// Flash shield effect and destroy it
tween(game.children[j], {
alpha: 0
}, {
duration: 120,
onFinish: function onFinish(obj) {
obj.destroy();
}
});
break;
}
}
if (!hasShield) {
marine.hp--;
marine.invuln = 60;
marine.flash();
updateUI();
if (marine.hp <= 0) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
}
}
// Update best demon bullets
for (var i = bestDemonBullets.length - 1; i >= 0; --i) {
var b = bestDemonBullets[i];
b.update();
// Remove if out of bounds
if (b.x < ARENA_LEFT - 60 || b.x > ARENA_RIGHT + 60 || b.y < ARENA_TOP - 60 || b.y > ARENA_BOTTOM + 60) {
b.destroy();
bestDemonBullets.splice(i, 1);
continue;
}
// Hit marine
if (marine && marine.invuln === 0) {
var dx = b.x - marine.x;
var dy = b.y - marine.y;
var r = b.radius + marine.radius;
if (dx * dx + dy * dy < r * r) {
b.destroy();
bestDemonBullets.splice(i, 1);
// Check if a shield is protecting the marine
var hasShield = false;
for (var j = 0; j < game.children.length; j++) {
if (game.children[j] instanceof Shield) {
hasShield = true;
// Destroy shield immediately when hit
if (game.children[j].flashInterval) {
LK.clearInterval(game.children[j].flashInterval);
}
// Flash shield effect and destroy it
tween(game.children[j], {
alpha: 0
}, {
duration: 120,
onFinish: function onFinish(obj) {
obj.destroy();
}
});
break;
}
}
if (!hasShield) {
marine.hp--;
marine.invuln = 60;
marine.flash();
updateUI();
if (marine.hp <= 0) {
// Game over
gameOver = true;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
}
}
// Update health packs
for (var i = healthPacks.length - 1; i >= 0; --i) {
var hp = healthPacks[i];
if (marine) {
var dx = hp.x - marine.x;
var dy = hp.y - marine.y;
var r = hp.radius + marine.radius;
if (dx * dx + dy * dy < r * r) {
if (marine.hp < marineMaxHP) {
marine.hp++;
}
// Activate double points for 10 seconds
doublePointsActive = true;
doublePointsTimer = doublePointsDuration;
updateUI();
hp.destroy();
healthPacks.splice(i, 1);
}
}
}
// Power-ups have been removed
// Marine auto-fire
if (marine) {
marineFireTimer--;
if (marineFireTimer <= 0) {
marineFireTimer = marineFireRate;
// Auto-targeting: find nearest enemy
var target = marine.findNearestTarget();
var tx = target.x;
var ty = target.y;
// Fire
// Calculate bullets based on score - 1 bullet base + 1 extra per 500 points
var totalBullets = 1 + Math.floor(score / 500);
// Make sure marine has an AK47
if (marine.weapons.length === 0) {
marine.updateWeapons(1);
}
var dx = tx - marine.x;
var dy = ty - marine.y;
var baseAngle = Math.atan2(dy, dx);
for (var n = 0; n < totalBullets; ++n) {
// Spread out extra bullets a bit
var offset = (n - (totalBullets - 1) / 2) * 0.12;
var angle = baseAngle + offset;
var ddx = Math.cos(angle);
var ddy = Math.sin(angle);
spawnMarineBullet(marine.x, marine.y, ddx, ddy, false);
}
}
}
// Wave logic
if (!bossActive) {
// Spawn demons
if (demons.length < Math.min(3 + wave, 8)) {
spawnTimer--;
if (spawnTimer <= 0) {
spawnDemon();
spawnTimer = 120 + Math.floor(Math.random() * 60); // Increased timer for slower demon spawning
}
}
// Next wave
if (demons.length === 0 && !boss && waveTimer > 60) {
if (score >= 100 && score < 490 && Math.floor(score / 100) > Math.floor((score - (doublePointsActive ? 20 : 10)) / 100)) {
spawnBoss();
} else {
wave++;
waveTimer = 0;
}
}
waveTimer++;
} else {
// Wait for boss defeat
if (boss && boss.dead) {
bossActive = false;
boss = null;
wave++;
waveTimer = 0;
}
}
// Randomly spawn health packs
if (LK.ticks % 420 === 0 && Math.random() < 0.5) {
spawnHealthPack();
}
// Pulsate lava ground for hellish effect
if (LK.ticks % 120 === 0) {
var targetTint = Math.random() < 0.5 ? 0x990000 : 0x660000;
tween(lavaGround, {
tint: targetTint
}, {
duration: 1000,
easing: tween.easeInOut
});
}
};
// Reset game on game over
LK.on('gameover', function () {
startGame();
});
yumurta şeytan. In-Game asset. 2d. High contrast. No shadows
yuvarlak bir çerçevenin içerisinde şeytan kulaklı gülümseyen balkabağı kırmızı. In-Game asset. 2d. High contrast. No shadows
yarasa. In-Game asset. 2d. High contrast. No shadows
furca. In-Game asset. 2d. High contrast. No shadows
+. In-Game asset. 2d. High contrast. No shadows
angel . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
blue 2x. In-Game asset. 2d. High contrast. No shadows
bigg demon. In-Game asset. 2d. High contrast. No shadows
red sword. In-Game asset. 2d. High contrast. No shadows
ateş. In-Game asset. 2d. High contrast. No shadows
tsunami. In-Game asset. 2d. High contrast. No shadows
yeşil bar. In-Game asset. 2d. High contrast. No shadows