User prompt
Make enemyz health 800
User prompt
When score is 451 in boss mode spawn enemyz
User prompt
When enemyx or enemyz get hit by ulti add to the score 50 points instead of 1
User prompt
Enable it for enemyx too
User prompt
When ulti is used against boss type enemies, add it in the score as 50 points
User prompt
When hero touches boss type enemies, enemy doesn't affect or gets hit
User prompt
Enemyz still doesn't spawn
User prompt
Enemyz didn't spawn after enemyx died
User prompt
In boss mode, first enemyx spawns after it died enemyz spawns, and after enemyx spawns. That goes like that
User prompt
Update enemyz image at help menu
User prompt
Use enemyZ asset for enemyz
User prompt
Place close, items and enemies buttons under the description text
User prompt
Only after 600 score it starts to spawn
User prompt
Also needs 3 hit to die
User prompt
Enemy4 doesn't shoot but when is near to the hero, explodes
User prompt
Add enemy4 with enemy4 asset
User prompt
When in boss mode enemyx is killed, summon enemyz
User prompt
Add a new boss type class called enemyZ
User prompt
In god mode enemies also come after enemyx is killed just like the normal mode
User prompt
When enemyx is killed, stop boss music and play bgmusic
User prompt
When score raches 600, start to increasing enemy spawning speed slowly
User prompt
I mean spawn 1 enemy per second after enemyx is killed
User prompt
I mean make it 60 ticks when enemyx is killed
User prompt
When starting to spawn enemies again, set spawnrate to normal like the beginning of the game
User prompt
If score is higher than 250 and enemyx is killed once, don't spawn enemyx again, instead start to spawn other enemies.
/**** * 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; }); // Enemy4: new enemy type, moves diagonally, bounces off screen edges, explodes when near hero, needs 3 hits to die var Enemy4 = Container.expand(function () { var self = Container.call(this); var enemySprite = self.attachAsset('Enemy4', { anchorX: 0.5, anchorY: 0.5 }); enemySprite.width = 180; enemySprite.height = 180; self.speedX = (Math.random() < 0.5 ? 1 : -1) * (7 + Math.random() * 3); self.speedY = 7 + Math.random() * 3; self.exploded = false; self.hp = 3; // Needs 3 hits to die self.update = function () { self.x += self.speedX; self.y += self.speedY; // Bounce off left/right edges if (self.x - enemySprite.width / 2 <= 0 && self.speedX < 0 || self.x + enemySprite.width / 2 >= 2048 && self.speedX > 0) { self.speedX *= -1; } // Explode if near hero (distance < 180px), only once if (!self.exploded && typeof hero !== "undefined") { var dx = self.x - hero.x; var dy = self.y - hero.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 180) { self.exploded = true; // Explosion effect: flash, particles, damage hero if not shielded/godmode LK.effects.flashObject(self, 0xff6600, 400); for (var pi = 0; pi < 18; pi++) { var part = LK.getAsset('Enemy4', { anchorX: 0.5, anchorY: 0.5 }); part.x = self.x; part.y = self.y; part.width = 24 + Math.random() * 24; part.height = 24 + Math.random() * 24; part.alpha = 0.7 + Math.random() * 0.3; var angle = Math.PI * 2 * (pi / 18) + Math.random() * 0.2; var speed = 12 + Math.random() * 10; var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = 12 + Math.floor(Math.random() * 8); 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(); } }; if (typeof game !== "undefined") game.addChild(part); } // Damage hero if not shielded/godmode if (!godMode && !shieldActive) { LK.getSound('heroHit').play(); LK.effects.flashScreen(0xff6600, 800); if (typeof heroHealth !== "undefined") heroHealth--; if (typeof updateHealthBar === "function") updateHealthBar(); if (typeof updateDemoHealthSquares === "function") updateDemoHealthSquares(); if (typeof heroHealth !== "undefined" && heroHealth <= 0) { LK.getSound('Death').play(); LK.showGameOver(); return; } } else if (!godMode && shieldActive) { LK.effects.flashObject(hero, 0x00ffff, 200); } // Destroy self after explosion self.destroy(); } } }; 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(); updateDemoHealthSquares && updateDemoHealthSquares(); if (heroHealth <= 0) { LK.getSound('Death').play(); 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; }); // EnemyZ: New boss type, appears as a large, fast, zig-zagging boss with high HP and double beam attack var EnemyZ = Container.expand(function () { var self = Container.call(this); // Use enemyZ asset, large size var enemySprite = self.attachAsset('EnemyZ', { anchorX: 0.5, anchorY: 0.5 }); enemySprite.width = 520; enemySprite.height = 520; enemySprite.alpha = 0.99; self.speed = 10 + Math.random() * 3; self.hp = 350; // High HP, but less than EnemyX in boss mode self.t = Math.random() * 2; self.zigzagAmplitude = 320 + Math.random() * 80; self.zigzagSpeed = 0.018 + Math.random() * 0.008; self.baseX = 2048 / 2; self.directionY = 1; self.shootTick = 0; self.beamCooldown = 0; self.update = function () { // Zig-zag movement, stays in upper half of screen self.t += self.zigzagSpeed; self.x = self.baseX + Math.sin(self.t * 2.5) * self.zigzagAmplitude; // Restrict vertical movement: only allow y to increase up to a certain limit, then bounce back up var upperY = 80 + enemySprite.height / 2; var lowerY = 600 + enemySprite.height / 2; self.y += self.speed * self.directionY * 0.5; if (self.y >= lowerY) { self.y = lowerY; self.directionY = -1; } else if (self.y <= upperY) { self.y = upperY; self.directionY = 1; } // --- Shooting logic: fires 5-way spread every 48-72 ticks --- self.shootTick = (self.shootTick || 0) + 1; self.beamCooldown = (self.beamCooldown || 0) - 1; if (self.shootTick >= 48 + Math.floor(Math.random() * 25)) { if (typeof game !== "undefined" && typeof hero !== "undefined") { // Shoot 5-way spread for (var i = -2; i <= 2; 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 * 60; var dy = hero.y - bullet.y; var len = Math.sqrt(dx * dx + dy * dy); if (len > 0) { bullet.speedX = dx / len * 22; bullet.speedY = dy / len * 22; } enemyBullets.push(bullet); game.addChild(bullet); LK.getSound('enemyShoot').play(); } } self.shootTick = 0; } // --- Double beam attack: two beams at once, more frequent than EnemyX --- if (self.beamCooldown <= 0 && Math.random() < 0.025) { if (typeof game !== "undefined") { // Visual: two vertical beams, one at self.x-100, one at self.x+100 for (var bx = -100; bx <= 100; bx += 200) { var beam = LK.getAsset('beamItem', { anchorX: 0.5, anchorY: 0, x: self.x + bx, y: self.y + enemySprite.height / 2 }); beam.width = 100; beam.height = 2732 - (self.y + enemySprite.height / 2); beam.alpha = 0.42 + Math.random() * 0.10; game.addChild(beam); // Beam effect: damage hero if in column for 48 frames, only first 16 frames "active" beam.life = 48; beam.activeFrames = 16; beam.update = function () { this.life--; this.alpha = 0.32 + Math.random() * 0.18; if (this.life > 48 - 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 === 48 - this.activeFrames + 1) { LK.getSound('heroHit').play(); LK.effects.flashScreen(0x3399ff, 800); heroHealth--; updateHealthBar(); updateDemoHealthSquares && updateDemoHealthSquares(); if (heroHealth <= 0) { LK.getSound('Death').play(); LK.showGameOver(); return; } } else if (!godMode && shieldActive && this.life === 48 - this.activeFrames + 1) { LK.effects.flashObject(hero, 0x00ffff, 200); } } } } if (this.life <= 0) { this.destroy(); } }; enemyBullets.push(beam); LK.getSound('laser').play(); } // Set cooldown for next double beam (more frequent than EnemyX) self.beamCooldown = 180 + Math.floor(Math.random() * 90); } } }; 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; }); // Shield item class var ShieldItem = Container.expand(function () { var self = Container.call(this); var shieldSprite = self.attachAsset('shieldItem', { anchorX: 0.5, anchorY: 0.5 }); shieldSprite.width = 90; shieldSprite.height = 90; shieldSprite.alpha = 0.95; // Make it much brighter/less transparent self.update = function () { self.y += 12; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Add 6 healthbar assets to the screen (for demo/test, not for health UI) var greenSquares = []; // (defer adding to game until after background is added) for (var i = 0; i < 6; i++) { var square = LK.getAsset('Healthbar', { anchorX: 0, anchorY: 1, x: 60 + i * 70, y: 2732 - 60, width: 60, height: 60, tint: 0x44ff44 }); greenSquares.push(square); } // Helper to update demo health squares to match heroHealth function updateDemoHealthSquares() { for (var i = 0; i < greenSquares.length; i++) { if (i < heroHealth) { greenSquares[i].visible = true; } else { greenSquares[i].visible = false; } } } // separate asset for health potion // Play menu music and stop background music when menu is shown // --- MENU OVERLAY --- // Add menu asset as background in menu, and background asset in game var menuBgSprite = LK.getAsset('Menu', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); menuBgSprite.width = 2048; menuBgSprite.height = 2732; game.addChild(menuBgSprite); 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; backgroundSprite.visible = false; // Only show in game, not in menu game.addChild(backgroundSprite); // Add green squares after background so they are in front for (var i = 0; i < greenSquares.length; i++) { game.addChild(greenSquares[i]); } 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('Button', { anchorX: 0.5, anchorY: 0.5 }); playBtnBg.width = 600; playBtnBg.height = 220; playBtnBg.alpha = 0.7; 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 god mode and boss mode play buttons first, so their positions are available for music button placement var godMode = false; var bossMode = false; var godPlayBtnBg = LK.getAsset('Button', { anchorX: 0.5, anchorY: 0.5 }); godPlayBtnBg.width = 600; godPlayBtnBg.height = 220; godPlayBtnBg.alpha = 0.7; // Place god mode button below the play button (with margin) godPlayBtnBg.x = 2048 / 2; godPlayBtnBg.y = 700 + 220 / 2 + 60 + godPlayBtnBg.height / 2; // below playBtnBg menuOverlay.addChild(godPlayBtnBg); var godPlayBtn = new Text2("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); var bossPlayBtnBg = LK.getAsset('Button', { anchorX: 0.5, anchorY: 0.5 }); bossPlayBtnBg.width = 600; bossPlayBtnBg.height = 220; bossPlayBtnBg.alpha = 0.7; 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 music toggle button to menu var musicOn = true; // Place music button below boss mode button (with margin) var musicBtnBg = LK.getAsset('Button', { anchorX: 0.5, anchorY: 0.5 }); musicBtnBg.width = 340; musicBtnBg.height = 140; musicBtnBg.alpha = 0.7; musicBtnBg.x = 2048 / 2; musicBtnBg.y = bossPlayBtnBg.y + bossPlayBtnBg.height / 2 + 60 + musicBtnBg.height / 2; 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 = musicBtnBg.y; menuOverlay.addChild(musicBtn); // Add help button below music button var helpBtnBg = LK.getAsset('Button', { anchorX: 0.5, anchorY: 0.5 }); helpBtnBg.width = 340; helpBtnBg.height = 140; helpBtnBg.alpha = 0.7; helpBtnBg.x = 2048 / 2; helpBtnBg.y = musicBtnBg.y + musicBtnBg.height / 2 + 60 + helpBtnBg.height / 2; menuOverlay.addChild(helpBtnBg); var helpBtn = new Text2("HELP", { size: 80, fill: 0x00EAFF, fontWeight: "bold" }); helpBtn.anchor.set(0.5, 0.5); helpBtn.x = 2048 / 2; helpBtn.y = helpBtnBg.y; menuOverlay.addChild(helpBtn); // Add menu overlay to game game.addChild(menuOverlay); // --- HELP MENU POPUP --- var helpMenuOverlay = null; function showHelpMenu() { if (helpMenuOverlay && helpMenuOverlay.visible) return; if (helpMenuOverlay) { helpMenuOverlay.visible = true; return; } helpMenuOverlay = new Container(); helpMenuOverlay.zIndex = 20000; // Help menu background using helpbackground asset var bg = LK.getAsset('Helpbackground', { anchorX: 0, anchorY: 0, x: 0, y: 0 }); bg.width = 2048; bg.height = 2732; bg.alpha = 1.0; helpMenuOverlay.addChild(bg); // Help title var helpTitle = new Text2("HELP", { size: 120, fill: "#fff", fontWeight: "bold" }); helpTitle.anchor.set(0.5, 0); helpTitle.x = 2048 / 2; helpTitle.y = 320; helpMenuOverlay.addChild(helpTitle); // --- PAGE SYSTEM --- var helpPage = 0; // 0 = enemies, 1 = items // Enemy types info for help menu var enemyTypes = [{ asset: 'enemy1', name: 'Enemy 1', desc: 'Standard enemy. Moves straight down. Easy to destroy.' }, { asset: 'enemy2', name: 'Enemy 2', desc: 'Sine-wave enemy. Moves in a wavy pattern. Harder to hit.' }, { asset: 'enemy3', name: 'Enemy 3', desc: 'Strong enemy. Takes 5 hits to destroy. Large and slow.' }, { asset: 'Enemy4', name: 'Enemy 4', desc: 'Diagonal bouncer. Moves diagonally, bounces off edges, and explodes when near the hero!' }, { asset: 'enemyX', name: 'Boss Enemy X', desc: 'Boss enemy. Appears after 250 score or in Boss Mode. Very tough!' }, { asset: 'EnemyZ', name: 'Boss Enemy Z', desc: 'Boss enemy. Zig-zags, shoots double beams, and is very fast!' }]; // Item types info for help menu var itemTypes = [{ asset: 'healthPotion', name: 'Health Potion', desc: 'Restores 1 health. Pick up to heal. If at max health, does nothing.' }, { asset: 'shieldItem', name: 'Shield', desc: 'Grants a shield for 6 seconds if at max health. Absorbs one hit from enemies or bullets.' }, { asset: 'beamItem', name: 'Beam Powerup', desc: 'Enables triple-shot for a short time. Fires 3 bullets in a spread.' }]; // --- CONTAINER FOR PAGE CONTENT --- var pageContent = new Container(); helpMenuOverlay.addChild(pageContent); // --- RENDER PAGE FUNCTION --- function renderHelpPage() { // Remove all children from pageContent while (pageContent.children.length > 0) { pageContent.removeChild(pageContent.children[0]); } // Title helpTitle.setText(helpPage === 0 ? "HELP" : "ITEMS"); // Layout: vertical list, left-aligned for both image and text var startY = 500; var spacingY = 340; var leftMargin = 220; var imageTextGap = 40; var data = helpPage === 0 ? enemyTypes : itemTypes; for (var i = 0; i < data.length; i++) { var et = data[i]; // Image var img = LK.getAsset(et.asset, { anchorX: 0, anchorY: 0, x: leftMargin, y: startY + i * spacingY, width: et.asset === 'enemyX' ? 180 : 120, height: et.asset === 'enemyX' ? 180 : 120 }); pageContent.addChild(img); // Name var nameTxt = new Text2(et.name, { size: 80, fill: "#fff", fontWeight: "bold" }); nameTxt.anchor.set(0, 0); nameTxt.x = leftMargin + (et.asset === 'enemyX' ? 180 : 120) + imageTextGap; nameTxt.y = startY + i * spacingY + 10; pageContent.addChild(nameTxt); // Description var descTxt = new Text2(et.desc, { size: 60, fill: "#fff", align: "left" }); descTxt.anchor.set(0, 0); descTxt.x = leftMargin + (et.asset === 'enemyX' ? 180 : 120) + imageTextGap; descTxt.y = startY + i * spacingY + 100; pageContent.addChild(descTxt); } } renderHelpPage(); // --- PAGE NAVIGATION BUTTONS --- // Place under the last description text // Compute button Y based on number of entries in current help page var numEntries = helpPage === 0 ? enemyTypes.length : itemTypes.length; var lastDescY = 500 + (numEntries - 1) * 340 + 100 + 120; // 100 for desc offset, 120 for some margin // Close button (centered, first so it's under others visually) var closeBtnBg = LK.getAsset('Button', { anchorX: 0.5, anchorY: 0.5 }); closeBtnBg.width = 340; closeBtnBg.height = 140; closeBtnBg.alpha = 0.7; closeBtnBg.x = 2048 / 2; closeBtnBg.y = lastDescY; helpMenuOverlay.addChild(closeBtnBg); var closeBtn = new Text2("CLOSE", { size: 80, fill: 0x00EAFF, fontWeight: "bold" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = 2048 / 2; closeBtn.y = lastDescY; helpMenuOverlay.addChild(closeBtn); // Items button (right) var nextBtnBg = LK.getAsset('Button', { anchorX: 0.5, anchorY: 0.5 }); nextBtnBg.width = 340; nextBtnBg.height = 140; nextBtnBg.alpha = 0.7; nextBtnBg.x = 2048 / 2 + 400; nextBtnBg.y = lastDescY; helpMenuOverlay.addChild(nextBtnBg); var nextBtn = new Text2("ITEMS", { size: 60, fill: 0x00EAFF, fontWeight: "bold" }); nextBtn.anchor.set(0.5, 0.5); nextBtn.x = nextBtnBg.x; nextBtn.y = nextBtnBg.y; helpMenuOverlay.addChild(nextBtn); // Enemies button (left) var prevBtnBg = LK.getAsset('Button', { anchorX: 0.5, anchorY: 0.5 }); prevBtnBg.width = 340; prevBtnBg.height = 140; prevBtnBg.alpha = 0.7; prevBtnBg.x = 2048 / 2 - 400; prevBtnBg.y = lastDescY; helpMenuOverlay.addChild(prevBtnBg); var prevBtn = new Text2("ENEMIES", { size: 60, fill: 0x00EAFF, fontWeight: "bold" }); prevBtn.anchor.set(0.5, 0.5); prevBtn.x = prevBtnBg.x; prevBtn.y = prevBtnBg.y; helpMenuOverlay.addChild(prevBtn); // Dismiss help menu on close button press, and handle page navigation helpMenuOverlay.down = function (x, y, obj) { // Close var dx = x - closeBtn.x; var dy = y - closeBtn.y; if (Math.abs(dx) <= closeBtnBg.width / 2 && Math.abs(dy) <= closeBtnBg.height / 2) { helpMenuOverlay.visible = false; return; } // Prev (ENEMIES) var pdx = x - prevBtn.x; var pdy = y - prevBtn.y; if (Math.abs(pdx) <= prevBtnBg.width / 2 && Math.abs(pdy) <= prevBtnBg.height / 2) { if (helpPage !== 0) { helpPage = 0; renderHelpPage(); } return; } // Next (ITEMS) var ndx = x - nextBtn.x; var ndy = y - nextBtn.y; if (Math.abs(ndx) <= nextBtnBg.width / 2 && Math.abs(ndy) <= nextBtnBg.height / 2) { if (helpPage !== 1) { helpPage = 1; renderHelpPage(); } return; } }; game.addChild(helpMenuOverlay); } // 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; if (typeof menuBgSprite !== "undefined") menuBgSprite.visible = false; if (typeof backgroundSprite !== "undefined") backgroundSprite.visible = true; 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; if (typeof menuBgSprite !== "undefined") menuBgSprite.visible = false; if (typeof backgroundSprite !== "undefined") backgroundSprite.visible = 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; if (typeof menuBgSprite !== "undefined") menuBgSprite.visible = false; if (typeof backgroundSprite !== "undefined") backgroundSprite.visible = 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; } // Check if help button was pressed if (typeof helpBtn !== "undefined" && typeof helpBtnBg !== "undefined") { var hdx = x - helpBtn.x; var hdy = y - helpBtn.y; if (Math.abs(hdx) <= helpBtnBg.width / 2 && Math.abs(hdy) <= helpBtnBg.height / 2) { // Show help popup (custom help menu) showHelpMenu(); 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); // --- Health bar squares (vertical, left bottom) --- var healthBarVSquares = []; var squareSpacing = 12; var squareSize = 60; var baseX = 60; var baseY = 2732 - 60; for (var i = 0; i < maxHealth; i++) { var square = LK.getAsset('Healthbar', { anchorX: 0, anchorY: 1, x: baseX + i * (squareSize + squareSpacing), y: baseY, width: squareSize, height: squareSize, tint: 0x44ff44 }); LK.gui.bottomLeft.addChild(square); healthBarVSquares.push(square); } 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; } } } updateDemoHealthSquares && updateDemoHealthSquares(); 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; // Track if EnemyX has been killed after 250 score (not in boss mode) var enemyXKilledAfter250 = 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() { // 10% Enemy4 (only if score >= 600), 20% Enemy3, 35% Enemy1, 35% Enemy2 var rand = Math.random(); var enemy; if (rand < 0.35) { enemy = new Enemy1(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; } else if (rand < 0.7) { enemy = new Enemy2(); enemy.baseX = 200 + Math.random() * (2048 - 400); enemy.x = enemy.baseX; enemy.y = -80; enemy.t = Math.random() * 2; } else if (rand < 0.9) { enemy = new Enemy3(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; } else { // Only spawn Enemy4 if score >= 600 if (score >= 600) { enemy = new Enemy4(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; } else { // If not allowed, fallback to Enemy1 enemy = new Enemy1(); 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) { // Always follow mouse if not dragging and not in menu if (!dragNode && !(typeof menuActive !== "undefined" && menuActive)) { // Clamp hero inside screen var hw = hero.width / 2; var hh = hero.height / 2; var nx = Math.max(hw, Math.min(2048 - hw, x)); var ny = Math.max(hh + 100, Math.min(2732 - hh, y)); hero.x = nx; hero.y = ny; return; } 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; } } // --- Make hero follow mouse automatically at game start (PC only) --- if (typeof window !== "undefined" && window.addEventListener) { window.addEventListener("mousemove", function (evt) { // Only follow if not in menu and not dragging if (typeof menuActive !== "undefined" && menuActive) return; if (dragNode) return; // Get bounding rect of canvas var canvas = LK.getCanvas ? LK.getCanvas() : document.querySelector("canvas") || null; if (!canvas) return; var rect = canvas.getBoundingClientRect(); // Convert mouse coordinates to game coordinates var scaleX = 2048 / rect.width; var scaleY = 2732 / rect.height; var x = (evt.clientX - rect.left) * scaleX; var y = (evt.clientY - rect.top) * scaleY; handleMove(x, y, { event: evt }); }); } // 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; // Play ulti sound LK.getSound('Ulti').play(); // 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 damage to EnemyX in both boss mode and normal mode 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(); updateDemoHealthSquares && updateDemoHealthSquares(); 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/Enemy4 hp if (e instanceof Enemy3 || e instanceof EnemyX || e instanceof Enemy4) { e.hp--; if (e.hp <= 0) { // Play death animation before destroying Enemy3/EnemyX/Enemy4 var enemyToDestroy = e; // Particle animation for Enemy3/EnemyX/Enemy4 death (more particles, richer effect) var particleCount = e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 24 : 16; var assetName = e instanceof EnemyX ? 'enemyX' : e instanceof Enemy3 ? 'enemy3' : 'Enemy4'; for (var pi = 0; pi < particleCount; pi++) { var part = LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); part.x = enemyToDestroy.x; part.y = enemyToDestroy.y; part.width = (e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 32 : 24) + Math.random() * (e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 32 : 24); part.height = (e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 32 : 24) + Math.random() * (e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 32 : 24); 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 : e instanceof Enemy3 ? 16 : 12) + Math.random() * (e instanceof EnemyX ? 16 : e instanceof Enemy3 ? 12 : 8); var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = (e instanceof EnemyX ? 32 : e instanceof Enemy3 ? 16 : 10) + Math.floor(Math.random() * (e instanceof EnemyX ? 32 : e instanceof Enemy3 ? 16 : 8)); 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/Enemy4) 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); } // --- Summon EnemyZ if in boss mode --- if (typeof bossMode !== "undefined" && bossMode) { // Summon EnemyZ at center top var enemyZ = new EnemyZ(); enemyZ.x = 2048 / 2; enemyZ.y = -80; enemies.push(enemyZ); game.addChild(enemyZ); } // Reset boss state and allow normal enemies to spawn again enemyXSpawned = false; bossMusicPlayed = false; // Stop boss music and play bgmusic when EnemyX is killed LK.stopMusic(); LK.playMusic('bgmusic'); // If score >= 250, mark EnemyX as killed so it never spawns again (not in boss mode) if (score >= 250 && !(typeof bossMode !== "undefined" && bossMode)) { enemyXKilledAfter250 = true; } // Reset spawnTick so normal enemies start coming again, but slowly spawnTick = 0; // Reset spawnIntervalNormal to its original value when resuming normal enemy spawns spawnIntervalNormal = 60; // 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(); updateDemoHealthSquares && updateDemoHealthSquares(); if (heroHealth <= 0) { LK.getSound('Death').play(); 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(); updateDemoHealthSquares && updateDemoHealthSquares(); if (heroHealth <= 0) { LK.getSound('Death').play(); 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; } if (!enemyXKilledAfter250) { // --- 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) } } else { // EnemyX has been killed after 250 score, resume normal enemy and item spawns spawnTick++; // After score reaches 600, start to increase enemy spawn speed slowly var postBossSpawnInterval = 60; if (score >= 600) { // Decrease interval by 1 every 60 score above 600, minimum 18 postBossSpawnInterval = Math.max(18, 60 - Math.floor((score - 600) / 60)); } // Always spawn exactly 1 enemy per postBossSpawnInterval ticks after EnemyX is killed post-250 if (spawnTick >= postBossSpawnInterval) { spawnEnemy(); // 1 in 8 chance to spawn an item if (Math.random() < 0.125) { spawnItem(); } spawnTick = 0; } } // In god mode, also allow enemies to spawn after EnemyX is killed, just like normal mode if (godMode && enemyXKilledAfter250) { spawnTick++; var postBossSpawnInterval = 60; if (score >= 600) { postBossSpawnInterval = Math.max(18, 60 - Math.floor((score - 600) / 60)); } if (spawnTick >= postBossSpawnInterval) { spawnEnemy(); if (Math.random() < 0.125) { spawnItem(); } spawnTick = 0; } } } } };
===================================================================
--- original.js
+++ change.js
@@ -724,9 +724,9 @@
asset: 'enemyX',
name: 'Boss Enemy X',
desc: 'Boss enemy. Appears after 250 score or in Boss Mode. Very tough!'
}, {
- asset: 'enemy3',
+ asset: 'EnemyZ',
name: 'Boss Enemy Z',
desc: 'Boss enemy. Zig-zags, shoots double beams, and is very fast!'
}];
// Item types info for help menu
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