/**** * 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 += 6; }; 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 = 2 + Math.random() * 1; 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 = 3.5 + Math.random() * 1.5; self.waveAmplitude = 120 + Math.random() * 80; self.waveSpeed = 0.012 + Math.random() * 0.005; 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 = 3 + Math.random() * 1; 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) * (3.5 + Math.random() * 1.5); self.speedY = 3.5 + Math.random() * 1.5; self.exploded = false; self.hp = 3; // Needs 3 hits to die self.update = function () { // --- Enemy4 follows the hero --- // Only follow if hero exists and Enemy4 is not exploded if (typeof hero !== "undefined" && !self.exploded) { // Calculate direction vector to hero var dx = hero.x - self.x; var dy = hero.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); // Set speed to always move at a fixed speed toward the hero var speed = 8; // Enemy4's following speed (slower movement) if (dist > 0) { self.speedX = dx / dist * speed; self.speedY = dy / dist * speed; } } 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; } // --- Enemy4 explodes and deals 2 damage when near or colliding with hero --- if (typeof hero !== "undefined" && !self.exploded && ( // Collides with hero self.intersects(hero) || // Or is "near" hero (distance < 120px) Math.abs(self.x - hero.x) < 120 && Math.abs(self.y - hero.y) < 120)) { self.exploded = true; // Tiny flash/explosion effect on Enemy4 itself LK.effects.flashObject(self, 0xffffff, 120); // Only deal damage if not in godMode or shieldActive if (!godMode && !shieldActive) { LK.getSound('heroHit').play(); LK.effects.flashScreen(0xff0000, 800); if (typeof heroHealth !== "undefined") { heroHealth -= 2; if (heroHealth < 0) heroHealth = 0; if (typeof updateHealthBar === "function") updateHealthBar(); if (typeof updateDemoHealthSquares === "function") updateDemoHealthSquares(); if (heroHealth <= 0) { LK.getSound('Death').play(); LK.showGameOver(); return; } } } else if (shieldActive) { LK.effects.flashObject(hero, 0x00ffff, 200); } // Play explosion animation (particles) var particleCount = 16; for (var pi = 0; pi < particleCount; 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.8 + Math.random() * 0.2; var angle = Math.PI * 2 * (pi / particleCount) + Math.random() * 0.2; var speed = 12 + Math.random() * 8; 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(); } }; if (typeof game !== "undefined") game.addChild(part); } // Remove Enemy4 from game/enemies array on next update if (typeof self.destroy === "function") self.destroy(); // Remove from enemies[] in main game.update (handled by main loop) return; } // Enemy4 now ignores proximity to hero and does not explode or interact when flying through the hero }; 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 = 8; 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.3; // even 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 * 10; bullet.speedY = dy / len * 10; } 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 = 540 + 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 = 4 + Math.random() * 1.5; self.hp = Math.round((typeof bossMode !== "undefined" && bossMode ? 500 : 250) * 8 / 5); // Set EnemyZ health to 8/5 of EnemyX health self.t = Math.random() * 2; self.zigzagAmplitude = 320 + Math.random() * 80; self.zigzagSpeed = 0.009 + Math.random() * 0.004; 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.3; 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 * 11; bullet.speedY = dy / len * 11; } 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 = 300 + 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 += 8; }; 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 = -16; 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 += 6; }; 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; var enemyZSpawned = false; // Track if EnemyZ is present in boss mode // 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 = 12; 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 = 20; 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]; // In boss mode, if EnemyZ is killed, allow next boss to spawn if (typeof bossMode !== "undefined" && bossMode && e instanceof EnemyZ && e.hp <= 1 && b.intersects(e)) { enemyZSpawned = false; spawnTick = 0; bossNextToSpawn = "X"; // Always alternate to X after Z dies } 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/EnemyZ hp if (e instanceof Enemy3 || e instanceof EnemyX || e instanceof Enemy4 || e instanceof EnemyZ) { e.hp--; if (e.hp <= 0) { // Play death animation before destroying Enemy3/EnemyX/Enemy4/EnemyZ var enemyToDestroy = e; // --- Enemy4 explosion: NO damage to hero when destroyed by bullets, laser, or shield --- // (intentionally left blank, Enemy4 does not deal damage when destroyed by bullets, laser, or shield) // Particle animation for Enemy3/EnemyX/Enemy4/EnemyZ death (more particles, richer effect) var particleCount = e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 24 : e instanceof EnemyZ ? 48 : 16; var assetName = e instanceof EnemyX ? 'enemyX' : e instanceof Enemy3 ? 'enemy3' : e instanceof EnemyZ ? 'EnemyZ' : '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 : e instanceof EnemyZ ? 48 : 24) + Math.random() * (e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 32 : e instanceof EnemyZ ? 48 : 24); part.height = (e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 32 : e instanceof EnemyZ ? 48 : 24) + Math.random() * (e instanceof EnemyX ? 48 : e instanceof Enemy3 ? 32 : e instanceof EnemyZ ? 48 : 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 : e instanceof EnemyZ ? 24 : 12) + Math.random() * (e instanceof EnemyX ? 16 : e instanceof Enemy3 ? 12 : e instanceof EnemyZ ? 16 : 8); var vx = Math.cos(angle) * speed; var vy = Math.sin(angle) * speed; part.life = (e instanceof EnemyX ? 32 : e instanceof Enemy3 ? 16 : e instanceof EnemyZ ? 32 : 10) + Math.floor(Math.random() * (e instanceof EnemyX ? 32 : e instanceof Enemy3 ? 16 : e instanceof EnemyZ ? 32 : 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/EnemyZ) 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); } // --- In boss mode, alternate to EnemyZ after EnemyX dies if (typeof bossMode !== "undefined" && bossMode) { enemyXSpawned = false; enemyZSpawned = false; // Allow next boss to spawn (EnemyZ) spawnTick = 0; bossNextToSpawn = "Z"; // Always alternate to Z after X dies // Music continues } else { // 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; } } // If EnemyZ was killed, alternate to EnemyX after Z dies (boss mode) if (e instanceof EnemyZ && typeof bossMode !== "undefined" && bossMode) { enemyZSpawned = false; spawnTick = 0; bossNextToSpawn = "X"; // Always alternate to X after Z dies } } 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]; if (typeof bossMode !== "undefined" && bossMode && e instanceof EnemyZ && e.hp <= 0) { enemyZSpawned = false; spawnTick = 0; bossNextToSpawn = "X"; // Always alternate to X after Z dies } 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 // Prevent hero from affecting or being affected by boss type enemies (EnemyX, EnemyZ) if (e instanceof EnemyX || e instanceof EnemyZ) { // Do nothing: hero does not affect or get hit by boss type enemies continue; } 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]; if (typeof bossMode !== "undefined" && bossMode && b instanceof EnemyBullet) { // Check if this bullet killed EnemyZ (shouldn't happen, but for completeness) for (var j = enemies.length - 1; j >= 0; j--) { var e = enemies[j]; if (e instanceof EnemyZ && e.hp <= 0) { enemyZSpawned = false; spawnTick = 0; bossNextToSpawn = "X"; // Always alternate to X after Z dies } } } 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 = 90; var spawnIntervalBoost = 60; } // Boss mode: alternate EnemyX and EnemyZ, never normal enemies or items if (typeof bossMode !== "undefined" && bossMode) { spawnTick++; // Track which boss to spawn next: "X" or "Z" if (typeof bossNextToSpawn === "undefined") bossNextToSpawn = "X"; if (!enemyXSpawned && !enemyZSpawned && spawnTick >= 30) { var enemy; // Special case: at score 451, always spawn EnemyZ regardless of alternation if (score === 451) { enemy = new EnemyZ(); enemy.x = 2048 / 2; enemy.y = -80; enemies.push(enemy); game.addChild(enemy); enemyZSpawned = true; bossNextToSpawn = "X"; // After Z, alternate to X spawnTick = 0; } else if (bossNextToSpawn === "X") { enemy = new EnemyX(); enemy.x = 200 + Math.random() * (2048 - 400); enemy.y = -80; enemies.push(enemy); game.addChild(enemy); enemyXSpawned = true; // Do not alternate bossNextToSpawn here; alternate only on death spawnTick = 0; } else if (bossNextToSpawn === "Z") { enemy = new EnemyZ(); enemy.x = 2048 / 2; enemy.y = -80; enemies.push(enemy); game.addChild(enemy); enemyZSpawned = true; // Do not alternate bossNextToSpawn here; alternate only on death 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 (score < 800 && spawnTick >= postBossSpawnInterval) { spawnEnemy(); // 1 in 8 chance to spawn an item if (Math.random() < 0.125) { spawnItem(); } spawnTick = 0; } else if (score === 800 && !enemyZSpawned) { // Spawn EnemyZ at score 800 var enemy = new EnemyZ(); enemy.x = 2048 / 2; enemy.y = -80; enemies.push(enemy); game.addChild(enemy); enemyZSpawned = true; // Optionally play boss music if not already playing if (typeof bossMusicPlayed === "undefined" || !bossMusicPlayed) { LK.stopMusic(); LK.playMusic('Boss'); bossMusicPlayed = true; } 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 (score < 800 && spawnTick >= postBossSpawnInterval) { spawnEnemy(); if (Math.random() < 0.125) { spawnItem(); } spawnTick = 0; } } } } };
===================================================================
--- original.js
+++ change.js
@@ -16,9 +16,9 @@
beamSprite.width = 90;
beamSprite.height = 90;
beamSprite.alpha = 0.95;
self.update = function () {
- self.y += 12;
+ self.y += 6;
};
return self;
});
// Enemy type 1: moves straight down
@@ -29,9 +29,9 @@
anchorY: 0.5
});
enemySprite.width = 200;
enemySprite.height = 200;
- self.speed = 8 + Math.random() * 4;
+ self.speed = 2 + Math.random() * 1;
self.update = function () {
self.y += self.speed;
};
return self;
@@ -44,11 +44,11 @@
anchorY: 0.5
});
enemySprite.width = 160;
enemySprite.height = 160;
- self.speed = 7 + Math.random() * 3;
+ self.speed = 3.5 + Math.random() * 1.5;
self.waveAmplitude = 120 + Math.random() * 80;
- self.waveSpeed = 0.02 + Math.random() * 0.01;
+ self.waveSpeed = 0.012 + Math.random() * 0.005;
self.baseX = 0;
self.t = 0;
self.update = function () {
self.y += self.speed;
@@ -65,9 +65,9 @@
anchorY: 0.5
});
enemySprite.width = 260;
enemySprite.height = 260;
- self.speed = 6 + Math.random() * 2;
+ self.speed = 3 + Math.random() * 1;
self.hp = 5;
self.update = function () {
self.y += self.speed;
};
@@ -81,10 +81,10 @@
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.speedX = (Math.random() < 0.5 ? 1 : -1) * (3.5 + Math.random() * 1.5);
+ self.speedY = 3.5 + Math.random() * 1.5;
self.exploded = false;
self.hp = 3; // Needs 3 hits to die
self.update = function () {
// --- Enemy4 follows the hero ---
@@ -94,9 +94,9 @@
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Set speed to always move at a fixed speed toward the hero
- var speed = 18; // Enemy4's following speed (increased for faster movement)
+ var speed = 8; // Enemy4's following speed (slower movement)
if (dist > 0) {
self.speedX = dx / dist * speed;
self.speedY = dy / dist * speed;
}
@@ -178,9 +178,9 @@
anchorX: 0.5,
anchorY: 0.5
});
self.speedX = 0;
- self.speedY = 16;
+ self.speedY = 8;
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
};
@@ -233,9 +233,9 @@
// 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
+ self.y += self.speed * self.directionY * 0.3; // even 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;
@@ -258,10 +258,10 @@
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;
+ bullet.speedX = dx / len * 10;
+ bullet.speedY = dy / len * 10;
}
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
@@ -319,9 +319,9 @@
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);
+ self.beamCooldown = 540 + Math.floor(Math.random() * 180);
}
}
};
return self;
@@ -336,13 +336,13 @@
});
enemySprite.width = 520;
enemySprite.height = 520;
enemySprite.alpha = 0.99;
- self.speed = 10 + Math.random() * 3;
+ self.speed = 4 + Math.random() * 1.5;
self.hp = Math.round((typeof bossMode !== "undefined" && bossMode ? 500 : 250) * 8 / 5); // Set EnemyZ health to 8/5 of EnemyX health
self.t = Math.random() * 2;
self.zigzagAmplitude = 320 + Math.random() * 80;
- self.zigzagSpeed = 0.018 + Math.random() * 0.008;
+ self.zigzagSpeed = 0.009 + Math.random() * 0.004;
self.baseX = 2048 / 2;
self.directionY = 1;
self.shootTick = 0;
self.beamCooldown = 0;
@@ -352,9 +352,9 @@
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;
+ self.y += self.speed * self.directionY * 0.3;
if (self.y >= lowerY) {
self.y = lowerY;
self.directionY = -1;
} else if (self.y <= upperY) {
@@ -375,10 +375,10 @@
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;
+ bullet.speedX = dx / len * 11;
+ bullet.speedY = dy / len * 11;
}
enemyBullets.push(bullet);
game.addChild(bullet);
LK.getSound('enemyShoot').play();
@@ -434,9 +434,9 @@
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);
+ self.beamCooldown = 300 + Math.floor(Math.random() * 90);
}
}
};
return self;
@@ -451,9 +451,9 @@
potionSprite.width = 90;
potionSprite.height = 90;
potionSprite.alpha = 0.95;
self.update = function () {
- self.y += 16;
+ self.y += 8;
};
return self;
});
// Hero class
@@ -476,9 +476,9 @@
var bulletSprite = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
- self.speed = -32;
+ self.speed = -16;
self.update = function () {
self.y += self.speed;
};
return self;
@@ -493,9 +493,9 @@
shieldSprite.width = 90;
shieldSprite.height = 90;
shieldSprite.alpha = 0.95; // Make it much brighter/less transparent
self.update = function () {
- self.y += 12;
+ self.y += 6;
};
return self;
});
@@ -1764,17 +1764,17 @@
}(bullet.update, bullet.spread);
heroBullets.push(bullet);
game.addChild(bullet);
}
- hero.shootCooldown = 6;
+ hero.shootCooldown = 12;
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;
+ hero.shootCooldown = 20;
LK.getSound('laser').play();
}
}
// Update hero bullets
@@ -2079,10 +2079,10 @@
if (typeof spawnRateBoostActive === "undefined") {
var spawnRateBoostActive = false;
var spawnRateBoostTimer = 0;
var nextBoostScore = 50;
- var spawnIntervalNormal = 60;
- var spawnIntervalBoost = 30;
+ var spawnIntervalNormal = 90;
+ var spawnIntervalBoost = 60;
}
// Boss mode: alternate EnemyX and EnemyZ, never normal enemies or items
if (typeof bossMode !== "undefined" && bossMode) {
spawnTick++;
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
Pixel art style, spaceship lazer bullet. In-Game asset. 2d. High contrast. No shadows
Pixel art 2d heart. In-Game asset. 2d. High contrast. No shadows
Pixel art Spaceship. In-Game asset. 2d. High contrast. No shadows
Pixel art shield. In-Game asset. 2d. High contrast. No shadows. blue color. Simple design
Flying Space enemy. In-Game asset. 2d. High contrast. No shadows. Pixelart style.
A yellow pixelart rectangle button.. In-Game asset. 2d. High contrast. No shadows
a pixelart flying space enemy. Green color, scary smiley face. In-Game asset. 2d. High contrast. No shadows
Pixelart healt thing. Simple. In-Game asset. 2d. High contrast. No shadows
Pixelart power up item. In-Game asset. 2d. High contrast. No shadows
Pixelart spike ellipse enemy bullet. In-Game asset. 2d. High contrast. No shadows. No fire effect.