User prompt
There is no healthbar
User prompt
MAKE THEM VISIBLE
User prompt
Add 6 healthbar asset as visuals to left bottom of the screen
User prompt
They are invisible fix it already
User prompt
Add left bottom 6 healthbar squares
User prompt
Make it visible
User prompt
Re add 6 square health bars using assets
User prompt
On mobile devices make the laser cannon work normally without left clicking
User prompt
Make it visible at left bottom corner
User prompt
Re add the 6 square health bar using the assets
User prompt
Re add health bars
User prompt
it doesn't work, why laser cannon not shoots when i click
User prompt
if cooldown is over, when i left click shoot the laser beam
User prompt
player must click after the cooldown is over. clicking once isn't enough to shoot laser cannon
User prompt
so when laser cannon cooldown is over, wait for the left click and when clicked then shoot it
User prompt
on pc, laser cannon only shoots when left clicked
User prompt
when the game starts hero automaticly follows the mouse
User prompt
when the game starts hero automaticly follows the mouse
User prompt
for pc players the hero can follow the mouse without clicking
User prompt
Use ulti sound when ulti used
User prompt
Please fix the bug: 'healthBarVSquares is not defined' in or related to this line: 'for (var i = 0; i < healthBarVSquares.length; i++) {' Line Number: 572
User prompt
Please fix the bug: 'healthBarVSquares is not defined' in or related to this line: 'for (var i = 0; i < healthBarVSquares.length; i++) {' Line Number: 572
User prompt
Please fix the bug: 'healthBarVSquares is not defined' in or related to this line: 'for (var i = 0; i < healthBarVSquares.length; i++) {' Line Number: 572
User prompt
Remove the healthbar at the right bottom
User prompt
Make a seperate shield asset
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Beam item class var BeamItem = Container.expand(function () { var self = Container.call(this); var beamSprite = self.attachAsset('beamItem', { anchorX: 0.5, anchorY: 0.5 }); beamSprite.width = 90; beamSprite.height = 90; beamSprite.alpha = 0.95; self.update = function () { self.y += 12; }; return self; }); // Enemy type 1: moves straight down var Enemy1 = Container.expand(function () { var self = Container.call(this); var enemySprite = self.attachAsset('enemy1', { anchorX: 0.5, anchorY: 0.5 }); enemySprite.width = 200; enemySprite.height = 200; self.speed = 8 + Math.random() * 4; self.update = function () { self.y += self.speed; }; return self; }); // Enemy type 2: moves in sine wave var Enemy2 = Container.expand(function () { var self = Container.call(this); var enemySprite = self.attachAsset('enemy2', { anchorX: 0.5, anchorY: 0.5 }); enemySprite.width = 160; enemySprite.height = 160; self.speed = 7 + Math.random() * 3; self.waveAmplitude = 120 + Math.random() * 80; self.waveSpeed = 0.02 + Math.random() * 0.01; self.baseX = 0; self.t = 0; self.update = function () { self.y += self.speed; self.t += self.waveSpeed; self.x = self.baseX + Math.sin(self.t * 6.28) * self.waveAmplitude; }; return self; }); // Strong Enemy3: needs 5 hits to die, moves straight down, larger and purple var Enemy3 = Container.expand(function () { var self = Container.call(this); var enemySprite = self.attachAsset('enemy3', { anchorX: 0.5, anchorY: 0.5 }); enemySprite.width = 260; enemySprite.height = 260; self.speed = 6 + Math.random() * 2; self.hp = 5; self.update = function () { self.y += self.speed; }; return self; }); // Enemy bullet class var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speedX = 0; self.speedY = 16; self.update = function () { self.x += self.speedX; self.y += self.speedY; }; return self; }); // Extraordinary EnemyX: spawns only once after 250 score, unique appearance and behavior var EnemyX = Container.expand(function () { var self = Container.call(this); // Use dedicated enemyX asset var enemySprite = self.attachAsset('enemyX', { anchorX: 0.5, anchorY: 0.5 }); enemySprite.width = 600; enemySprite.height = 600; enemySprite.alpha = 0.98; self.speed = 4 + Math.random() * 2; // Set EnemyX health to 250 only in normal mode, otherwise 500 (boss mode) self.hp = typeof bossMode !== "undefined" && bossMode ? 500 : 250; // much higher HP (500 hits required) // --- EnemyX shooting logic --- self.shootTick = 0; self.beamCooldown = 0; self.update = function () { // Boss mode: randomly drop health potion, shield, or beam items from EnemyX (less frequently) if (typeof bossMode !== "undefined" && bossMode && Math.random() < 0.005) { // 1/3 chance for each item var dropType = Math.floor(Math.random() * 3); var item; if (dropType === 0) { item = new HealthPotion(); } else if (dropType === 1) { item = new ShieldItem(); } else { item = new BeamItem(); } item.x = self.x + (Math.random() - 0.5) * 120; item.y = self.y + 120; if (typeof items !== "undefined" && typeof game !== "undefined") { items.push(item); game.addChild(item); } } // Only allow EnemyX to wander in the upper part of the screen (y between 0 and 500) if (typeof self.t === "undefined") self.t = Math.random() * 2; self.t += 0.012; // Sway left/right slowly self.x += Math.sin(self.t * 2.5) * 6; // Restrict vertical movement: only allow y to increase up to a certain limit, then bounce back up if (typeof self.directionY === "undefined") self.directionY = 1; // Set upper and lower bounds for wandering var upperY = 60 + enemySprite.height / 2; var lowerY = 420 + enemySprite.height / 2; // Move down or up depending on direction self.y += self.speed * self.directionY * 0.5; // much slower vertical movement // If reached lower bound, go up; if reached upper bound, go down if (self.y >= lowerY) { self.y = lowerY; self.directionY = -1; } else if (self.y <= upperY) { self.y = upperY; self.directionY = 1; } // --- Shooting less frequently --- self.shootTick = (self.shootTick || 0) + 1; self.beamCooldown = (self.beamCooldown || 0) - 1; // Shoot a bullet every 60-90 ticks (randomized, less frequent) if (self.shootTick >= 60 + Math.floor(Math.random() * 31)) { if (typeof game !== "undefined" && typeof hero !== "undefined") { // Shoot 3-way spread for (var i = -1; i <= 1; i++) { var bullet = new EnemyBullet(); bullet.x = self.x + i * 60; bullet.y = self.y + 120; // Aim at hero, but with spread var dx = hero.x - bullet.x + i * 80; var dy = hero.y - bullet.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { bullet.speedX = dx / len * 20; bullet.speedY = dy / len * 20; } enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('enemyShoot').play(); } } self.shootTick = 0; } // Sometimes shoot a beam down (every 360-540 ticks, much more rare, and only hit when visually active) if (self.beamCooldown <= 0 && Math.random() < 0.012) { if (typeof game !== "undefined") { // Visual: big vertical beam var beam = LK.getAsset('beamItem', { anchorX: 0.5, anchorY: 0, x: self.x, y: self.y + enemySprite.height / 2 }); beam.width = 120; beam.height = 2732 - (self.y + enemySprite.height / 2); beam.alpha = 0.38 + Math.random() * 0.12; game.addChild(beam); // Beam effect: damage hero if in column for 60 frames, but only on first 20 frames is it "active" beam.life = 60; beam.activeFrames = 20; beam.update = function () { this.life--; // Flicker effect this.alpha = 0.32 + Math.random() * 0.18; // Only hit hero if beam is visually active (first 20 frames) if (this.life > 60 - this.activeFrames) { if (typeof hero !== "undefined" && hero.y > this.y && hero.y < this.y + this.height) { if (Math.abs(hero.x - this.x) < this.width / 2 + hero.width / 2) { if (!godMode && !shieldActive && this.life === 60 - this.activeFrames + 1) { LK.getSound('heroHit').play(); LK.effects.flashScreen(0xff0000, 800); heroHealth--; updateHealthBar(); if (heroHealth <= 0) { LK.showGameOver(); return; } } else if (!godMode && shieldActive && this.life === 60 - this.activeFrames + 1) { LK.effects.flashObject(hero, 0x00ffff, 200); } } } } if (this.life <= 0) { this.destroy(); } }; // Add to enemyBullets for update/removal enemyBullets.push(beam); // Play laser sound LK.getSound('laser').play(); // Set cooldown for next beam (much more rare) self.beamCooldown = 360 + Math.floor(Math.random() * 180); } } }; return self; }); // Health potion class (separate asset) var HealthPotion = Container.expand(function () { var self = Container.call(this); var potionSprite = self.attachAsset('healthPotion', { anchorX: 0.5, anchorY: 0.5 }); potionSprite.width = 90; potionSprite.height = 90; potionSprite.alpha = 0.95; self.update = function () { self.y += 16; }; return self; }); // Hero class var Hero = Container.expand(function () { var self = Container.call(this); var heroSprite = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.radius = heroSprite.width / 2; self.shootCooldown = 0; self.update = function () { if (self.shootCooldown > 0) self.shootCooldown--; }; return self; }); // Hero bullet class var HeroBullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('heroBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -32; self.update = function () { self.y += self.speed; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Add background asset to game scene // --- MENU OVERLAY --- // Play menu music and stop background music when menu is shown // separate asset for health potion var backgroundSprite = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); // Scale background to fill the screen (2048x2732) var bgOriginalWidth = backgroundSprite.width; var bgOriginalHeight = backgroundSprite.height; var scaleX = 2048 / bgOriginalWidth; var scaleY = 2732 / bgOriginalHeight; var scale = Math.max(scaleX, scaleY); // cover entire area backgroundSprite.width = bgOriginalWidth * scale; backgroundSprite.height = bgOriginalHeight * scale; // Center if needed (in case aspect ratio doesn't match) backgroundSprite.x = (2048 - backgroundSprite.width) / 2; backgroundSprite.y = (2732 - backgroundSprite.height) / 2; game.addChild(backgroundSprite); LK.stopMusic(); LK.playMusic('Menu'); var menuOverlay = new Container(); menuOverlay.zIndex = 10000; // ensure on top // Title text var titleTxt = new Text2("GALAXY DODGE SHOOTER", { size: 160, fill: "#fff", fontWeight: "bold" }); titleTxt.anchor.set(0.5, 0); titleTxt.x = 2048 / 2; titleTxt.y = 220; menuOverlay.addChild(titleTxt); // Play button background var playBtnBg = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, tint: 0x00eaff }); playBtnBg.width = 600; playBtnBg.height = 220; playBtnBg.alpha = 0.25; playBtnBg.x = 2048 / 2; playBtnBg.y = 700; menuOverlay.addChild(playBtnBg); // Play button text var playBtn = new Text2("PLAY", { size: 140, fill: 0x00EAFF, fontWeight: "bold" }); playBtn.anchor.set(0.5, 0.5); playBtn.x = 2048 / 2; playBtn.y = 700; menuOverlay.addChild(playBtn); // Add music toggle button to menu var musicOn = true; var musicBtnBg = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, tint: 0x00eaff }); musicBtnBg.width = 340; musicBtnBg.height = 140; musicBtnBg.alpha = 0.25; musicBtnBg.x = 2048 / 2; musicBtnBg.y = 1050; menuOverlay.addChild(musicBtnBg); var musicBtn = new Text2("MUSIC: ON", { size: 80, fill: 0x00EAFF, fontWeight: "bold" }); musicBtn.anchor.set(0.5, 0.5); musicBtn.x = 2048 / 2; musicBtn.y = 1050; menuOverlay.addChild(musicBtn); // Add god mode play button to menu, positioned under the music button var godMode = false; var bossMode = false; var godPlayBtnBg = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, tint: 0xffd700 }); godPlayBtnBg.width = 600; godPlayBtnBg.height = 220; godPlayBtnBg.alpha = 0.25; // Place god mode button below the music button (with margin) godPlayBtnBg.x = 2048 / 2; godPlayBtnBg.y = 1050 + musicBtnBg.height / 2 + 60 + godPlayBtnBg.height / 2; menuOverlay.addChild(godPlayBtnBg); var godPlayBtn = new Text2("PLAY (GOD MODE)", { size: 100, fill: 0xffd700, fontWeight: "bold" }); godPlayBtn.anchor.set(0.5, 0.5); godPlayBtn.x = 2048 / 2; godPlayBtn.y = godPlayBtnBg.y; menuOverlay.addChild(godPlayBtn); // Add boss mode play button to menu, positioned under god mode button var bossPlayBtnBg = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, tint: 0xff4444 }); bossPlayBtnBg.width = 600; bossPlayBtnBg.height = 220; bossPlayBtnBg.alpha = 0.25; bossPlayBtnBg.x = 2048 / 2; bossPlayBtnBg.y = godPlayBtnBg.y + godPlayBtnBg.height / 2 + 60 + bossPlayBtnBg.height / 2; menuOverlay.addChild(bossPlayBtnBg); var bossPlayBtn = new Text2("BOSS MODE", { size: 100, fill: 0xff4444, fontWeight: "bold" }); bossPlayBtn.anchor.set(0.5, 0.5); bossPlayBtn.x = 2048 / 2; bossPlayBtn.y = bossPlayBtnBg.y; menuOverlay.addChild(bossPlayBtn); // Add menu overlay to game game.addChild(menuOverlay); // Menu state var menuActive = true; // Play button interaction menuOverlay.down = function (x, y, obj) { // Check if play button was pressed (normal mode) var dx = x - playBtn.x; var dy = y - playBtn.y; if (Math.abs(dx) <= playBtnBg.width / 2 && Math.abs(dy) <= playBtnBg.height / 2) { // Hide menu, start game in normal mode menuOverlay.visible = false; menuActive = false; godMode = false; LK.stopMusic(); if (musicOn) { LK.playMusic('bgmusic'); } return; } // Check if god mode play button was pressed var gdx = x - godPlayBtn.x; var gdy = y - godPlayBtn.y; if (Math.abs(gdx) <= godPlayBtnBg.width / 2 && Math.abs(gdy) <= godPlayBtnBg.height / 2) { // Hide menu, start game in god mode menuOverlay.visible = false; menuActive = false; godMode = true; LK.stopMusic(); if (musicOn) { LK.playMusic('Godmode'); // godmode music while playing in godmode } return; } // Check if music button was pressed var mdx = x - musicBtn.x; var mdy = y - musicBtn.y; if (Math.abs(mdx) <= musicBtnBg.width / 2 && Math.abs(mdy) <= musicBtnBg.height / 2) { musicOn = !musicOn; if (musicOn) { musicBtn.setText("MUSIC: ON"); LK.playMusic('Menu'); } else { musicBtn.setText("MUSIC: OFF"); LK.stopMusic(); } return; } // Check if boss mode play button was pressed var bdx = x - bossPlayBtn.x; var bdy = y - bossPlayBtn.y; if (Math.abs(bdx) <= bossPlayBtnBg.width / 2 && Math.abs(bdy) <= bossPlayBtnBg.height / 2) { // Hide menu, start game in boss mode menuOverlay.visible = false; menuActive = false; godMode = false; bossMode = true; LK.stopMusic(); if (musicOn) { LK.playMusic('Boss'); } // Set up for boss mode: reset score, enemyXSpawned, etc. score = 0; scoreTxt.setText(score); enemyXSpawned = false; bossMusicPlayed = true; // already playing spawnTick = 0; return; } }; menuOverlay.move = function (x, y, obj) {}; menuOverlay.up = function (x, y, obj) {}; // Score display var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Health bar UI (horizontal, left bottom) var maxHealth = 6; var heroHealth = maxHealth; var healthBarBg = LK.getAsset('enemy2', { anchorX: 0, // left edge anchorY: 1, // bottom edge tint: 0x222222 }); healthBarBg.width = 420; healthBarBg.height = 60; healthBarBg.alpha = 0.45; // Place at left bottom, with margin (60px from left, 60px from bottom) healthBarBg.x = 60; healthBarBg.y = 2732 - 60; LK.gui.bottomLeft.addChild(healthBarBg); var healthBar = LK.getAsset('enemy1', { anchorX: 0, // left edge anchorY: 1, // bottom edge tint: 0xff4444 }); healthBar.width = 420; healthBar.height = 60; healthBar.alpha = 0.85; healthBar.x = 60; healthBar.y = 2732 - 60; LK.gui.bottomLeft.addChild(healthBar); // Add vertical health bar using 6-square Healthbar asset in the left bottom of the screen var healthBarVSquares = []; var healthBarVContainer = new Container(); // Get the Healthbar asset to measure a single square var healthbarAsset = LK.getAsset('Healthbar', { anchorX: 0.5, anchorY: 0.5 }); var squareW = healthbarAsset.width; var squareH = healthbarAsset.height; healthbarAsset.destroy(); // Only used for measurement // Place the container at left bottom, with margin (60px from left, 60px from bottom) healthBarVContainer.x = 60 + squareW / 2; healthBarVContainer.y = 2732 - 60 - (maxHealth - 1) * (squareH + 8) / 2; // Arrange 6 squares vertically, centered on container for (var i = 0; i < maxHealth; i++) { var sq = LK.getAsset('Healthbar', { anchorX: 0.5, anchorY: 0.5 }); sq.x = 0; sq.y = (i - (maxHealth - 1) / 2) * (squareH + 8); // 8px gap between squares sq.alpha = 1.0; healthBarVContainer.addChild(sq); healthBarVSquares.push(sq); } game.addChild(healthBarVContainer); function updateHealthBar() { // Horizontal bar (left) healthBar.width = 420 * (heroHealth / maxHealth); if (heroHealth <= 2) { healthBar.tint = 0xff2222; } else if (heroHealth <= 4) { healthBar.tint = 0xffbb22; } else { healthBar.tint = 0x44ff44; } // Vertical bar (center) - show/hide squares and tint by health for (var i = 0; i < healthBarVSquares.length; i++) { if (i < heroHealth) { healthBarVSquares[i].visible = true; // Tint by health: green if >4, yellow if 3-4, red if 1-2 if (heroHealth <= 2) { healthBarVSquares[i].tint = 0xff2222; } else if (heroHealth <= 4) { healthBarVSquares[i].tint = 0xffbb22; } else { healthBarVSquares[i].tint = 0x44ff44; } // Add a white border highlight if health is full healthBarVSquares[i].alpha = heroHealth === maxHealth ? 1.0 : 0.95; } else { healthBarVSquares[i].visible = false; } } } updateHealthBar(); // Game variables var hero = new Hero(); game.addChild(hero); hero.x = 2048 / 2; hero.y = 2732 - 350; // Track if EnemyX has spawned var enemyXSpawned = false; // Ulti button UI and state var ultiReady = true; var ultiCooldown = 0; var ultiCooldownMax = 1800; // 30 seconds at 60fps // Create ulti button var ultiBtn = new Text2("ULTI", { size: 120, fill: 0x00EAFF, fontWeight: "bold" }); ultiBtn.anchor.set(0.5, 0.5); // Place in right corner, leaving margin for touch and not overlapping top menu ultiBtn.x = 2048 - 180; ultiBtn.y = 2732 - 180; ultiBtn.bg = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, tint: 0x00eaff }); ultiBtn.bg.width = 340; ultiBtn.bg.height = 220; ultiBtn.bg.alpha = 0.25; ultiBtn.bg.x = ultiBtn.x; ultiBtn.bg.y = ultiBtn.y; game.addChild(ultiBtn.bg); game.addChild(ultiBtn); // Ulti button cooldown overlay var ultiBtnOverlay = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, tint: 0x222222 }); ultiBtnOverlay.width = 340; ultiBtnOverlay.height = 220; ultiBtnOverlay.alpha = 0.55; ultiBtnOverlay.x = ultiBtn.x; ultiBtnOverlay.y = ultiBtn.y; ultiBtnOverlay.visible = false; game.addChild(ultiBtnOverlay); // Laser Cannon state (no button UI) var laserReady = true; var laserCooldown = 0; var laserCooldownMax = 720; // 12 seconds at 60fps // Armor visual overlay for shield var heroArmor = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, tint: 0x80eaff // light blue }); heroArmor.width = hero.width * 1.35; heroArmor.height = hero.height * 1.35; heroArmor.alpha = 0.55; heroArmor.visible = false; game.addChild(heroArmor); var heroBullets = []; var enemies = []; var enemyBullets = []; var items = []; var dragNode = null; var lastHeroIntersecting = false; var score = 0; var spawnTick = 0; var enemyShootTick = 0; // Powerup state var shieldActive = false; var shieldTimer = 0; var beamActive = false; var beamTimer = 0; // Helper: spawn item function spawnItem() { var itemType = Math.random() < 0.5 ? "shield" : "beam"; var item; if (itemType === "shield") { item = new ShieldItem(); } else { item = new BeamItem(); } item.x = 200 + Math.random() * (2048 - 400); item.y = -80; items.push(item); game.addChild(item); } // Helper: spawn enemy function spawnEnemy() { // Increase chance for Enemy3: 20% Enemy3, 40% Enemy1, 40% Enemy2 var rand = Math.random(); var enemy; if (rand < 0.4) { enemy = new Enemy1(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; } else if (rand < 0.8) { enemy = new Enemy2(); enemy.baseX = 200 + Math.random() * (2048 - 400); enemy.x = enemy.baseX; enemy.y = -80; enemy.t = Math.random() * 2; } else { enemy = new Enemy3(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; } enemies.push(enemy); game.addChild(enemy); } // Helper: spawn enemy bullet function spawnEnemyBullet(enemy, targetX, targetY) { var bullet = new EnemyBullet(); bullet.x = enemy.x; bullet.y = enemy.y + 60; // Aim at hero var dx = targetX - bullet.x; var dy = targetY - bullet.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { bullet.speedX = dx / len * 18; bullet.speedY = dy / len * 18; } enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('enemyShoot').play(); } // Move handler for dragging hero function handleMove(x, y, obj) { if (dragNode) { // Clamp hero inside screen var hw = dragNode.width / 2; var hh = dragNode.height / 2; var nx = Math.max(hw, Math.min(2048 - hw, x)); var ny = Math.max(hh + 100, Math.min(2732 - hh, y)); dragNode.x = nx; dragNode.y = ny; } } // Touch down: start dragging hero game.down = function (x, y, obj) { if (typeof menuActive !== "undefined" && menuActive) return; // PC: left mouse click triggers laser cannon if ready and not on menu if (obj && obj.event && obj.event.type === "mousedown" && obj.event.button === 0 && laserReady) { // Only fire if click is inside hero sprite (circle) var dx = x - hero.x; var dy = y - hero.y; if (dx * dx + dy * dy <= hero.radius * hero.radius) { // Activate laser laserReady = false; laserCooldown = laserCooldownMax; // Laser effect: fire a vertical laser column at hero.x var laserX = hero.x; // Visual effect: draw a vertical beam for a short time, and make it follow the hero var laserBeam = LK.getAsset('heroBullet', { anchorX: 0.5, anchorY: 1, x: laserX, y: hero.y - hero.height / 2 }); laserBeam.width = 80; laserBeam.height = hero.y - hero.height / 2; // from hero to top laserBeam.alpha = 0.45; game.addChild(laserBeam); // Store reference to active laser beam and set timer game.activeLaserBeam = laserBeam; game.activeLaserBeamTimer = 90; // 1.5 seconds at 60fps LK.getSound('laser').play(); // Destroy all enemies and enemy bullets in the column for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; if (Math.abs(e.x - laserX) <= 80) { // Laser cannon does NOT affect EnemyX (boss) if (e instanceof EnemyX) { // Flash to show hit, but do not damage or destroy LK.effects.flashObject(e, 0xff0000, 120); continue; } LK.effects.flashObject(e, 0xffffff, 200); for (var pi = 0; pi < 12; pi++) { var part = LK.getAsset(e instanceof Enemy1 ? 'enemy1' : e instanceof Enemy2 ? 'enemy2' : 'enemy3', { anchorX: 0.5, anchorY: 0.5 }); part.x = e.x; part.y = e.y; part.width = 20 + Math.random() * 20; part.height = 20 + Math.random() * 20; part.alpha = 0.7 + Math.random() * 0.3; var angle = Math.PI * 2 * (pi / 12) + Math.random() * 0.2; var speed = 10 + Math.random() * 10; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 10 + Math.floor(Math.random() * 10); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.88 + Math.random() * 0.06; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } // 35% chance to drop a health potion if Enemy3 if (e instanceof Enemy3 && Math.random() < 0.35) { var healthPotion = new HealthPotion(); healthPotion.x = e.x; healthPotion.y = e.y; items.push(healthPotion); game.addChild(healthPotion); } e.destroy(); enemies.splice(i, 1); } } for (var i = enemyBullets.length - 1; i >= 0; i--) { var b = enemyBullets[i]; if (Math.abs(b.x - laserX) <= 80) { b.destroy(); enemyBullets.splice(i, 1); } } return; } } dragNode = hero; handleMove(x, y, obj); }; // Touch up: stop dragging game.up = function (x, y, obj) { if (typeof menuActive !== "undefined" && menuActive) return; dragNode = null; }; // Touch move: move hero game.move = function (x, y, obj) { if (typeof menuActive !== "undefined" && menuActive) return; handleMove(x, y, obj); // Handle ulti button press (touch/click) if (ultiReady) { // Check if touch is inside ulti button area var dx = x - ultiBtn.x; var dy = y - ultiBtn.y; if (Math.abs(dx) <= ultiBtn.bg.width / 2 && Math.abs(dy) <= ultiBtn.bg.height / 2) { // Activate ulti ultiReady = false; ultiCooldown = ultiCooldownMax; ultiBtnOverlay.visible = true; // Ulti effect: clear all enemies and enemy bullets for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; if (e instanceof EnemyX) { // Ulti does 50 hits to EnemyX e.hp -= 50; LK.effects.flashObject(e, 0xffffff, 400); if (e.hp <= 0) { // Play death animation for EnemyX var enemyToDestroy = e; for (var pi = 0; pi < 48; pi++) { var part = LK.getAsset('enemyX', { anchorX: 0.5, anchorY: 0.5 }); part.x = enemyToDestroy.x; part.y = enemyToDestroy.y; part.width = 48 + Math.random() * 48; part.height = 48 + Math.random() * 48; part.alpha = 0.8 + Math.random() * 0.2; var angle = Math.PI * 2 * (pi / 48) + Math.random() * 0.2; var speed = 24 + Math.random() * 16; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 32 + Math.floor(Math.random() * 32); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.90 + Math.random() * 0.04; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } // Add score for killing EnemyX with ulti score += 1; scoreTxt.setText(score); // Drop 3 health potions when EnemyX is killed by ulti for (var hp = 0; hp < 3; hp++) { var healthPotion = new HealthPotion(); healthPotion.x = enemyToDestroy.x - 60 + 60 * hp; healthPotion.y = enemyToDestroy.y; items.push(healthPotion); game.addChild(healthPotion); } enemyToDestroy.destroy(); enemies.splice(i, 1); } } else { // Add score for killing normal enemies with ulti score += 1; scoreTxt.setText(score); enemies[i].destroy(); enemies.splice(i, 1); } } for (var i = enemyBullets.length - 1; i >= 0; i--) { enemyBullets[i].destroy(); enemyBullets.splice(i, 1); } LK.effects.flashScreen(0x00eaff, 600); return; } } // Handle laser cannon activation by clicking on the hero if (laserReady) { var dx = x - hero.x; var dy = y - hero.y; // Check if touch/click is inside hero sprite (circle) if (dx * dx + dy * dy <= hero.radius * hero.radius) { // Activate laser laserReady = false; laserCooldown = laserCooldownMax; // Laser effect: fire a vertical laser column at hero.x var laserX = hero.x; // Visual effect: draw a vertical beam for a short time, and make it follow the hero var laserBeam = LK.getAsset('heroBullet', { anchorX: 0.5, anchorY: 1, //{37} // anchor at bottom x: laserX, y: hero.y - hero.height / 2 }); laserBeam.width = 80; laserBeam.height = hero.y - hero.height / 2; // from hero to top laserBeam.alpha = 0.45; game.addChild(laserBeam); // Store reference to active laser beam and set timer game.activeLaserBeam = laserBeam; game.activeLaserBeamTimer = 90; // 1.5 seconds at 60fps LK.getSound('laser').play(); // Destroy all enemies and enemy bullets in the column for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; if (Math.abs(e.x - laserX) <= 80) { // Laser cannon does NOT affect EnemyX (boss) if (e instanceof EnemyX) { // Flash to show hit, but do not damage or destroy LK.effects.flashObject(e, 0xff0000, 120); continue; } // Play death animation for each LK.effects.flashObject(e, 0xffffff, 200); // Particle effect for each for (var pi = 0; pi < 12; pi++) { var part = LK.getAsset(e instanceof Enemy1 ? 'enemy1' : e instanceof Enemy2 ? 'enemy2' : 'enemy3', { anchorX: 0.5, anchorY: 0.5 }); part.x = e.x; part.y = e.y; part.width = 20 + Math.random() * 20; part.height = 20 + Math.random() * 20; part.alpha = 0.7 + Math.random() * 0.3; var angle = Math.PI * 2 * (pi / 12) + Math.random() * 0.2; var speed = 10 + Math.random() * 10; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 10 + Math.floor(Math.random() * 10); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.88 + Math.random() * 0.06; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } // 35% chance to drop a health potion if Enemy3 if (e instanceof Enemy3 && Math.random() < 0.35) { var healthPotion = new HealthPotion(); healthPotion.x = e.x; healthPotion.y = e.y; items.push(healthPotion); game.addChild(healthPotion); } e.destroy(); enemies.splice(i, 1); } } for (var i = enemyBullets.length - 1; i >= 0; i--) { var b = enemyBullets[i]; if (Math.abs(b.x - laserX) <= 80) { b.destroy(); enemyBullets.splice(i, 1); } } return; } } }; // Main game update game.update = function () { if (typeof menuActive !== "undefined" && menuActive) { // Block all game logic while menu is active return; } // Update hero hero.update(); // Laser beam follow logic if (game.activeLaserBeam) { // Move the beam to follow the hero's x and y, and always stretch from hero to top game.activeLaserBeam.x = hero.x; game.activeLaserBeam.y = hero.y - hero.height / 2; game.activeLaserBeam.height = hero.y - hero.height / 2; // Laser beam hits enemies it touches for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; // Check if enemy is within the laser beam's column (80px wide, same as beam) if (e.y + (e.height ? e.height / 2 : 60) >= 0 && // enemy is not above top e.y - (e.height ? e.height / 2 : 60) <= hero.y - hero.height / 2 && // enemy is not below beam bottom Math.abs(e.x - game.activeLaserBeam.x) <= game.activeLaserBeam.width / 2) { // Laser cannon does NOT affect EnemyX (boss) if (e instanceof EnemyX) { // Flash to show hit, but do not damage or destroy LK.effects.flashObject(e, 0xff0000, 120); continue; } // Play hit sound and flash LK.getSound('enemyHit').play(); LK.effects.flashObject(e, 0xffffff, 200); // Score and handle Enemy3 HP score += 1; scoreTxt.setText(score); if (e instanceof Enemy3) { e.hp--; if (e.hp <= 0) { // Play death animation before destroying Enemy3 var enemyToDestroy = e; for (var pi = 0; pi < 24; pi++) { var part = LK.getAsset('enemy3', { anchorX: 0.5, anchorY: 0.5 }); part.x = enemyToDestroy.x; part.y = enemyToDestroy.y; part.width = 32 + Math.random() * 32; part.height = 32 + Math.random() * 32; part.alpha = 0.8 + Math.random() * 0.2; var angle = Math.PI * 2 * (pi / 24) + Math.random() * 0.2; var speed = 16 + Math.random() * 12; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 16 + Math.floor(Math.random() * 16); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.90 + Math.random() * 0.04; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } // 35% chance to drop a health potion when Enemy3 is killed if (Math.random() < 0.35) { var healthPotion = new HealthPotion(); healthPotion.x = enemyToDestroy.x; healthPotion.y = enemyToDestroy.y; items.push(healthPotion); game.addChild(healthPotion); } enemyToDestroy.destroy(); enemies.splice(i, 1); } else { // Flash for hit, but don't destroy LK.effects.flashObject(e, 0x8e24aa, 120); } } else { // Play death animation before destroying normal enemies var enemyToDestroy = e; for (var pi = 0; pi < 16; pi++) { var part = LK.getAsset(enemyToDestroy instanceof Enemy1 ? 'enemy1' : 'enemy2', { anchorX: 0.5, anchorY: 0.5 }); part.x = enemyToDestroy.x; part.y = enemyToDestroy.y; part.width = 20 + Math.random() * 20; part.height = 20 + Math.random() * 20; part.alpha = 0.7 + Math.random() * 0.3; var angle = Math.PI * 2 * (pi / 16) + Math.random() * 0.2; var speed = 10 + Math.random() * 10; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 10 + Math.floor(Math.random() * 10); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.88 + Math.random() * 0.06; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } enemyToDestroy.destroy(); enemies.splice(i, 1); } } } // Decrement timer and destroy when done game.activeLaserBeamTimer--; if (game.activeLaserBeamTimer <= 0) { game.activeLaserBeam.destroy(); game.activeLaserBeam = null; } } // Ulti cooldown logic if (godMode) { ultiReady = true; ultiCooldown = 0; ultiBtnOverlay.visible = false; ultiBtn.text = "ULTI"; } else { if (!ultiReady) { ultiCooldown--; if (ultiCooldown <= 0) { ultiReady = true; ultiBtnOverlay.visible = false; } else { // Show overlay and update text to show seconds left ultiBtnOverlay.visible = true; ultiBtn.text = "ULTI\n" + Math.ceil(ultiCooldown / 60) + "s"; } } if (ultiReady) { ultiBtn.text = "ULTI"; ultiBtnOverlay.visible = false; } } // Update armor overlay position and visibility heroArmor.x = hero.x; heroArmor.y = hero.y; heroArmor.visible = shieldActive; // Laser cannon cooldown logic (no button UI) if (!laserReady) { laserCooldown--; if (laserCooldown <= 0) { laserReady = true; } } // Update shield/beam timers if (shieldActive) { shieldTimer--; if (shieldTimer <= 0) { shieldActive = false; } } if (beamActive) { beamTimer--; if (beamTimer <= 0) { beamActive = false; } } // Update items (powerups) for (var i = items.length - 1; i >= 0; i--) { var item = items[i]; item.update(); // Remove if off screen if (item.y > 2732 + 100) { item.destroy(); items.splice(i, 1); continue; } // Pickup by hero if (item.intersects(hero)) { if (item instanceof HealthPotion) { // Health potion always gives 1 health (if not at max) if (heroHealth < maxHealth) { heroHealth = Math.min(maxHealth, heroHealth + 1); updateHealthBar(); LK.effects.flashObject(hero, 0x44ff44, 400); } } else if (item instanceof ShieldItem) { // If already at max health, treat as shield if (heroHealth >= maxHealth) { shieldActive = true; shieldTimer = 360; // 6 seconds at 60fps LK.effects.flashObject(hero, 0x00ffff, 400); } else { // If not at max health, ignore shield pickup (health potions now handled separately) } } else if (item instanceof BeamItem) { beamActive = true; beamTimer = 180; // 3 seconds at 60fps LK.effects.flashObject(hero, 0xff00ff, 400); } item.destroy(); items.splice(i, 1); continue; } } // Hero shooting (auto-fire) if (hero.shootCooldown <= 0) { if (beamActive) { // Beam: fire 3 bullets in spread for (var bdir = -1; bdir <= 1; bdir++) { var bullet = new HeroBullet(); bullet.x = hero.x + bdir * 40; bullet.y = hero.y - hero.height / 2 - 20; bullet.spread = bdir * 0.18; // radians bullet.update = function (origUpdate, spread) { return function () { this.y += this.speed; this.x += Math.sin(spread) * 18; }; }(bullet.update, bullet.spread); heroBullets.push(bullet); game.addChild(bullet); } hero.shootCooldown = 6; LK.getSound('laser').play(); } else { var bullet = new HeroBullet(); bullet.x = hero.x; bullet.y = hero.y - hero.height / 2 - 20; heroBullets.push(bullet); game.addChild(bullet); hero.shootCooldown = 10; LK.getSound('laser').play(); } } // Update hero bullets for (var i = heroBullets.length - 1; i >= 0; i--) { var b = heroBullets[i]; b.update(); // Remove if off screen if (b.y < -60) { b.destroy(); heroBullets.splice(i, 1); continue; } // Check collision with enemies for (var j = enemies.length - 1; j >= 0; j--) { var e = enemies[j]; if (b.intersects(e)) { // Enemy hit LK.getSound('enemyHit').play(); score += 1; scoreTxt.setText(score); // Flash enemy LK.effects.flashObject(e, 0xffffff, 200); // Remove both or handle Enemy3/EnemyX hp if (e instanceof Enemy3 || e instanceof EnemyX) { e.hp--; if (e.hp <= 0) { // Play death animation before destroying Enemy3/EnemyX var enemyToDestroy = e; // Particle animation for Enemy3/EnemyX death (more particles, richer effect) var particleCount = e instanceof EnemyX ? 48 : 24; for (var pi = 0; pi < particleCount; pi++) { var part = LK.getAsset(e instanceof EnemyX ? 'enemyX' : 'enemy3', { anchorX: 0.5, anchorY: 0.5 }); part.x = enemyToDestroy.x; part.y = enemyToDestroy.y; part.width = (e instanceof EnemyX ? 48 : 32) + Math.random() * (e instanceof EnemyX ? 48 : 32); part.height = (e instanceof EnemyX ? 48 : 32) + Math.random() * (e instanceof EnemyX ? 48 : 32); part.alpha = 0.8 + Math.random() * 0.2; var angle = Math.PI * 2 * (pi / particleCount) + Math.random() * 0.2; var speed = (e instanceof EnemyX ? 24 : 16) + Math.random() * (e instanceof EnemyX ? 16 : 12); var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = (e instanceof EnemyX ? 32 : 16) + Math.floor(Math.random() * (e instanceof EnemyX ? 32 : 16)); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.90 + Math.random() * 0.04; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } // 35% chance to drop a health potion when Enemy3 is killed (not for EnemyX) if (e instanceof Enemy3 && Math.random() < 0.35) { var healthPotion = new HealthPotion(); healthPotion.x = enemyToDestroy.x; healthPotion.y = enemyToDestroy.y; items.push(healthPotion); game.addChild(healthPotion); } enemyToDestroy.destroy(); enemies.splice(j, 1); // If EnemyX was killed, drop 3 health potions and restart normal enemy spawning if (e instanceof EnemyX) { for (var hp = 0; hp < 3; hp++) { var healthPotion = new HealthPotion(); // Spread potions horizontally a bit healthPotion.x = enemyToDestroy.x - 60 + 60 * hp; healthPotion.y = enemyToDestroy.y; items.push(healthPotion); game.addChild(healthPotion); } // Reset boss state and allow normal enemies to spawn again enemyXSpawned = false; bossMusicPlayed = false; // Reset spawnTick so normal enemies start coming again, but slowly spawnTick = 0; // Optionally, set a delay before next normal spawn (e.g. 60 frames) // spawnTick = -60; } } else { // Flash for hit, but don't destroy LK.effects.flashObject(e, 0x8e24aa, 120); } b.destroy(); heroBullets.splice(i, 1); break; } else { b.destroy(); heroBullets.splice(i, 1); // Play death animation before destroying normal enemies var enemyToDestroy = e; // Particle animation for normal enemy death (more particles, richer effect) for (var pi = 0; pi < 16; pi++) { var part = LK.getAsset(enemyToDestroy instanceof Enemy1 ? 'enemy1' : 'enemy2', { anchorX: 0.5, anchorY: 0.5 }); part.x = enemyToDestroy.x; part.y = enemyToDestroy.y; part.width = 20 + Math.random() * 20; part.height = 20 + Math.random() * 20; part.alpha = 0.7 + Math.random() * 0.3; var angle = Math.PI * 2 * (pi / 16) + Math.random() * 0.2; var speed = 10 + Math.random() * 10; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 10 + Math.floor(Math.random() * 10); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.88 + Math.random() * 0.06; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } enemyToDestroy.destroy(); enemies.splice(j, 1); break; } } } } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); // Remove if off screen if (e.y > 2732 + 100) { e.destroy(); enemies.splice(i, 1); continue; } // Enemy shoots randomly if (Math.random() < 0.008) { spawnEnemyBullet(e, hero.x, hero.y); } // Check collision with hero if (e.intersects(hero)) { if (godMode) { // In god mode, ignore all damage and just destroy enemy enemies[i].destroy(); enemies.splice(i, 1); continue; } if (!shieldActive) { // Hero hit LK.getSound('heroHit').play(); LK.effects.flashScreen(0xff0000, 800); heroHealth--; updateHealthBar(); if (heroHealth <= 0) { LK.showGameOver(); return; } enemies[i].destroy(); enemies.splice(i, 1); continue; } else { // Shield absorbs hit, destroy enemy LK.effects.flashObject(hero, 0x00ffff, 200); // Play death animation before destroying enemy killed by shield var enemyToDestroy = e; // Particle animation for shield kill (more particles, richer effect) for (var pi = 0; pi < 16; pi++) { var part = LK.getAsset(enemyToDestroy instanceof Enemy1 ? 'enemy1' : enemyToDestroy instanceof Enemy2 ? 'enemy2' : 'enemy3', { anchorX: 0.5, anchorY: 0.5 }); part.x = enemyToDestroy.x; part.y = enemyToDestroy.y; part.width = 20 + Math.random() * 20; part.height = 20 + Math.random() * 20; part.alpha = 0.7 + Math.random() * 0.3; var angle = Math.PI * 2 * (pi / 16) + Math.random() * 0.2; var speed = 10 + Math.random() * 10; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 10 + Math.floor(Math.random() * 10); part.update = function () { this.x += vx; this.y += vy; this.life--; this.alpha *= 0.88 + Math.random() * 0.06; if (this.life <= 0) { this.destroy(); } }; game.addChild(part); } enemyToDestroy.destroy(); enemies.splice(i, 1); continue; } } } // Update enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var b = enemyBullets[i]; b.update(); // Remove if off screen if (b.y < -60 || b.y > 2732 + 60 || b.x < -60 || b.x > 2048 + 60) { b.destroy(); enemyBullets.splice(i, 1); continue; } // Check collision with hero if (b.intersects(hero)) { if (godMode) { // In god mode, ignore all damage and just destroy bullet b.destroy(); enemyBullets.splice(i, 1); continue; } if (!shieldActive) { LK.getSound('heroHit').play(); LK.effects.flashScreen(0xff0000, 800); heroHealth--; updateHealthBar(); if (heroHealth <= 0) { LK.showGameOver(); return; } b.destroy(); enemyBullets.splice(i, 1); continue; } else { // Shield absorbs bullet LK.effects.flashObject(hero, 0x00ffff, 120); b.destroy(); enemyBullets.splice(i, 1); continue; } } } // --- ENEMY SPAWN RATE BOOST LOGIC --- if (typeof spawnRateBoostActive === "undefined") { var spawnRateBoostActive = false; var spawnRateBoostTimer = 0; var nextBoostScore = 50; var spawnIntervalNormal = 60; var spawnIntervalBoost = 30; } // Boss mode: only spawn EnemyX, never normal enemies or items if (typeof bossMode !== "undefined" && bossMode) { spawnTick++; // Only spawn EnemyX if not already present if (!enemyXSpawned && spawnTick >= 30) { var enemy = new EnemyX(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; enemies.push(enemy); game.addChild(enemy); enemyXSpawned = true; // Music already handled in menu spawnTick = 0; } // Never spawn items or normal enemies in boss mode } else { // Check if we reached a new boost score (50, 150, 250, ...) and trigger boost if (score >= nextBoostScore && !spawnRateBoostActive) { spawnRateBoostActive = true; spawnRateBoostTimer = 180; // 3 seconds at 60fps // After 50, next is 150, then 250, 350, ... if (nextBoostScore === 50) { nextBoostScore = 150; } else { nextBoostScore += 100; } } // Handle boost timer if (spawnRateBoostActive) { spawnRateBoostTimer--; if (spawnRateBoostTimer <= 0) { spawnRateBoostActive = false; } } // Spawn enemies spawnTick++; // Use boosted or normal interval // Make spawn rate scale more slowly with score (easier): decrease by 2 per 20 score, not 4 per 10 var spawnInterval = spawnRateBoostActive ? spawnIntervalBoost : Math.max(18, spawnIntervalNormal - Math.floor(score / 20) * 2); // Slower scaling for easier game if (score < 250) { if (spawnTick >= spawnInterval) { // Calculate how many enemies to spawn based on score (1 + 1 per 50 score) var numEnemies = 1 + Math.floor(score / 50); for (var i = 0; i < numEnemies; i++) { spawnEnemy(); } // 1 in 8 chance to spawn an item if (Math.random() < 0.125) { spawnItem(); } spawnTick = 0; } } else if (score >= 250) { // Stop current music and play boss music when score passes 250 if (typeof bossMusicPlayed === "undefined") { var bossMusicPlayed = false; } // --- Ensure EnemyX spawns immediately after passing 250 score before any other enemies --- if (!enemyXSpawned) { // Only spawn EnemyX if not already present var enemy = new EnemyX(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; enemies.push(enemy); game.addChild(enemy); enemyXSpawned = true; // Stop current music and play boss music when EnemyX spawns if (!bossMusicPlayed) { LK.stopMusic(); LK.playMusic('Boss'); bossMusicPlayed = true; } spawnTick = 0; } // Do NOT spawn any other enemies or items until EnemyX is killed else { // Wait until EnemyX is killed before resuming normal spawns // (do nothing here) } } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Beam item class
var BeamItem = Container.expand(function () {
var self = Container.call(this);
var beamSprite = self.attachAsset('beamItem', {
anchorX: 0.5,
anchorY: 0.5
});
beamSprite.width = 90;
beamSprite.height = 90;
beamSprite.alpha = 0.95;
self.update = function () {
self.y += 12;
};
return self;
});
// Enemy type 1: moves straight down
var Enemy1 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy1', {
anchorX: 0.5,
anchorY: 0.5
});
enemySprite.width = 200;
enemySprite.height = 200;
self.speed = 8 + Math.random() * 4;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy type 2: moves in sine wave
var Enemy2 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
enemySprite.width = 160;
enemySprite.height = 160;
self.speed = 7 + Math.random() * 3;
self.waveAmplitude = 120 + Math.random() * 80;
self.waveSpeed = 0.02 + Math.random() * 0.01;
self.baseX = 0;
self.t = 0;
self.update = function () {
self.y += self.speed;
self.t += self.waveSpeed;
self.x = self.baseX + Math.sin(self.t * 6.28) * self.waveAmplitude;
};
return self;
});
// Strong Enemy3: needs 5 hits to die, moves straight down, larger and purple
var Enemy3 = Container.expand(function () {
var self = Container.call(this);
var enemySprite = self.attachAsset('enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
enemySprite.width = 260;
enemySprite.height = 260;
self.speed = 6 + Math.random() * 2;
self.hp = 5;
self.update = function () {
self.y += self.speed;
};
return self;
});
// Enemy bullet class
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
self.speedY = 16;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
};
return self;
});
// Extraordinary EnemyX: spawns only once after 250 score, unique appearance and behavior
var EnemyX = Container.expand(function () {
var self = Container.call(this);
// Use dedicated enemyX asset
var enemySprite = self.attachAsset('enemyX', {
anchorX: 0.5,
anchorY: 0.5
});
enemySprite.width = 600;
enemySprite.height = 600;
enemySprite.alpha = 0.98;
self.speed = 4 + Math.random() * 2;
// Set EnemyX health to 250 only in normal mode, otherwise 500 (boss mode)
self.hp = typeof bossMode !== "undefined" && bossMode ? 500 : 250; // much higher HP (500 hits required)
// --- EnemyX shooting logic ---
self.shootTick = 0;
self.beamCooldown = 0;
self.update = function () {
// Boss mode: randomly drop health potion, shield, or beam items from EnemyX (less frequently)
if (typeof bossMode !== "undefined" && bossMode && Math.random() < 0.005) {
// 1/3 chance for each item
var dropType = Math.floor(Math.random() * 3);
var item;
if (dropType === 0) {
item = new HealthPotion();
} else if (dropType === 1) {
item = new ShieldItem();
} else {
item = new BeamItem();
}
item.x = self.x + (Math.random() - 0.5) * 120;
item.y = self.y + 120;
if (typeof items !== "undefined" && typeof game !== "undefined") {
items.push(item);
game.addChild(item);
}
}
// Only allow EnemyX to wander in the upper part of the screen (y between 0 and 500)
if (typeof self.t === "undefined") self.t = Math.random() * 2;
self.t += 0.012;
// Sway left/right slowly
self.x += Math.sin(self.t * 2.5) * 6;
// Restrict vertical movement: only allow y to increase up to a certain limit, then bounce back up
if (typeof self.directionY === "undefined") self.directionY = 1;
// Set upper and lower bounds for wandering
var upperY = 60 + enemySprite.height / 2;
var lowerY = 420 + enemySprite.height / 2;
// Move down or up depending on direction
self.y += self.speed * self.directionY * 0.5; // much slower vertical movement
// If reached lower bound, go up; if reached upper bound, go down
if (self.y >= lowerY) {
self.y = lowerY;
self.directionY = -1;
} else if (self.y <= upperY) {
self.y = upperY;
self.directionY = 1;
}
// --- Shooting less frequently ---
self.shootTick = (self.shootTick || 0) + 1;
self.beamCooldown = (self.beamCooldown || 0) - 1;
// Shoot a bullet every 60-90 ticks (randomized, less frequent)
if (self.shootTick >= 60 + Math.floor(Math.random() * 31)) {
if (typeof game !== "undefined" && typeof hero !== "undefined") {
// Shoot 3-way spread
for (var i = -1; i <= 1; i++) {
var bullet = new EnemyBullet();
bullet.x = self.x + i * 60;
bullet.y = self.y + 120;
// Aim at hero, but with spread
var dx = hero.x - bullet.x + i * 80;
var dy = hero.y - bullet.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
bullet.speedX = dx / len * 20;
bullet.speedY = dy / len * 20;
}
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
}
}
self.shootTick = 0;
}
// Sometimes shoot a beam down (every 360-540 ticks, much more rare, and only hit when visually active)
if (self.beamCooldown <= 0 && Math.random() < 0.012) {
if (typeof game !== "undefined") {
// Visual: big vertical beam
var beam = LK.getAsset('beamItem', {
anchorX: 0.5,
anchorY: 0,
x: self.x,
y: self.y + enemySprite.height / 2
});
beam.width = 120;
beam.height = 2732 - (self.y + enemySprite.height / 2);
beam.alpha = 0.38 + Math.random() * 0.12;
game.addChild(beam);
// Beam effect: damage hero if in column for 60 frames, but only on first 20 frames is it "active"
beam.life = 60;
beam.activeFrames = 20;
beam.update = function () {
this.life--;
// Flicker effect
this.alpha = 0.32 + Math.random() * 0.18;
// Only hit hero if beam is visually active (first 20 frames)
if (this.life > 60 - this.activeFrames) {
if (typeof hero !== "undefined" && hero.y > this.y && hero.y < this.y + this.height) {
if (Math.abs(hero.x - this.x) < this.width / 2 + hero.width / 2) {
if (!godMode && !shieldActive && this.life === 60 - this.activeFrames + 1) {
LK.getSound('heroHit').play();
LK.effects.flashScreen(0xff0000, 800);
heroHealth--;
updateHealthBar();
if (heroHealth <= 0) {
LK.showGameOver();
return;
}
} else if (!godMode && shieldActive && this.life === 60 - this.activeFrames + 1) {
LK.effects.flashObject(hero, 0x00ffff, 200);
}
}
}
}
if (this.life <= 0) {
this.destroy();
}
};
// Add to enemyBullets for update/removal
enemyBullets.push(beam);
// Play laser sound
LK.getSound('laser').play();
// Set cooldown for next beam (much more rare)
self.beamCooldown = 360 + Math.floor(Math.random() * 180);
}
}
};
return self;
});
// Health potion class (separate asset)
var HealthPotion = Container.expand(function () {
var self = Container.call(this);
var potionSprite = self.attachAsset('healthPotion', {
anchorX: 0.5,
anchorY: 0.5
});
potionSprite.width = 90;
potionSprite.height = 90;
potionSprite.alpha = 0.95;
self.update = function () {
self.y += 16;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroSprite = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = heroSprite.width / 2;
self.shootCooldown = 0;
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
};
return self;
});
// Hero bullet class
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -32;
self.update = function () {
self.y += self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// Add background asset to game scene
// --- MENU OVERLAY ---
// Play menu music and stop background music when menu is shown
// separate asset for health potion
var backgroundSprite = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Scale background to fill the screen (2048x2732)
var bgOriginalWidth = backgroundSprite.width;
var bgOriginalHeight = backgroundSprite.height;
var scaleX = 2048 / bgOriginalWidth;
var scaleY = 2732 / bgOriginalHeight;
var scale = Math.max(scaleX, scaleY); // cover entire area
backgroundSprite.width = bgOriginalWidth * scale;
backgroundSprite.height = bgOriginalHeight * scale;
// Center if needed (in case aspect ratio doesn't match)
backgroundSprite.x = (2048 - backgroundSprite.width) / 2;
backgroundSprite.y = (2732 - backgroundSprite.height) / 2;
game.addChild(backgroundSprite);
LK.stopMusic();
LK.playMusic('Menu');
var menuOverlay = new Container();
menuOverlay.zIndex = 10000; // ensure on top
// Title text
var titleTxt = new Text2("GALAXY DODGE SHOOTER", {
size: 160,
fill: "#fff",
fontWeight: "bold"
});
titleTxt.anchor.set(0.5, 0);
titleTxt.x = 2048 / 2;
titleTxt.y = 220;
menuOverlay.addChild(titleTxt);
// Play button background
var playBtnBg = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00eaff
});
playBtnBg.width = 600;
playBtnBg.height = 220;
playBtnBg.alpha = 0.25;
playBtnBg.x = 2048 / 2;
playBtnBg.y = 700;
menuOverlay.addChild(playBtnBg);
// Play button text
var playBtn = new Text2("PLAY", {
size: 140,
fill: 0x00EAFF,
fontWeight: "bold"
});
playBtn.anchor.set(0.5, 0.5);
playBtn.x = 2048 / 2;
playBtn.y = 700;
menuOverlay.addChild(playBtn);
// Add music toggle button to menu
var musicOn = true;
var musicBtnBg = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00eaff
});
musicBtnBg.width = 340;
musicBtnBg.height = 140;
musicBtnBg.alpha = 0.25;
musicBtnBg.x = 2048 / 2;
musicBtnBg.y = 1050;
menuOverlay.addChild(musicBtnBg);
var musicBtn = new Text2("MUSIC: ON", {
size: 80,
fill: 0x00EAFF,
fontWeight: "bold"
});
musicBtn.anchor.set(0.5, 0.5);
musicBtn.x = 2048 / 2;
musicBtn.y = 1050;
menuOverlay.addChild(musicBtn);
// Add god mode play button to menu, positioned under the music button
var godMode = false;
var bossMode = false;
var godPlayBtnBg = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xffd700
});
godPlayBtnBg.width = 600;
godPlayBtnBg.height = 220;
godPlayBtnBg.alpha = 0.25;
// Place god mode button below the music button (with margin)
godPlayBtnBg.x = 2048 / 2;
godPlayBtnBg.y = 1050 + musicBtnBg.height / 2 + 60 + godPlayBtnBg.height / 2;
menuOverlay.addChild(godPlayBtnBg);
var godPlayBtn = new Text2("PLAY (GOD MODE)", {
size: 100,
fill: 0xffd700,
fontWeight: "bold"
});
godPlayBtn.anchor.set(0.5, 0.5);
godPlayBtn.x = 2048 / 2;
godPlayBtn.y = godPlayBtnBg.y;
menuOverlay.addChild(godPlayBtn);
// Add boss mode play button to menu, positioned under god mode button
var bossPlayBtnBg = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff4444
});
bossPlayBtnBg.width = 600;
bossPlayBtnBg.height = 220;
bossPlayBtnBg.alpha = 0.25;
bossPlayBtnBg.x = 2048 / 2;
bossPlayBtnBg.y = godPlayBtnBg.y + godPlayBtnBg.height / 2 + 60 + bossPlayBtnBg.height / 2;
menuOverlay.addChild(bossPlayBtnBg);
var bossPlayBtn = new Text2("BOSS MODE", {
size: 100,
fill: 0xff4444,
fontWeight: "bold"
});
bossPlayBtn.anchor.set(0.5, 0.5);
bossPlayBtn.x = 2048 / 2;
bossPlayBtn.y = bossPlayBtnBg.y;
menuOverlay.addChild(bossPlayBtn);
// Add menu overlay to game
game.addChild(menuOverlay);
// Menu state
var menuActive = true;
// Play button interaction
menuOverlay.down = function (x, y, obj) {
// Check if play button was pressed (normal mode)
var dx = x - playBtn.x;
var dy = y - playBtn.y;
if (Math.abs(dx) <= playBtnBg.width / 2 && Math.abs(dy) <= playBtnBg.height / 2) {
// Hide menu, start game in normal mode
menuOverlay.visible = false;
menuActive = false;
godMode = false;
LK.stopMusic();
if (musicOn) {
LK.playMusic('bgmusic');
}
return;
}
// Check if god mode play button was pressed
var gdx = x - godPlayBtn.x;
var gdy = y - godPlayBtn.y;
if (Math.abs(gdx) <= godPlayBtnBg.width / 2 && Math.abs(gdy) <= godPlayBtnBg.height / 2) {
// Hide menu, start game in god mode
menuOverlay.visible = false;
menuActive = false;
godMode = true;
LK.stopMusic();
if (musicOn) {
LK.playMusic('Godmode'); // godmode music while playing in godmode
}
return;
}
// Check if music button was pressed
var mdx = x - musicBtn.x;
var mdy = y - musicBtn.y;
if (Math.abs(mdx) <= musicBtnBg.width / 2 && Math.abs(mdy) <= musicBtnBg.height / 2) {
musicOn = !musicOn;
if (musicOn) {
musicBtn.setText("MUSIC: ON");
LK.playMusic('Menu');
} else {
musicBtn.setText("MUSIC: OFF");
LK.stopMusic();
}
return;
}
// Check if boss mode play button was pressed
var bdx = x - bossPlayBtn.x;
var bdy = y - bossPlayBtn.y;
if (Math.abs(bdx) <= bossPlayBtnBg.width / 2 && Math.abs(bdy) <= bossPlayBtnBg.height / 2) {
// Hide menu, start game in boss mode
menuOverlay.visible = false;
menuActive = false;
godMode = false;
bossMode = true;
LK.stopMusic();
if (musicOn) {
LK.playMusic('Boss');
}
// Set up for boss mode: reset score, enemyXSpawned, etc.
score = 0;
scoreTxt.setText(score);
enemyXSpawned = false;
bossMusicPlayed = true; // already playing
spawnTick = 0;
return;
}
};
menuOverlay.move = function (x, y, obj) {};
menuOverlay.up = function (x, y, obj) {};
// Score display
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Health bar UI (horizontal, left bottom)
var maxHealth = 6;
var heroHealth = maxHealth;
var healthBarBg = LK.getAsset('enemy2', {
anchorX: 0,
// left edge
anchorY: 1,
// bottom edge
tint: 0x222222
});
healthBarBg.width = 420;
healthBarBg.height = 60;
healthBarBg.alpha = 0.45;
// Place at left bottom, with margin (60px from left, 60px from bottom)
healthBarBg.x = 60;
healthBarBg.y = 2732 - 60;
LK.gui.bottomLeft.addChild(healthBarBg);
var healthBar = LK.getAsset('enemy1', {
anchorX: 0,
// left edge
anchorY: 1,
// bottom edge
tint: 0xff4444
});
healthBar.width = 420;
healthBar.height = 60;
healthBar.alpha = 0.85;
healthBar.x = 60;
healthBar.y = 2732 - 60;
LK.gui.bottomLeft.addChild(healthBar);
// Add vertical health bar using 6-square Healthbar asset in the left bottom of the screen
var healthBarVSquares = [];
var healthBarVContainer = new Container();
// Get the Healthbar asset to measure a single square
var healthbarAsset = LK.getAsset('Healthbar', {
anchorX: 0.5,
anchorY: 0.5
});
var squareW = healthbarAsset.width;
var squareH = healthbarAsset.height;
healthbarAsset.destroy(); // Only used for measurement
// Place the container at left bottom, with margin (60px from left, 60px from bottom)
healthBarVContainer.x = 60 + squareW / 2;
healthBarVContainer.y = 2732 - 60 - (maxHealth - 1) * (squareH + 8) / 2;
// Arrange 6 squares vertically, centered on container
for (var i = 0; i < maxHealth; i++) {
var sq = LK.getAsset('Healthbar', {
anchorX: 0.5,
anchorY: 0.5
});
sq.x = 0;
sq.y = (i - (maxHealth - 1) / 2) * (squareH + 8); // 8px gap between squares
sq.alpha = 1.0;
healthBarVContainer.addChild(sq);
healthBarVSquares.push(sq);
}
game.addChild(healthBarVContainer);
function updateHealthBar() {
// Horizontal bar (left)
healthBar.width = 420 * (heroHealth / maxHealth);
if (heroHealth <= 2) {
healthBar.tint = 0xff2222;
} else if (heroHealth <= 4) {
healthBar.tint = 0xffbb22;
} else {
healthBar.tint = 0x44ff44;
}
// Vertical bar (center) - show/hide squares and tint by health
for (var i = 0; i < healthBarVSquares.length; i++) {
if (i < heroHealth) {
healthBarVSquares[i].visible = true;
// Tint by health: green if >4, yellow if 3-4, red if 1-2
if (heroHealth <= 2) {
healthBarVSquares[i].tint = 0xff2222;
} else if (heroHealth <= 4) {
healthBarVSquares[i].tint = 0xffbb22;
} else {
healthBarVSquares[i].tint = 0x44ff44;
}
// Add a white border highlight if health is full
healthBarVSquares[i].alpha = heroHealth === maxHealth ? 1.0 : 0.95;
} else {
healthBarVSquares[i].visible = false;
}
}
}
updateHealthBar();
// Game variables
var hero = new Hero();
game.addChild(hero);
hero.x = 2048 / 2;
hero.y = 2732 - 350;
// Track if EnemyX has spawned
var enemyXSpawned = false;
// Ulti button UI and state
var ultiReady = true;
var ultiCooldown = 0;
var ultiCooldownMax = 1800; // 30 seconds at 60fps
// Create ulti button
var ultiBtn = new Text2("ULTI", {
size: 120,
fill: 0x00EAFF,
fontWeight: "bold"
});
ultiBtn.anchor.set(0.5, 0.5);
// Place in right corner, leaving margin for touch and not overlapping top menu
ultiBtn.x = 2048 - 180;
ultiBtn.y = 2732 - 180;
ultiBtn.bg = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00eaff
});
ultiBtn.bg.width = 340;
ultiBtn.bg.height = 220;
ultiBtn.bg.alpha = 0.25;
ultiBtn.bg.x = ultiBtn.x;
ultiBtn.bg.y = ultiBtn.y;
game.addChild(ultiBtn.bg);
game.addChild(ultiBtn);
// Ulti button cooldown overlay
var ultiBtnOverlay = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x222222
});
ultiBtnOverlay.width = 340;
ultiBtnOverlay.height = 220;
ultiBtnOverlay.alpha = 0.55;
ultiBtnOverlay.x = ultiBtn.x;
ultiBtnOverlay.y = ultiBtn.y;
ultiBtnOverlay.visible = false;
game.addChild(ultiBtnOverlay);
// Laser Cannon state (no button UI)
var laserReady = true;
var laserCooldown = 0;
var laserCooldownMax = 720; // 12 seconds at 60fps
// Armor visual overlay for shield
var heroArmor = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x80eaff // light blue
});
heroArmor.width = hero.width * 1.35;
heroArmor.height = hero.height * 1.35;
heroArmor.alpha = 0.55;
heroArmor.visible = false;
game.addChild(heroArmor);
var heroBullets = [];
var enemies = [];
var enemyBullets = [];
var items = [];
var dragNode = null;
var lastHeroIntersecting = false;
var score = 0;
var spawnTick = 0;
var enemyShootTick = 0;
// Powerup state
var shieldActive = false;
var shieldTimer = 0;
var beamActive = false;
var beamTimer = 0;
// Helper: spawn item
function spawnItem() {
var itemType = Math.random() < 0.5 ? "shield" : "beam";
var item;
if (itemType === "shield") {
item = new ShieldItem();
} else {
item = new BeamItem();
}
item.x = 200 + Math.random() * (2048 - 400);
item.y = -80;
items.push(item);
game.addChild(item);
}
// Helper: spawn enemy
function spawnEnemy() {
// Increase chance for Enemy3: 20% Enemy3, 40% Enemy1, 40% Enemy2
var rand = Math.random();
var enemy;
if (rand < 0.4) {
enemy = new Enemy1();
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = -80;
} else if (rand < 0.8) {
enemy = new Enemy2();
enemy.baseX = 200 + Math.random() * (2048 - 400);
enemy.x = enemy.baseX;
enemy.y = -80;
enemy.t = Math.random() * 2;
} else {
enemy = new Enemy3();
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = -80;
}
enemies.push(enemy);
game.addChild(enemy);
}
// Helper: spawn enemy bullet
function spawnEnemyBullet(enemy, targetX, targetY) {
var bullet = new EnemyBullet();
bullet.x = enemy.x;
bullet.y = enemy.y + 60;
// Aim at hero
var dx = targetX - bullet.x;
var dy = targetY - bullet.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 0) {
bullet.speedX = dx / len * 18;
bullet.speedY = dy / len * 18;
}
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
}
// Move handler for dragging hero
function handleMove(x, y, obj) {
if (dragNode) {
// Clamp hero inside screen
var hw = dragNode.width / 2;
var hh = dragNode.height / 2;
var nx = Math.max(hw, Math.min(2048 - hw, x));
var ny = Math.max(hh + 100, Math.min(2732 - hh, y));
dragNode.x = nx;
dragNode.y = ny;
}
}
// Touch down: start dragging hero
game.down = function (x, y, obj) {
if (typeof menuActive !== "undefined" && menuActive) return;
// PC: left mouse click triggers laser cannon if ready and not on menu
if (obj && obj.event && obj.event.type === "mousedown" && obj.event.button === 0 && laserReady) {
// Only fire if click is inside hero sprite (circle)
var dx = x - hero.x;
var dy = y - hero.y;
if (dx * dx + dy * dy <= hero.radius * hero.radius) {
// Activate laser
laserReady = false;
laserCooldown = laserCooldownMax;
// Laser effect: fire a vertical laser column at hero.x
var laserX = hero.x;
// Visual effect: draw a vertical beam for a short time, and make it follow the hero
var laserBeam = LK.getAsset('heroBullet', {
anchorX: 0.5,
anchorY: 1,
x: laserX,
y: hero.y - hero.height / 2
});
laserBeam.width = 80;
laserBeam.height = hero.y - hero.height / 2; // from hero to top
laserBeam.alpha = 0.45;
game.addChild(laserBeam);
// Store reference to active laser beam and set timer
game.activeLaserBeam = laserBeam;
game.activeLaserBeamTimer = 90; // 1.5 seconds at 60fps
LK.getSound('laser').play();
// Destroy all enemies and enemy bullets in the column
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
if (Math.abs(e.x - laserX) <= 80) {
// Laser cannon does NOT affect EnemyX (boss)
if (e instanceof EnemyX) {
// Flash to show hit, but do not damage or destroy
LK.effects.flashObject(e, 0xff0000, 120);
continue;
}
LK.effects.flashObject(e, 0xffffff, 200);
for (var pi = 0; pi < 12; pi++) {
var part = LK.getAsset(e instanceof Enemy1 ? 'enemy1' : e instanceof Enemy2 ? 'enemy2' : 'enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = e.x;
part.y = e.y;
part.width = 20 + Math.random() * 20;
part.height = 20 + Math.random() * 20;
part.alpha = 0.7 + Math.random() * 0.3;
var angle = Math.PI * 2 * (pi / 12) + Math.random() * 0.2;
var speed = 10 + Math.random() * 10;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = 10 + Math.floor(Math.random() * 10);
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.88 + Math.random() * 0.06;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
// 35% chance to drop a health potion if Enemy3
if (e instanceof Enemy3 && Math.random() < 0.35) {
var healthPotion = new HealthPotion();
healthPotion.x = e.x;
healthPotion.y = e.y;
items.push(healthPotion);
game.addChild(healthPotion);
}
e.destroy();
enemies.splice(i, 1);
}
}
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
if (Math.abs(b.x - laserX) <= 80) {
b.destroy();
enemyBullets.splice(i, 1);
}
}
return;
}
}
dragNode = hero;
handleMove(x, y, obj);
};
// Touch up: stop dragging
game.up = function (x, y, obj) {
if (typeof menuActive !== "undefined" && menuActive) return;
dragNode = null;
};
// Touch move: move hero
game.move = function (x, y, obj) {
if (typeof menuActive !== "undefined" && menuActive) return;
handleMove(x, y, obj);
// Handle ulti button press (touch/click)
if (ultiReady) {
// Check if touch is inside ulti button area
var dx = x - ultiBtn.x;
var dy = y - ultiBtn.y;
if (Math.abs(dx) <= ultiBtn.bg.width / 2 && Math.abs(dy) <= ultiBtn.bg.height / 2) {
// Activate ulti
ultiReady = false;
ultiCooldown = ultiCooldownMax;
ultiBtnOverlay.visible = true;
// Ulti effect: clear all enemies and enemy bullets
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
if (e instanceof EnemyX) {
// Ulti does 50 hits to EnemyX
e.hp -= 50;
LK.effects.flashObject(e, 0xffffff, 400);
if (e.hp <= 0) {
// Play death animation for EnemyX
var enemyToDestroy = e;
for (var pi = 0; pi < 48; pi++) {
var part = LK.getAsset('enemyX', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = enemyToDestroy.x;
part.y = enemyToDestroy.y;
part.width = 48 + Math.random() * 48;
part.height = 48 + Math.random() * 48;
part.alpha = 0.8 + Math.random() * 0.2;
var angle = Math.PI * 2 * (pi / 48) + Math.random() * 0.2;
var speed = 24 + Math.random() * 16;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = 32 + Math.floor(Math.random() * 32);
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.90 + Math.random() * 0.04;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
// Add score for killing EnemyX with ulti
score += 1;
scoreTxt.setText(score);
// Drop 3 health potions when EnemyX is killed by ulti
for (var hp = 0; hp < 3; hp++) {
var healthPotion = new HealthPotion();
healthPotion.x = enemyToDestroy.x - 60 + 60 * hp;
healthPotion.y = enemyToDestroy.y;
items.push(healthPotion);
game.addChild(healthPotion);
}
enemyToDestroy.destroy();
enemies.splice(i, 1);
}
} else {
// Add score for killing normal enemies with ulti
score += 1;
scoreTxt.setText(score);
enemies[i].destroy();
enemies.splice(i, 1);
}
}
for (var i = enemyBullets.length - 1; i >= 0; i--) {
enemyBullets[i].destroy();
enemyBullets.splice(i, 1);
}
LK.effects.flashScreen(0x00eaff, 600);
return;
}
}
// Handle laser cannon activation by clicking on the hero
if (laserReady) {
var dx = x - hero.x;
var dy = y - hero.y;
// Check if touch/click is inside hero sprite (circle)
if (dx * dx + dy * dy <= hero.radius * hero.radius) {
// Activate laser
laserReady = false;
laserCooldown = laserCooldownMax;
// Laser effect: fire a vertical laser column at hero.x
var laserX = hero.x;
// Visual effect: draw a vertical beam for a short time, and make it follow the hero
var laserBeam = LK.getAsset('heroBullet', {
anchorX: 0.5,
anchorY: 1,
//{37} // anchor at bottom
x: laserX,
y: hero.y - hero.height / 2
});
laserBeam.width = 80;
laserBeam.height = hero.y - hero.height / 2; // from hero to top
laserBeam.alpha = 0.45;
game.addChild(laserBeam);
// Store reference to active laser beam and set timer
game.activeLaserBeam = laserBeam;
game.activeLaserBeamTimer = 90; // 1.5 seconds at 60fps
LK.getSound('laser').play();
// Destroy all enemies and enemy bullets in the column
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
if (Math.abs(e.x - laserX) <= 80) {
// Laser cannon does NOT affect EnemyX (boss)
if (e instanceof EnemyX) {
// Flash to show hit, but do not damage or destroy
LK.effects.flashObject(e, 0xff0000, 120);
continue;
}
// Play death animation for each
LK.effects.flashObject(e, 0xffffff, 200);
// Particle effect for each
for (var pi = 0; pi < 12; pi++) {
var part = LK.getAsset(e instanceof Enemy1 ? 'enemy1' : e instanceof Enemy2 ? 'enemy2' : 'enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = e.x;
part.y = e.y;
part.width = 20 + Math.random() * 20;
part.height = 20 + Math.random() * 20;
part.alpha = 0.7 + Math.random() * 0.3;
var angle = Math.PI * 2 * (pi / 12) + Math.random() * 0.2;
var speed = 10 + Math.random() * 10;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = 10 + Math.floor(Math.random() * 10);
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.88 + Math.random() * 0.06;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
// 35% chance to drop a health potion if Enemy3
if (e instanceof Enemy3 && Math.random() < 0.35) {
var healthPotion = new HealthPotion();
healthPotion.x = e.x;
healthPotion.y = e.y;
items.push(healthPotion);
game.addChild(healthPotion);
}
e.destroy();
enemies.splice(i, 1);
}
}
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
if (Math.abs(b.x - laserX) <= 80) {
b.destroy();
enemyBullets.splice(i, 1);
}
}
return;
}
}
};
// Main game update
game.update = function () {
if (typeof menuActive !== "undefined" && menuActive) {
// Block all game logic while menu is active
return;
}
// Update hero
hero.update();
// Laser beam follow logic
if (game.activeLaserBeam) {
// Move the beam to follow the hero's x and y, and always stretch from hero to top
game.activeLaserBeam.x = hero.x;
game.activeLaserBeam.y = hero.y - hero.height / 2;
game.activeLaserBeam.height = hero.y - hero.height / 2;
// Laser beam hits enemies it touches
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
// Check if enemy is within the laser beam's column (80px wide, same as beam)
if (e.y + (e.height ? e.height / 2 : 60) >= 0 &&
// enemy is not above top
e.y - (e.height ? e.height / 2 : 60) <= hero.y - hero.height / 2 &&
// enemy is not below beam bottom
Math.abs(e.x - game.activeLaserBeam.x) <= game.activeLaserBeam.width / 2) {
// Laser cannon does NOT affect EnemyX (boss)
if (e instanceof EnemyX) {
// Flash to show hit, but do not damage or destroy
LK.effects.flashObject(e, 0xff0000, 120);
continue;
}
// Play hit sound and flash
LK.getSound('enemyHit').play();
LK.effects.flashObject(e, 0xffffff, 200);
// Score and handle Enemy3 HP
score += 1;
scoreTxt.setText(score);
if (e instanceof Enemy3) {
e.hp--;
if (e.hp <= 0) {
// Play death animation before destroying Enemy3
var enemyToDestroy = e;
for (var pi = 0; pi < 24; pi++) {
var part = LK.getAsset('enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = enemyToDestroy.x;
part.y = enemyToDestroy.y;
part.width = 32 + Math.random() * 32;
part.height = 32 + Math.random() * 32;
part.alpha = 0.8 + Math.random() * 0.2;
var angle = Math.PI * 2 * (pi / 24) + Math.random() * 0.2;
var speed = 16 + Math.random() * 12;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = 16 + Math.floor(Math.random() * 16);
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.90 + Math.random() * 0.04;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
// 35% chance to drop a health potion when Enemy3 is killed
if (Math.random() < 0.35) {
var healthPotion = new HealthPotion();
healthPotion.x = enemyToDestroy.x;
healthPotion.y = enemyToDestroy.y;
items.push(healthPotion);
game.addChild(healthPotion);
}
enemyToDestroy.destroy();
enemies.splice(i, 1);
} else {
// Flash for hit, but don't destroy
LK.effects.flashObject(e, 0x8e24aa, 120);
}
} else {
// Play death animation before destroying normal enemies
var enemyToDestroy = e;
for (var pi = 0; pi < 16; pi++) {
var part = LK.getAsset(enemyToDestroy instanceof Enemy1 ? 'enemy1' : 'enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = enemyToDestroy.x;
part.y = enemyToDestroy.y;
part.width = 20 + Math.random() * 20;
part.height = 20 + Math.random() * 20;
part.alpha = 0.7 + Math.random() * 0.3;
var angle = Math.PI * 2 * (pi / 16) + Math.random() * 0.2;
var speed = 10 + Math.random() * 10;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = 10 + Math.floor(Math.random() * 10);
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.88 + Math.random() * 0.06;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
enemyToDestroy.destroy();
enemies.splice(i, 1);
}
}
}
// Decrement timer and destroy when done
game.activeLaserBeamTimer--;
if (game.activeLaserBeamTimer <= 0) {
game.activeLaserBeam.destroy();
game.activeLaserBeam = null;
}
}
// Ulti cooldown logic
if (godMode) {
ultiReady = true;
ultiCooldown = 0;
ultiBtnOverlay.visible = false;
ultiBtn.text = "ULTI";
} else {
if (!ultiReady) {
ultiCooldown--;
if (ultiCooldown <= 0) {
ultiReady = true;
ultiBtnOverlay.visible = false;
} else {
// Show overlay and update text to show seconds left
ultiBtnOverlay.visible = true;
ultiBtn.text = "ULTI\n" + Math.ceil(ultiCooldown / 60) + "s";
}
}
if (ultiReady) {
ultiBtn.text = "ULTI";
ultiBtnOverlay.visible = false;
}
}
// Update armor overlay position and visibility
heroArmor.x = hero.x;
heroArmor.y = hero.y;
heroArmor.visible = shieldActive;
// Laser cannon cooldown logic (no button UI)
if (!laserReady) {
laserCooldown--;
if (laserCooldown <= 0) {
laserReady = true;
}
}
// Update shield/beam timers
if (shieldActive) {
shieldTimer--;
if (shieldTimer <= 0) {
shieldActive = false;
}
}
if (beamActive) {
beamTimer--;
if (beamTimer <= 0) {
beamActive = false;
}
}
// Update items (powerups)
for (var i = items.length - 1; i >= 0; i--) {
var item = items[i];
item.update();
// Remove if off screen
if (item.y > 2732 + 100) {
item.destroy();
items.splice(i, 1);
continue;
}
// Pickup by hero
if (item.intersects(hero)) {
if (item instanceof HealthPotion) {
// Health potion always gives 1 health (if not at max)
if (heroHealth < maxHealth) {
heroHealth = Math.min(maxHealth, heroHealth + 1);
updateHealthBar();
LK.effects.flashObject(hero, 0x44ff44, 400);
}
} else if (item instanceof ShieldItem) {
// If already at max health, treat as shield
if (heroHealth >= maxHealth) {
shieldActive = true;
shieldTimer = 360; // 6 seconds at 60fps
LK.effects.flashObject(hero, 0x00ffff, 400);
} else {
// If not at max health, ignore shield pickup (health potions now handled separately)
}
} else if (item instanceof BeamItem) {
beamActive = true;
beamTimer = 180; // 3 seconds at 60fps
LK.effects.flashObject(hero, 0xff00ff, 400);
}
item.destroy();
items.splice(i, 1);
continue;
}
}
// Hero shooting (auto-fire)
if (hero.shootCooldown <= 0) {
if (beamActive) {
// Beam: fire 3 bullets in spread
for (var bdir = -1; bdir <= 1; bdir++) {
var bullet = new HeroBullet();
bullet.x = hero.x + bdir * 40;
bullet.y = hero.y - hero.height / 2 - 20;
bullet.spread = bdir * 0.18; // radians
bullet.update = function (origUpdate, spread) {
return function () {
this.y += this.speed;
this.x += Math.sin(spread) * 18;
};
}(bullet.update, bullet.spread);
heroBullets.push(bullet);
game.addChild(bullet);
}
hero.shootCooldown = 6;
LK.getSound('laser').play();
} else {
var bullet = new HeroBullet();
bullet.x = hero.x;
bullet.y = hero.y - hero.height / 2 - 20;
heroBullets.push(bullet);
game.addChild(bullet);
hero.shootCooldown = 10;
LK.getSound('laser').play();
}
}
// Update hero bullets
for (var i = heroBullets.length - 1; i >= 0; i--) {
var b = heroBullets[i];
b.update();
// Remove if off screen
if (b.y < -60) {
b.destroy();
heroBullets.splice(i, 1);
continue;
}
// Check collision with enemies
for (var j = enemies.length - 1; j >= 0; j--) {
var e = enemies[j];
if (b.intersects(e)) {
// Enemy hit
LK.getSound('enemyHit').play();
score += 1;
scoreTxt.setText(score);
// Flash enemy
LK.effects.flashObject(e, 0xffffff, 200);
// Remove both or handle Enemy3/EnemyX hp
if (e instanceof Enemy3 || e instanceof EnemyX) {
e.hp--;
if (e.hp <= 0) {
// Play death animation before destroying Enemy3/EnemyX
var enemyToDestroy = e;
// Particle animation for Enemy3/EnemyX death (more particles, richer effect)
var particleCount = e instanceof EnemyX ? 48 : 24;
for (var pi = 0; pi < particleCount; pi++) {
var part = LK.getAsset(e instanceof EnemyX ? 'enemyX' : 'enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = enemyToDestroy.x;
part.y = enemyToDestroy.y;
part.width = (e instanceof EnemyX ? 48 : 32) + Math.random() * (e instanceof EnemyX ? 48 : 32);
part.height = (e instanceof EnemyX ? 48 : 32) + Math.random() * (e instanceof EnemyX ? 48 : 32);
part.alpha = 0.8 + Math.random() * 0.2;
var angle = Math.PI * 2 * (pi / particleCount) + Math.random() * 0.2;
var speed = (e instanceof EnemyX ? 24 : 16) + Math.random() * (e instanceof EnemyX ? 16 : 12);
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = (e instanceof EnemyX ? 32 : 16) + Math.floor(Math.random() * (e instanceof EnemyX ? 32 : 16));
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.90 + Math.random() * 0.04;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
// 35% chance to drop a health potion when Enemy3 is killed (not for EnemyX)
if (e instanceof Enemy3 && Math.random() < 0.35) {
var healthPotion = new HealthPotion();
healthPotion.x = enemyToDestroy.x;
healthPotion.y = enemyToDestroy.y;
items.push(healthPotion);
game.addChild(healthPotion);
}
enemyToDestroy.destroy();
enemies.splice(j, 1);
// If EnemyX was killed, drop 3 health potions and restart normal enemy spawning
if (e instanceof EnemyX) {
for (var hp = 0; hp < 3; hp++) {
var healthPotion = new HealthPotion();
// Spread potions horizontally a bit
healthPotion.x = enemyToDestroy.x - 60 + 60 * hp;
healthPotion.y = enemyToDestroy.y;
items.push(healthPotion);
game.addChild(healthPotion);
}
// Reset boss state and allow normal enemies to spawn again
enemyXSpawned = false;
bossMusicPlayed = false;
// Reset spawnTick so normal enemies start coming again, but slowly
spawnTick = 0;
// Optionally, set a delay before next normal spawn (e.g. 60 frames)
// spawnTick = -60;
}
} else {
// Flash for hit, but don't destroy
LK.effects.flashObject(e, 0x8e24aa, 120);
}
b.destroy();
heroBullets.splice(i, 1);
break;
} else {
b.destroy();
heroBullets.splice(i, 1);
// Play death animation before destroying normal enemies
var enemyToDestroy = e;
// Particle animation for normal enemy death (more particles, richer effect)
for (var pi = 0; pi < 16; pi++) {
var part = LK.getAsset(enemyToDestroy instanceof Enemy1 ? 'enemy1' : 'enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = enemyToDestroy.x;
part.y = enemyToDestroy.y;
part.width = 20 + Math.random() * 20;
part.height = 20 + Math.random() * 20;
part.alpha = 0.7 + Math.random() * 0.3;
var angle = Math.PI * 2 * (pi / 16) + Math.random() * 0.2;
var speed = 10 + Math.random() * 10;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = 10 + Math.floor(Math.random() * 10);
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.88 + Math.random() * 0.06;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
enemyToDestroy.destroy();
enemies.splice(j, 1);
break;
}
}
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// Remove if off screen
if (e.y > 2732 + 100) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Enemy shoots randomly
if (Math.random() < 0.008) {
spawnEnemyBullet(e, hero.x, hero.y);
}
// Check collision with hero
if (e.intersects(hero)) {
if (godMode) {
// In god mode, ignore all damage and just destroy enemy
enemies[i].destroy();
enemies.splice(i, 1);
continue;
}
if (!shieldActive) {
// Hero hit
LK.getSound('heroHit').play();
LK.effects.flashScreen(0xff0000, 800);
heroHealth--;
updateHealthBar();
if (heroHealth <= 0) {
LK.showGameOver();
return;
}
enemies[i].destroy();
enemies.splice(i, 1);
continue;
} else {
// Shield absorbs hit, destroy enemy
LK.effects.flashObject(hero, 0x00ffff, 200);
// Play death animation before destroying enemy killed by shield
var enemyToDestroy = e;
// Particle animation for shield kill (more particles, richer effect)
for (var pi = 0; pi < 16; pi++) {
var part = LK.getAsset(enemyToDestroy instanceof Enemy1 ? 'enemy1' : enemyToDestroy instanceof Enemy2 ? 'enemy2' : 'enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
part.x = enemyToDestroy.x;
part.y = enemyToDestroy.y;
part.width = 20 + Math.random() * 20;
part.height = 20 + Math.random() * 20;
part.alpha = 0.7 + Math.random() * 0.3;
var angle = Math.PI * 2 * (pi / 16) + Math.random() * 0.2;
var speed = 10 + Math.random() * 10;
var vx = Math.cos(angle) * speed;
var vy = Math.sin(angle) * speed;
part.life = 10 + Math.floor(Math.random() * 10);
part.update = function () {
this.x += vx;
this.y += vy;
this.life--;
this.alpha *= 0.88 + Math.random() * 0.06;
if (this.life <= 0) {
this.destroy();
}
};
game.addChild(part);
}
enemyToDestroy.destroy();
enemies.splice(i, 1);
continue;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var b = enemyBullets[i];
b.update();
// Remove if off screen
if (b.y < -60 || b.y > 2732 + 60 || b.x < -60 || b.x > 2048 + 60) {
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
// Check collision with hero
if (b.intersects(hero)) {
if (godMode) {
// In god mode, ignore all damage and just destroy bullet
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
if (!shieldActive) {
LK.getSound('heroHit').play();
LK.effects.flashScreen(0xff0000, 800);
heroHealth--;
updateHealthBar();
if (heroHealth <= 0) {
LK.showGameOver();
return;
}
b.destroy();
enemyBullets.splice(i, 1);
continue;
} else {
// Shield absorbs bullet
LK.effects.flashObject(hero, 0x00ffff, 120);
b.destroy();
enemyBullets.splice(i, 1);
continue;
}
}
}
// --- ENEMY SPAWN RATE BOOST LOGIC ---
if (typeof spawnRateBoostActive === "undefined") {
var spawnRateBoostActive = false;
var spawnRateBoostTimer = 0;
var nextBoostScore = 50;
var spawnIntervalNormal = 60;
var spawnIntervalBoost = 30;
}
// Boss mode: only spawn EnemyX, never normal enemies or items
if (typeof bossMode !== "undefined" && bossMode) {
spawnTick++;
// Only spawn EnemyX if not already present
if (!enemyXSpawned && spawnTick >= 30) {
var enemy = new EnemyX();
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = -80;
enemies.push(enemy);
game.addChild(enemy);
enemyXSpawned = true;
// Music already handled in menu
spawnTick = 0;
}
// Never spawn items or normal enemies in boss mode
} else {
// Check if we reached a new boost score (50, 150, 250, ...) and trigger boost
if (score >= nextBoostScore && !spawnRateBoostActive) {
spawnRateBoostActive = true;
spawnRateBoostTimer = 180; // 3 seconds at 60fps
// After 50, next is 150, then 250, 350, ...
if (nextBoostScore === 50) {
nextBoostScore = 150;
} else {
nextBoostScore += 100;
}
}
// Handle boost timer
if (spawnRateBoostActive) {
spawnRateBoostTimer--;
if (spawnRateBoostTimer <= 0) {
spawnRateBoostActive = false;
}
}
// Spawn enemies
spawnTick++;
// Use boosted or normal interval
// Make spawn rate scale more slowly with score (easier): decrease by 2 per 20 score, not 4 per 10
var spawnInterval = spawnRateBoostActive ? spawnIntervalBoost : Math.max(18, spawnIntervalNormal - Math.floor(score / 20) * 2); // Slower scaling for easier game
if (score < 250) {
if (spawnTick >= spawnInterval) {
// Calculate how many enemies to spawn based on score (1 + 1 per 50 score)
var numEnemies = 1 + Math.floor(score / 50);
for (var i = 0; i < numEnemies; i++) {
spawnEnemy();
}
// 1 in 8 chance to spawn an item
if (Math.random() < 0.125) {
spawnItem();
}
spawnTick = 0;
}
} else if (score >= 250) {
// Stop current music and play boss music when score passes 250
if (typeof bossMusicPlayed === "undefined") {
var bossMusicPlayed = false;
}
// --- Ensure EnemyX spawns immediately after passing 250 score before any other enemies ---
if (!enemyXSpawned) {
// Only spawn EnemyX if not already present
var enemy = new EnemyX();
enemy.x = 200 + Math.random() * (2048 - 400);
enemy.y = -80;
enemies.push(enemy);
game.addChild(enemy);
enemyXSpawned = true;
// Stop current music and play boss music when EnemyX spawns
if (!bossMusicPlayed) {
LK.stopMusic();
LK.playMusic('Boss');
bossMusicPlayed = true;
}
spawnTick = 0;
}
// Do NOT spawn any other enemies or items until EnemyX is killed
else {
// Wait until EnemyX is killed before resuming normal spawns
// (do nothing here)
}
}
}
};
A power core with electricity and blue lights. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A blue space ship with laser gun and powerful engines. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A shiny purple crystal. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A red winged alien with exoskeleton. . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A tiny green alien with spikes. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A strong yellow-white alien with horns. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A king yellow alien with black and white stripes and wings. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Neboula background. In-Game asset. 2d. High contrast. No shadows. Cinematic deep
A blue button. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A dark green alien with a gray orb above it. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A dark green alien boss with two gray orbs around it. On top it has a green crown. Has light gray stripes. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
A futuristic scoreboard tablet with blue and cyan colors. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat