User prompt
Make voids signature easier
User prompt
Change the abyss signature move
User prompt
Remove the speech bubble
User prompt
Make the dialogue for bosses in the speech bubble visible by making it white
User prompt
Make embers speech bubbles say “my parents didn’t prepare me for this.” The next one will say “Thank you”
User prompt
Change obsidians signature
User prompt
Make most bosses have 3 lives
User prompt
When the boss is defeated stop him from attacking
User prompt
When the speech bubbles appears make the boss stop attacking and make the words in the speech bubble white for visibilty
User prompt
When you beat a boss make the boss say something’s linked to its story. Make it say them in speech bubbles. When it’s is finished talking make it fall out of the stage and then show the stiry
User prompt
Make easier bosses have less lives
User prompt
Make the story in multiple lines and so it is easier to see
User prompt
Make inferno’s story that he was the gid of fire but got demoted after the celestials came after the gods and he couldn’t protect them so he became a dude with fire powers who roams around and causes trouble
User prompt
When you beat a boss go to a screen that shows the bosses story and then you can tap continue to win
User prompt
Make blizzard easier
User prompt
Make blizzards signature differnt
User prompt
Make the slash effect white
User prompt
Add a slash effect to the boss every 20 secs in the gameplay showing it has taken damge
User prompt
Bake the bosses lives go down by one every 20secs in the gameplay when it hits zero you win
User prompt
Stop obsidians moves from bouncing off the walls
User prompt
Make the obsidian bosses projectiles not bounce
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Boss class var Boss = Container.expand(function () { var self = Container.call(this); // Boss type will be set after creation self.bossType = null; self.attackTimer = 0; self.attackInterval = 90; // frames self.difficulty = 1; self.phase = 1; self.timeAlive = 0; self.asset = null; self.init = function (bossType) { self.bossType = bossType; var assetId = bossType.asset; self.asset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.difficulty = bossType.difficulty; self.attackInterval = bossType.baseInterval; self.phase = 1; self.timeAlive = 0; }; self.update = function () { self.timeAlive++; // --- Boss movement patterns for effect --- if (self.bossType) { var t = self.timeAlive; // Save original spawn position on first update if (self._spawnX === undefined) { self._spawnX = self.x; self._spawnY = self.y; } // Movement patterns by boss (idle/neutral) if (self.bossType.name === "Inferno") { // Sway left/right and bob up/down self.x = self._spawnX + Math.sin(t / 40) * 60; self.y = self._spawnY + Math.sin(t / 60) * 30; } else if (self.bossType.name === "Verdant") { // Gentle up/down wave, slight horizontal drift self.x = self._spawnX + Math.sin(t / 90) * 40; self.y = self._spawnY + Math.cos(t / 50) * 55; } else if (self.bossType.name === "Solaris") { // Small circular orbit self.x = self._spawnX + Math.cos(t / 55) * 45; self.y = self._spawnY + Math.sin(t / 55) * 45; } else if (self.bossType.name === "Abyss") { // Slow vertical bob, slight horizontal shake self.x = self._spawnX + Math.sin(t / 25) * 18; self.y = self._spawnY + Math.sin(t / 80) * 70; } else if (self.bossType.name === "Tempest") { // Fast horizontal zigzag, slight up/down self.x = self._spawnX + Math.sin(t / 18) * 110; self.y = self._spawnY + Math.cos(t / 60) * 25; } else if (self.bossType.name === "Prism") { // Figure-eight pattern self.x = self._spawnX + Math.sin(t / 38) * 70; self.y = self._spawnY + Math.sin(t / 19) * 40; } else if (self.bossType.name === "Obsidian") { // Small, erratic jitter self.x = self._spawnX + Math.sin(t / 13) * 18 + Math.sin(t / 7) * 8; self.y = self._spawnY + Math.cos(t / 17) * 18 + Math.cos(t / 11) * 8; } else if (self.bossType.name === "Blizzard") { // Gentle diagonal drift self.x = self._spawnX + Math.sin(t / 60) * 55; self.y = self._spawnY + Math.cos(t / 60) * 55; } else if (self.bossType.name === "Ember") { // Quick up/down bounce, slight left/right self.x = self._spawnX + Math.sin(t / 30) * 35; self.y = self._spawnY + Math.abs(Math.sin(t / 15)) * 60; } // --- Boss attack animation (per attack pattern) --- // Animate boss asset for each attack type if (self.asset && self.bossType && typeof bossAttack === "function" && boss && boss._attackIndex !== undefined) { // Get current attack pattern var attackList = []; if (self.bossType.name === "Inferno") { attackList = ["spiral", "double_spiral", "wall", "cross", "aimed_burst"]; } else if (self.bossType.name === "Verdant") { attackList = ["burst", "flower", "arc", "ring", "random_bounce"]; } else if (self.bossType.name === "Solaris") { attackList = ["aimed", "triple_aimed", "sniper", "spread", "rain"]; } else if (self.bossType.name === "Abyss") { attackList = ["vortex", "blackhole", "void_ring", "chaos", "gravity"]; } else if (self.bossType.name === "Tempest") { attackList = ["wind", "gust", "cyclone", "twister", "scatter"]; } else if (self.bossType.name === "Prism") { attackList = ["prism", "reflect", "laser", "split", "mirror"]; } else if (self.bossType.name === "Obsidian") { attackList = ["chaos"]; } else if (self.bossType.name === "Blizzard") { attackList = ["ring"]; } else if (self.bossType.name === "Ember") { attackList = ["burst"]; } var curPattern = attackList.length > 0 ? attackList[(boss._attackIndex - 1 + attackList.length) % attackList.length] : null; // Animate based on attack pattern // Animate scale, rotation, alpha, color, etc. if (self.bossType.name === "Inferno") { if (curPattern === "spiral") { // Pulse scale and color self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 8)) * 0.18; self.asset.alpha = 0.95 - 0.15 * Math.abs(Math.sin(t / 8)); } else if (curPattern === "double_spiral") { // Fast pulsing self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 4)) * 0.22; self.asset.alpha = 0.85 + 0.1 * Math.abs(Math.cos(t / 4)); } else if (curPattern === "wall") { // Squash horizontally or vertically if (t % 120 < 60) { self.asset.scale.x = 1.25; self.asset.scale.y = 0.8; } else { self.asset.scale.x = 0.8; self.asset.scale.y = 1.25; } self.asset.alpha = 1.0; } else if (curPattern === "cross") { // Quick rotate self.asset.rotation = t % 60 / 60 * 2 * Math.PI; self.asset.scale.x = self.asset.scale.y = 1.0; self.asset.alpha = 1.0; // After spinning, always reset to face right (rotation 0) if ((t + 1) % 60 === 0) { self.asset.rotation = 0; } } else if (curPattern === "aimed_burst") { // Flicker alpha self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 2)); self.asset.scale.x = self.asset.scale.y = 1.0; } } else if (self.bossType.name === "Verdant") { if (curPattern === "burst") { // Gentle scale pulse self.asset.scale.x = self.asset.scale.y = 1.0 + Math.sin(t / 18) * 0.08; self.asset.alpha = 1.0; } else if (curPattern === "flower") { // Petal open/close (scaleX/scaleY alternate) self.asset.scale.x = 1.0 + Math.sin(t / 10) * 0.13; self.asset.scale.y = 1.0 + Math.cos(t / 10) * 0.13; self.asset.alpha = 1.0; } else if (curPattern === "arc") { // Sway left/right self.asset.rotation = Math.sin(t / 20) * 0.25; self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "ring") { // Fade in/out self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 12)); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "random_bounce") { // Jitter self.asset.x = Math.sin(t / 2) * 6; self.asset.y = Math.cos(t / 2) * 6; self.asset.scale.x = self.asset.scale.y = 1.0; } } else if (self.bossType.name === "Solaris") { if (curPattern === "aimed") { // Glow (alpha pulse) self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 6)); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "triple_aimed") { // Quick scale pulse self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 4)) * 0.13; self.asset.alpha = 1.0; } else if (curPattern === "sniper") { // Hold still, then quick flicker if (t % 60 > 45) { self.asset.alpha = 0.5 + 0.5 * Math.abs(Math.sin(t * 2)); } else { self.asset.alpha = 1.0; } self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "spread") { // Sway self.asset.rotation = Math.sin(t / 10) * 0.18; self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "rain") { // Drip (scaleY pulse) self.asset.scale.x = 1.0; self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 8)) * 0.18; self.asset.alpha = 1.0; } } else if (self.bossType.name === "Abyss") { if (curPattern === "vortex") { // Slow swirl self.asset.rotation = t / 40 % (2 * Math.PI); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "blackhole") { // Shrink/grow self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 8)) * 0.18; self.asset.alpha = 1.0; } else if (curPattern === "void_ring") { // Fade in/out self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 12)); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "chaos") { // Jitter self.asset.x = Math.sin(t / 2) * 8; self.asset.y = Math.cos(t / 2) * 8; self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "gravity") { // Pulse scale self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 10)) * 0.13; self.asset.alpha = 1.0; } } else if (self.bossType.name === "Tempest") { if (curPattern === "wind") { // Sway horizontally self.asset.x = Math.sin(t / 4) * 12; self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "gust") { // Quick scale pulse self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 3)) * 0.13; self.asset.alpha = 1.0; } else if (curPattern === "cyclone") { // Spin self.asset.rotation = t / 20 % (2 * Math.PI); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "twister") { // S-curve: scaleX/scaleY alternate self.asset.scale.x = 1.0 + Math.sin(t / 6) * 0.18; self.asset.scale.y = 1.0 + Math.cos(t / 6) * 0.18; self.asset.alpha = 1.0; } else if (curPattern === "scatter") { // Flicker alpha self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 2)); self.asset.scale.x = self.asset.scale.y = 1.0; } } else if (self.bossType.name === "Prism") { if (curPattern === "prism") { // Rainbow pulse (scale) self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 7)) * 0.18; self.asset.alpha = 1.0; } else if (curPattern === "reflect") { // Bounce (scaleX/scaleY alternate) self.asset.scale.x = 1.0 + Math.sin(t / 8) * 0.13; self.asset.scale.y = 1.0 + Math.cos(t / 8) * 0.13; self.asset.alpha = 1.0; } else if (curPattern === "laser") { // Flicker alpha self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 2)); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (curPattern === "split") { // Quick scale pulse self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 3)) * 0.13; self.asset.alpha = 1.0; } else if (curPattern === "mirror") { // Spin self.asset.rotation = t / 12 % (2 * Math.PI); self.asset.scale.x = self.asset.scale.y = 1.0; } } else if (self.bossType.name === "Obsidian") { // Always jitter and flicker self.asset.x = Math.sin(t / 2) * 10; self.asset.y = Math.cos(t / 2) * 10; self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 2)); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (self.bossType.name === "Blizzard") { // Always fade in/out self.asset.alpha = 0.7 + 0.3 * Math.abs(Math.sin(t / 12)); self.asset.scale.x = self.asset.scale.y = 1.0; } else if (self.bossType.name === "Ember") { // Always quick scale pulse self.asset.scale.x = self.asset.scale.y = 1.0 + Math.abs(Math.sin(t / 3)) * 0.13; self.asset.alpha = 1.0; } } } // Increase difficulty over time // For Inferno, slow down ramp-up and minimum interval for more dodgeable attacks if (self.bossType && self.bossType.name === "Inferno") { if (self.timeAlive % 900 === 0 && self.attackInterval > 60) { self.attackInterval -= 10; self.phase++; } } else { if (self.timeAlive % 600 === 0 && self.attackInterval > 30) { self.attackInterval -= 10; self.phase++; } } }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerGfx = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.radius = playerGfx.width / 2; self.update = function () {}; return self; }); // Projectile class var Projectile = Container.expand(function () { var self = Container.call(this); self.speedX = 0; self.speedY = 0; self.radius = 30; self.asset = null; self.type = null; self.init = function (type, colorAsset) { self.type = type; self.asset = self.attachAsset(colorAsset, { anchorX: 0.5, anchorY: 0.5 }); self.radius = self.asset.width / 2; }; self.update = function () { // Ember: spin embers in a circle if _emberSpin is set if (self._emberSpin) { // Orbit around the boss center if (!self._emberOrbitTick) self._emberOrbitTick = 0; self._emberOrbitTick++; // If boss is still alive, follow its position if (boss && boss.x !== undefined && boss.y !== undefined) { self._emberOrbitCenter.x = boss.x; self._emberOrbitCenter.y = boss.y; } self._emberOrbitAngle += self._emberOrbitSpeed; self.x = self._emberOrbitCenter.x + Math.cos(self._emberOrbitAngle) * self._emberOrbitRadius; self.y = self._emberOrbitCenter.y + Math.sin(self._emberOrbitAngle) * self._emberOrbitRadius; } else { self.x += self.speedX; self.y += self.speedY; } // --- Tempest projectile rotation: face direction of movement --- if (self.type === "wind" || self.type === "gust" || self.type === "cyclone" || self.type === "twister" || self.type === "scatter") { // Only rotate if asset exists and there is movement if (self.asset && (self.speedX !== 0 || self.speedY !== 0)) { self.asset.rotation = Math.atan2(self.speedY, self.speedX); } } // --- New Boss Effects --- // Abyss: blackhole (gravity to player) if (self._gravityToPlayer) { if (player) { var dx = player.x - self.x; var dy = player.y - self.y; var mag = Math.sqrt(dx * dx + dy * dy) || 1; var grav = 0.18; self.speedX += dx / mag * grav; self.speedY += dy / mag * grav; // Clamp speed var spd = Math.sqrt(self.speedX * self.speedX + self.speedY * self.speedY); var maxSpd = 10; if (spd > maxSpd) { self.speedX *= maxSpd / spd; self.speedY *= maxSpd / spd; } } } // Tempest: twister (S-curve) if (self._twistDir) { self._twistTick++; // Oscillate speedX for S-curve self.speedX = Math.sin(self._twistTick / 18) * 7 * self._twistDir; } // Bounce off arena edges if .bounce is set if (self.bounce) { var minX = ARENA_X + self.radius; var maxX = ARENA_X + ARENA_W - self.radius; var minY = ARENA_Y + self.radius; var maxY = ARENA_Y + ARENA_H - self.radius; if (self.x <= minX && self.speedX < 0 || self.x >= maxX && self.speedX > 0) { self.speedX *= -1; self.x = Math.max(minX, Math.min(maxX, self.x)); } if (self.y <= minY && self.speedY < 0 || self.y >= maxY && self.speedY > 0) { self.speedY *= -1; self.y = Math.max(minY, Math.min(maxY, self.y)); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181818 }); /**** * Game Code ****/ // Signature ability sounds for each boss // Add missing boss music assets // Unique boss music (IDs are placeholders, replace with actual IDs as needed) // Prism // Tempest // Abyss // Prism // Tempest // Abyss // Arena settings // Bosses: Each boss will have a unique color and shape for now. // Player: Circle, blue // Bosses // Projectiles // Arena border // Unique sprites for 4th row bosses // If you decrement bossLives elsewhere in the code (e.g. on projectile collision or other damage), call bossHitEffect() at that point to trigger the visual effect. var ARENA_W = 1800; var ARENA_H = 2400; var ARENA_X = (2048 - ARENA_W) / 2; var ARENA_Y = (2732 - ARENA_H) / 2; // Boss definitions var bossDefs = [{ name: "Inferno", asset: "boss_red", proj: "proj_red", color: 0xe74c3c, difficulty: 2, baseInterval: 140, // was 110 pattern: "spiral" }, { name: "Verdant", asset: "boss_green", proj: "proj_green", color: 0x27ae60, difficulty: 1, baseInterval: 180, // was 140 pattern: "burst" }, { name: "Solaris", asset: "boss_yellow", proj: "proj_yellow", color: 0xf1c40f, difficulty: 3, baseInterval: 80, // was 50 pattern: "aimed" }, { name: "Abyss", asset: "boss_gray", proj: "proj_gray", color: 0x7f8c8d, difficulty: 4, baseInterval: 120, // was 80 pattern: "vortex" }, { name: "Tempest", asset: "boss_blue", proj: "proj_blue", color: 0x2980b9, difficulty: 3, baseInterval: 110, // was 70 pattern: "wind" }, { name: "Prism", asset: "boss_purple", proj: "proj_purple", color: 0x9b59b6, difficulty: 5, baseInterval: 100, // was 60 pattern: "prism" }, { name: "Obsidian", asset: "boss_black", proj: "proj_black", color: 0x111111, difficulty: 4, baseInterval: 120, pattern: "chaos" }, { name: "Blizzard", asset: "boss_white", proj: "proj_white", color: 0xffffff, difficulty: 4, baseInterval: 55, // was 130, now much more frequent and harder pattern: "ring" }, { name: "Ember", asset: "boss_orange", proj: "proj_orange", color: 0xffa500, difficulty: 3, baseInterval: 100, pattern: "burst" }]; var player = null; var boss = null; var projectiles = []; var survivalTime = 0; var gameStarted = false; var bossChoice = null; var dragNode = null; var lastMoveX = 0, lastMoveY = 0; // Boss animated background container (created once, reused) var bossBgContainer = new Container(); bossBgContainer.visible = false; game.addChild(bossBgContainer); // Arena border var arenaBorder = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(arenaBorder); // GUI: Survival time var timeTxt = new Text2('0.0s', { size: 110, fill: 0xFFFFFF }); timeTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timeTxt); // GUI: Player lives var playerLives = 5; var livesTxt = new Text2('Lives: 5', { size: 90, fill: 0xFF6666 }); livesTxt.anchor.set(0.5, 1); // anchor to center bottom // Place at bottom center, above the very bottom edge (leave 60px margin) livesTxt.y = -60; LK.gui.bottom.addChild(livesTxt); // GUI: Boss name var bossNameTxt = new Text2('', { size: 90, fill: 0xFFECB3 }); bossNameTxt.anchor.set(0.5, 0); LK.gui.top.addChild(bossNameTxt); // GUI: Boss lives (above healthbar) var bossLivesTxt = new Text2('', { size: 80, fill: 0xFF4444 }); bossLivesTxt.anchor.set(0.5, 1); // center, bottom anchor bossLivesTxt.x = 224 + 0.22 * 1800 / 2; // center above healthbar bossLivesTxt.y = 180 - 12; // just above healthbar LK.gui.top.addChild(bossLivesTxt); // GUI: Boss healthbar var bossHealthBarBg = LK.getAsset('arena_border', { anchorX: 0, anchorY: 0.5, x: 224, // leave space for top left menu y: 180, scaleX: 0.22, scaleY: 0.025 }); bossHealthBarBg.alpha = 0.22; LK.gui.top.addChild(bossHealthBarBg); var bossHealthBar = LK.getAsset('arena_border', { anchorX: 0, anchorY: 0.5, x: 224, y: 180, scaleX: 0.22, scaleY: 0.025 }); bossHealthBar.tint = 0xFF4444; bossHealthBar.alpha = 0.82; LK.gui.top.addChild(bossHealthBar); var bossHealthBarFrame = LK.getAsset('arena_border', { anchorX: 0, anchorY: 0.5, x: 224, y: 180, scaleX: 0.22, scaleY: 0.025 }); bossHealthBarFrame.alpha = 0.38; LK.gui.top.addChild(bossHealthBarFrame); // Boss health variables var bossMaxHealth = 100; var bossHealth = 100; // Boss lives variables var bossLives = 4; var bossMaxLives = 4; // Helper: flash boss hit effect (white flash) function bossHitEffect() { if (boss && boss.asset) { LK.effects.flashObject(boss.asset, 0xffffff, 350); } // Update boss lives text if visible if (typeof bossLivesTxt !== "undefined" && bossLivesTxt.visible !== false) { bossLivesTxt.setText('Lives: ' + bossLives + ' / ' + bossMaxLives); } } // --- Menu Background --- var menuBg = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 1.04, scaleY: 1.04 }); menuBg.alpha = 0.18; game.addChild(menuBg); // GUI: Boss select title var bossSelectTxt = new Text2('Choose Your Boss', { size: 140, fill: 0xFFFFFF, font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); bossSelectTxt.anchor.set(0.5, 0); bossSelectTxt.y = 20; LK.gui.top.addChild(bossSelectTxt); // GUI: Boss select subtitle var bossSelectSubTxt = new Text2('Tap a boss to begin', { size: 70, fill: 0xCCCCCC }); bossSelectSubTxt.anchor.set(0.5, 0); // Place just under the 'Choose Your Boss' title (title is at y=20, size=140) bossSelectSubTxt.y = bossSelectTxt.y + bossSelectTxt.height + 10; LK.gui.top.addChild(bossSelectSubTxt); // --- Menu: Info/Settings Button --- var menuInfoBtn = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 180, // right side, avoid overlap with top right y: 120, scaleX: 0.08, scaleY: 0.08 }); menuInfoBtn.alpha = 0.45; game.addChild(menuInfoBtn); var menuInfoBtnLabel = new Text2('?', { size: 90, fill: 0xFFFFFF }); menuInfoBtnLabel.anchor.set(0.5, 0.5); menuInfoBtnLabel.x = menuInfoBtn.x; menuInfoBtnLabel.y = menuInfoBtn.y + 2; game.addChild(menuInfoBtnLabel); // --- Menu: Style Select Button --- var menuStyleBtn = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 180, // left side, avoid overlap with top left menu y: menuInfoBtn.y + menuInfoBtn.height + 40, // place below info button, with spacing scaleX: 0.08, scaleY: 0.08 }); menuStyleBtn.alpha = 0.45; game.addChild(menuStyleBtn); var menuStyleBtnLabel = new Text2('S', { size: 90, fill: 0xFFFFFF }); menuStyleBtnLabel.anchor.set(0.5, 0.5); menuStyleBtnLabel.x = menuStyleBtn.x; menuStyleBtnLabel.y = menuStyleBtn.y + 2; game.addChild(menuStyleBtnLabel); // --- Style Select Page UI (hidden by default) --- var stylePageBg = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 0.7, scaleY: 0.7 }); stylePageBg.alpha = 0.92; stylePageBg.visible = false; game.addChild(stylePageBg); var stylePageTitle = new Text2('Choose Your Style', { size: 130, fill: 0xFFFFFF, font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); stylePageTitle.anchor.set(0.5, 0); stylePageTitle.x = 2048 / 2; stylePageTitle.y = 2732 / 2 - 420; stylePageTitle.visible = false; game.addChild(stylePageTitle); var stylePageBody = new Text2('Each style changes your abilities!\n\nTap a style to select.', { size: 84, fill: 0xFFFFFF, font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); stylePageBody.anchor.set(0.5, 0); stylePageBody.x = 2048 / 2; stylePageBody.y = 2732 / 2 - 250; stylePageBody.visible = false; game.addChild(stylePageBody); var stylePageBackBtn = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 100, y: 100, scaleX: 0.13, scaleY: 0.08 }); stylePageBackBtn.alpha = 0.85; stylePageBackBtn.tint = 0xD32F2F; // Red color stylePageBackBtn.visible = false; game.addChild(stylePageBackBtn); var stylePageBackLabel = new Text2('Back', { size: 80, fill: 0xFFFFFF }); stylePageBackLabel.anchor.set(0.5, 0.5); stylePageBackLabel.x = stylePageBackBtn.x; stylePageBackLabel.y = stylePageBackBtn.y + 2; stylePageBackLabel.visible = false; game.addChild(stylePageBackLabel); // --- Style Select Buttons --- var styleDefs = [{ name: "Tanky", desc: "Extra life, slower movement", color: 0x4CAF50 }, { name: "Martial Artist", desc: "Balanced stats", color: 0xFF9800 }, { name: "Swift", desc: "Faster movement, less life", color: 0x2196F3 }]; var styleSelectButtons = []; // Arrange in rows of 2 var styleBtnCols = 2; var styleBtnSpacingX = 700; var styleBtnSpacingY = 520; var styleBtnStartY = 2732 / 2 - 320; var styleBtnRowYOffset = [0, 180]; // Move second row (row 1) lower by 180px for (var i = 0; i < styleDefs.length; i++) { var s = styleDefs[i]; var col = i % styleBtnCols; var row = Math.floor(i / styleBtnCols); // Center the two columns horizontally var x = 2048 / 2 + (col - 0.5) * styleBtnSpacingX; var y = styleBtnStartY + row * styleBtnSpacingY + (styleBtnRowYOffset[row] || 0); // Add a subtle drop shadow behind each style button var shadow = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: x + 0, y: y + 18, scaleX: 0.52, scaleY: 0.52 }); shadow.alpha = 0.13; shadow.visible = false; game.addChild(shadow); var btn = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 0.45, scaleY: 0.45 }); btn.styleIndex = i; btn.alpha = 0.82; btn.tint = s.color; btn.visible = false; btn._shadow = shadow; btn._rounded = true; // for future: if engine supports rounded corners game.addChild(btn); styleSelectButtons.push(btn); // Add a white border for polish var border = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 0.45, scaleY: 0.45 }); border.alpha = 0.18; border.tint = 0xFFFFFF; border.visible = false; btn._border = border; game.addChild(border); var label = new Text2(s.name, { size: 90, fill: s.color, font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); label.anchor.set(0.5, 0); label.x = btn.x; label.y = btn.y - 90; label.visible = false; game.addChild(label); btn._label = label; var desc = new Text2(s.desc, { size: 60, fill: 0xFFFFFF }); desc.anchor.set(0.5, 0); desc.x = btn.x; desc.y = btn.y + 80; desc.visible = false; game.addChild(desc); btn._desc = desc; // Add touch/hover feedback for polish btn._pressed = false; btn._hovered = false; btn._origAlpha = btn.alpha; btn._origScale = btn.scale.x; // Animate feedback in update btn.update = function () { // Only animate if visible if (!this.visible) return; // Pressed feedback: scale up and brighten if (this._pressed) { this.scale.x = this.scale.y = this._origScale * 1.08; this.alpha = 1.0; if (this._border) this._border.alpha = 0.32; if (this._shadow) this._shadow.alpha = 0.22; } else if (this._hovered) { this.scale.x = this.scale.y = this._origScale * 1.04; this.alpha = 0.95; if (this._border) this._border.alpha = 0.24; if (this._shadow) this._shadow.alpha = 0.18; } else { this.scale.x = this.scale.y = this._origScale; this.alpha = this._origAlpha; if (this._border) this._border.alpha = 0.18; if (this._shadow) this._shadow.alpha = 0.13; } }; } var styleChoice = null; // Helper to show/hide style page function showStylePage(show) { stylePageBg.visible = show; stylePageTitle.visible = show; stylePageBody.visible = show; stylePageBackBtn.visible = show; stylePageBackLabel.visible = show; for (var i = 0; i < styleSelectButtons.length; i++) { styleSelectButtons[i].visible = show; styleSelectButtons[i]._label.visible = show; styleSelectButtons[i]._desc.visible = show; // Show/hide border and shadow for polish if (styleSelectButtons[i]._border) styleSelectButtons[i]._border.visible = show; if (styleSelectButtons[i]._shadow) styleSelectButtons[i]._shadow.visible = show; // Reset feedback state styleSelectButtons[i]._pressed = false; styleSelectButtons[i]._hovered = false; if (typeof styleSelectButtons[i].update === "function") styleSelectButtons[i].update(); } // Hide boss select UI and info/settings button when style page is open bossSelectTxt.visible = !show; bossSelectSubTxt.visible = !show; for (var i = 0; i < bossSelectButtons.length; i++) { bossSelectButtons[i].visible = !show; bossSelectButtons[i]._label.visible = !show; if (bossSelectButtons[i]._diffDots) { for (var d = 0; d < bossSelectButtons[i]._diffDots.length; d++) { bossSelectButtons[i]._diffDots[d].visible = !show; } } } menuInfoBtn.visible = !show; menuInfoBtnLabel.visible = !show; menuStyleBtn.visible = !show; menuStyleBtnLabel.visible = !show; } var stylePageOpen = false; // Info/Settings Page UI (hidden by default) var infoPageBg = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 0.7, scaleY: 0.7 }); infoPageBg.alpha = 0.92; infoPageBg.visible = false; game.addChild(infoPageBg); var infoPageTitle = new Text2('Game Info', { size: 130, fill: 0xFFFFFF, // white for high contrast font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); infoPageTitle.anchor.set(0.5, 0); infoPageTitle.x = 2048 / 2; infoPageTitle.y = 2732 / 2 - 420; infoPageTitle.visible = false; game.addChild(infoPageTitle); var infoPageBody = new Text2('Dodge the boss attacks!\n\nDrag the player to move.\n\nEach boss has unique patterns.\n\nGood luck!', { size: 84, fill: 0xFFFFFF, // white for high contrast font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); infoPageBody.anchor.set(0.5, 0); infoPageBody.x = 2048 / 2; infoPageBody.y = 2732 / 2 - 250; infoPageBody.visible = false; game.addChild(infoPageBody); var infoPageCloseBtn = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 420, scaleX: 0.18, scaleY: 0.08 }); infoPageCloseBtn.alpha = 0.55; infoPageCloseBtn.visible = false; game.addChild(infoPageCloseBtn); var infoPageCloseLabel = new Text2('Close', { size: 80, fill: 0x222222 }); infoPageCloseLabel.anchor.set(0.5, 0.5); infoPageCloseLabel.x = infoPageCloseBtn.x; infoPageCloseLabel.y = infoPageCloseBtn.y + 2; infoPageCloseLabel.visible = false; game.addChild(infoPageCloseLabel); // Add a back button to the info/settings page var infoPageBackBtn = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 100, // Top right, just inside the 100px margin y: 100, // Top right, just inside the 100px margin scaleX: 0.13, scaleY: 0.08 }); infoPageBackBtn.alpha = 0.85; infoPageBackBtn.tint = 0xD32F2F; // Red color infoPageBackBtn.visible = false; game.addChild(infoPageBackBtn); var infoPageBackLabel = new Text2('Back', { size: 80, fill: 0xFFFFFF }); infoPageBackLabel.anchor.set(0.5, 0.5); infoPageBackLabel.x = infoPageBackBtn.x; infoPageBackLabel.y = infoPageBackBtn.y + 2; infoPageBackLabel.visible = false; game.addChild(infoPageBackLabel); // Helper to show/hide info page function showInfoPage(show) { infoPageBg.visible = show; infoPageTitle.visible = show; infoPageBody.visible = show; infoPageCloseBtn.visible = show; infoPageCloseLabel.visible = show; if (typeof infoPageBackBtn !== "undefined") infoPageBackBtn.visible = show; if (typeof infoPageBackLabel !== "undefined") infoPageBackLabel.visible = show; // Hide boss select UI when info page is open bossSelectTxt.visible = !show; bossSelectSubTxt.visible = !show; for (var i = 0; i < bossSelectButtons.length; i++) { bossSelectButtons[i].visible = !show; bossSelectButtons[i]._label.visible = !show; if (bossSelectButtons[i]._diffDots) { for (var d = 0; d < bossSelectButtons[i]._diffDots.length; d++) { bossSelectButtons[i]._diffDots[d].visible = !show; } } } menuInfoBtn.visible = !show; menuInfoBtnLabel.visible = !show; } // Track info page state var infoPageOpen = false; // Boss select buttons and labels var bossSelectButtons = []; for (var i = 0; i < bossDefs.length; i++) { var b = bossDefs[i]; // Arrange in four rows of 3: 0,1,2 on top row, 3,4,5 in 2nd, 6,7,8 in 3rd, 9,10,11 in 4th row var row = Math.floor(i / 3); // 0 for top, 1 for 2nd, 2 for 3rd, 3 for 4th var col = i % 3; // 0,1,2 var x = 2048 / 2 + (col - 1) * 420; // center, -420, 0, +420 var y; // Increase spacing for top 3 rows for better visibility var rowSpacingTop = 420; // More spacing for top 3 rows var rowSpacingBottom = 370; // 4th row spacing if (row === 0) { y = 2732 / 2 - rowSpacingTop * 1.6; // top row, highest } else if (row === 1) { y = 2732 / 2 - rowSpacingTop * 0.5; // 2nd row, above center } else if (row === 2) { y = 2732 / 2 + rowSpacingTop * 0.6; // 3rd row, below center } else { y = 2732 / 2 + rowSpacingBottom * 2.0; // 4th row, moved down a little more for equal spacing } // Add a subtle shadow behind each boss button var shadow; if (i >= 9) { // 4th row: smaller shadow to match smaller boss button shadow = LK.getAsset(b.asset, { anchorX: 0.5, anchorY: 0.5, x: x + 0, y: y + 18, scaleX: 0.98, scaleY: 0.98 }); } else { shadow = LK.getAsset(b.asset, { anchorX: 0.5, anchorY: 0.5, x: x + 0, y: y + 18, scaleX: 1.18, scaleY: 1.18 }); } shadow.alpha = 0.13; game.addChild(shadow); var btn; if (i >= 9) { // 4th row: use ellipse/circle shape for button, but a bit smaller btn = LK.getAsset(b.asset, { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 0.93, scaleY: 0.93 }); } else { btn = LK.getAsset(b.asset, { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 1.13, scaleY: 1.13 }); } btn.bossIndex = i; bossSelectButtons.push(btn); game.addChild(btn); // Add boss name label var label = new Text2(b.name, { size: 78, fill: b.color }); label.anchor.set(0.5, 0); label.x = btn.x; // Place label below the button label.y = btn.y + 140; game.addChild(label); btn._label = label; // Add a difficulty indicator (dots) - now even further above the boss button var diffDots = []; for (var d = 0; d < b.difficulty; d++) { var dot = LK.getAsset('boss_' + b.asset.split('_')[1], { anchorX: 0.5, anchorY: 0.5, x: btn.x - 32 * (b.difficulty - 1) / 2 + d * 32, y: btn.y - 180, // moved further above the boss button scaleX: 0.18, scaleY: 0.18 }); dot.alpha = 0.7; game.addChild(dot); diffDots.push(dot); } btn._diffDots = diffDots; } // Hide arena and GUI until boss is chosen arenaBorder.visible = false; timeTxt.visible = false; bossNameTxt.visible = false; // Play menu music at game start LK.playMusic('music_menu', { loop: true }); // Boss select handler game.down = function (x, y, obj) { // Info/settings page open: only allow closing or back if (infoPageBg && infoPageBg.visible) { // Close button var dx = x - infoPageCloseBtn.x; var dy = y - infoPageCloseBtn.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 180) { showInfoPage(false); infoPageOpen = false; return; } // Back button if (typeof infoPageBackBtn !== "undefined") { var dxBack = x - infoPageBackBtn.x; var dyBack = y - infoPageBackBtn.y; var distBack = Math.sqrt(dxBack * dxBack + dyBack * dyBack); if (distBack < 140) { showInfoPage(false); infoPageOpen = false; return; } } return; } // Style page open: only allow closing/back or style selection if (stylePageBg && stylePageBg.visible) { // Back button var dxBack = x - stylePageBackBtn.x; var dyBack = y - stylePageBackBtn.y; var distBack = Math.sqrt(dxBack * dxBack + dyBack * dyBack); if (distBack < 140) { showStylePage(false); stylePageOpen = false; return; } // Check if a style button was pressed for (var i = 0; i < styleSelectButtons.length; i++) { var btn = styleSelectButtons[i]; var dx = x - btn.x; var dy = y - btn.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 200) { // Style chosen styleChoice = styleDefs[btn.styleIndex]; // Press feedback: set pressed state for a short time btn._pressed = true; if (typeof btn.update === "function") btn.update(); // Flash feedback, then close style page LK.setTimeout(function () { btn._pressed = false; if (typeof btn.update === "function") btn.update(); showStylePage(false); stylePageOpen = false; }, 120); // Optionally, show a quick feedback (flash or sound) // You could also update UI to show selected style return; } else { // Not pressed, but if close, set hover btn._hovered = dist < 220; if (typeof btn.update === "function") btn.update(); } } return; } if (!gameStarted) { // Check if info/settings button pressed var dxInfo = x - menuInfoBtn.x; var dyInfo = y - menuInfoBtn.y; var distInfo = Math.sqrt(dxInfo * dxInfo + dyInfo * dyInfo); if (menuInfoBtn.visible && distInfo < 120) { showInfoPage(true); infoPageOpen = true; return; } // Check if style select button pressed var dxStyle = x - menuStyleBtn.x; var dyStyle = y - menuStyleBtn.y; var distStyle = Math.sqrt(dxStyle * dxStyle + dyStyle * dyStyle); if (menuStyleBtn.visible && distStyle < 120) { showStylePage(true); stylePageOpen = true; return; } // Check if a boss button was pressed for (var i = 0; i < bossSelectButtons.length; i++) { var btn = bossSelectButtons[i]; var dx = x - btn.x; var dy = y - btn.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 120) { // Boss chosen bossChoice = bossDefs[btn.bossIndex]; startGame(); return; } } } else { // Start drag if inside player var dx = x - player.x; var dy = y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius) { dragNode = player; lastMoveX = x; lastMoveY = y; } } }; game.up = function (x, y, obj) { dragNode = null; }; function handleMove(x, y, obj) { if (dragNode && gameStarted) { // Clamp to arena var px = x, py = y; var minX = ARENA_X + player.radius; var maxX = ARENA_X + ARENA_W - player.radius; var minY = ARENA_Y + player.radius; var maxY = ARENA_Y + ARENA_H - player.radius; px = Math.max(minX, Math.min(maxX, px)); py = Math.max(minY, Math.min(maxY, py)); dragNode.x = px; dragNode.y = py; lastMoveX = px; lastMoveY = py; } // Style page hover feedback if (stylePageBg && stylePageBg.visible) { for (var i = 0; i < styleSelectButtons.length; i++) { var btn = styleSelectButtons[i]; var dx = x - btn.x; var dy = y - btn.y; var dist = Math.sqrt(dx * dx + dy * dy); btn._hovered = dist < 200; if (typeof btn.update === "function") btn.update(); } } } game.move = handleMove; // Start game after boss selection function startGame() { // Hide boss select UI bossSelectTxt.visible = false; bossSelectSubTxt.visible = false; menuBg.visible = false; for (var i = 0; i < bossSelectButtons.length; i++) { bossSelectButtons[i].visible = false; bossSelectButtons[i]._label.visible = false; if (bossSelectButtons[i]._diffDots) { for (var d = 0; d < bossSelectButtons[i]._diffDots.length; d++) { bossSelectButtons[i]._diffDots[d].visible = false; } } } // Hide style select UI for (var i = 0; i < styleSelectButtons.length; i++) { styleSelectButtons[i].visible = false; styleSelectButtons[i]._label.visible = false; styleSelectButtons[i]._desc.visible = false; } stylePageBg.visible = false; stylePageTitle.visible = false; stylePageBody.visible = false; stylePageBackBtn.visible = false; stylePageBackLabel.visible = false; menuStyleBtn.visible = false; menuStyleBtnLabel.visible = false; // Hide all other irrelevant UI during gameplay bossSelectTxt.visible = false; bossSelectSubTxt.visible = false; menuBg.visible = false; for (var i = 0; i < bossSelectButtons.length; i++) { bossSelectButtons[i].visible = false; bossSelectButtons[i]._label.visible = false; if (bossSelectButtons[i]._diffDots) { for (var d = 0; d < bossSelectButtons[i]._diffDots.length; d++) { bossSelectButtons[i]._diffDots[d].visible = false; } } } // Show arena and GUI arenaBorder.visible = true; timeTxt.visible = true; bossNameTxt.visible = true; // --- Play boss-specific music --- if (bossChoice && bossChoice.name) { // Stop any currently playing music (including menu music) LK.stopMusic(); // Map boss name to music id var bossMusicMap = { "Inferno": "music_inferno", "Verdant": "music_verdant", "Solaris": "music_solaris", "Abyss": "music_abyss", "Tempest": "music_tempest", "Prism": "music_prism", "Obsidian": "music_obsidian", "Blizzard": "music_blizzard", "Ember": "music_ember" }; var musicId = bossMusicMap[bossChoice.name]; if (musicId) { LK.playMusic(musicId); } } // --- Boss-specific animated background setup --- bossBgContainer.removeChildren(); bossBgContainer.visible = true; var bgAssetId = null; var bgColor = 0x222222; var bgAlpha = 0.13; var bgAnimType = null; // Pick background asset/color/animation based on boss if (bossChoice.name === "Inferno") { bgAssetId = "boss_red"; bgColor = 0xe74c3c; bgAlpha = 0.10; bgAnimType = "pulse"; } else if (bossChoice.name === "Verdant") { bgAssetId = "boss_green"; bgColor = 0x27ae60; bgAlpha = 0.10; bgAnimType = "wave"; } else if (bossChoice.name === "Solaris") { bgAssetId = "boss_yellow"; bgColor = 0xf1c40f; bgAlpha = 0.10; bgAnimType = "shine"; } else if (bossChoice.name === "Abyss") { bgAssetId = "boss_gray"; bgColor = 0x7f8c8d; bgAlpha = 0.10; bgAnimType = "vortex"; } else if (bossChoice.name === "Tempest") { bgAssetId = "boss_blue"; bgColor = 0x2980b9; bgAlpha = 0.10; bgAnimType = "wind"; } else if (bossChoice.name === "Prism") { bgAssetId = "boss_purple"; bgColor = 0x9b59b6; bgAlpha = 0.10; bgAnimType = "rainbow"; } else { bgAssetId = "arena_border"; bgColor = 0x222222; bgAlpha = 0.10; bgAnimType = null; } // Add a large faded background shape for the boss var bossBgShape = LK.getAsset(bgAssetId, { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 7.5, scaleY: 7.5 }); bossBgShape.alpha = bgAlpha; bossBgContainer.addChild(bossBgShape); // Optionally add a second, smaller animated overlay for some bosses if (bossChoice.name === "Prism") { // Add 6 colored faded ellipses in a circle for rainbow effect for (var c = 0; c < 6; c++) { var colorId = ["boss_red", "boss_yellow", "boss_green", "boss_blue", "boss_purple", "boss_gray"][c]; var angle = Math.PI * 2 * c / 6; var rainbow = LK.getAsset(colorId, { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + Math.cos(angle) * 500, y: 2732 / 2 + Math.sin(angle) * 500, scaleX: 2.7, scaleY: 2.7 }); rainbow.alpha = 0.09; bossBgContainer.addChild(rainbow); } } // Store animation type for update bossBgContainer._animType = bgAnimType; bossBgContainer._animTick = 0; // Create player drop shadow var playerShadow = LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 18, y: 2732 / 2 + 600 + 18, scaleX: 1.08, scaleY: 1.08 }); playerShadow.alpha = 0.18; game.addChild(playerShadow); // Create player player = new Player(); player.x = 2048 / 2; player.y = 2732 / 2 + 600; game.addChild(player); // Add soft white glow (behind player) var playerGlow = LK.getAsset('player', { anchorX: 0.5, anchorY: 0.5, x: player.x, y: player.y, scaleX: 1.25, scaleY: 1.25 }); playerGlow.alpha = 0.10; game.addChild(playerGlow); // Animate glow pulse playerGlow._pulseTick = 0; playerGlow.update = function () { this._pulseTick++; var t = this._pulseTick; this.scale.x = this.scale.y = 1.25 + Math.sin(t / 18) * 0.08; this.alpha = 0.10 + 0.04 * Math.abs(Math.sin(t / 18)); this.x = player.x; this.y = player.y; }; // Create boss drop shadow var bossShadow = LK.getAsset(bossChoice.asset, { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 22, y: 2732 / 2 - 700 + 22, scaleX: 1.10, scaleY: 1.10 }); bossShadow.alpha = 0.15; game.addChild(bossShadow); // Create boss boss = new Boss(); boss.init(bossChoice); boss.x = 2048 / 2; boss.y = 2732 / 2 - 700; game.addChild(boss); // Add soft white glow (behind boss) var bossGlow = LK.getAsset(bossChoice.asset, { anchorX: 0.5, anchorY: 0.5, x: boss.x, y: boss.y, scaleX: 1.32, scaleY: 1.32 }); bossGlow.alpha = 0.09; game.addChild(bossGlow); // Animate glow pulse bossGlow._pulseTick = 0; bossGlow.update = function () { this._pulseTick++; var t = this._pulseTick; this.scale.x = this.scale.y = 1.32 + Math.sin(t / 22) * 0.10; this.alpha = 0.09 + 0.03 * Math.abs(Math.sin(t / 22)); this.x = boss.x; this.y = boss.y; }; // Set boss name bossNameTxt.setText(bossChoice.name); // Set boss health based on difficulty (harder bosses = more health) bossMaxHealth = 100 + bossChoice.difficulty * 60; bossHealth = bossMaxHealth; // Set boss lives: most bosses have 3 lives, hardest (difficulty >= 5) have 4, easiest (difficulty <= 1) have 2 if (bossChoice.difficulty >= 5) { bossMaxLives = 4; } else if (bossChoice.difficulty <= 1) { bossMaxLives = 2; } else { bossMaxLives = 3; } bossLives = bossMaxLives; bossLivesTxt.setText('Lives: ' + bossLives + ' / ' + bossMaxLives); bossLivesTxt.visible = true; // --- Every 20 seconds, deal 1 damage to boss --- if (typeof bossDamageTimer !== "undefined") { LK.clearInterval(bossDamageTimer); } bossDamageTimer = LK.setInterval(function () { if (gameStarted && boss && bossLives > 0) { bossLives = Math.max(0, bossLives - 1); // Update boss lives text if (typeof bossLivesTxt !== "undefined" && bossLivesTxt.visible !== false) { bossLivesTxt.setText('Lives: ' + bossLives + ' / ' + bossMaxLives); } // Flash boss hit effect if (boss && boss.asset) { LK.effects.flashObject(boss.asset, 0xffffff, 350); // --- Slash effect: show a white slash across the boss for 0.5s --- var slash = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: boss.x, y: boss.y, scaleX: 0.7, scaleY: 0.08, rotation: Math.PI / 4 }); slash.tint = 0xffffff; slash.alpha = 0.85; game.addChild(slash); // Animate fade out and remove after 0.5s tween(slash, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { if (slash.parent) slash.parent.removeChild(slash); } }); } // If boss lives reach zero, show boss story screen before win if (bossLives === 0) { // Show boss story screen after boss defeat (no dialog/speech bubble) var showBossStoryScreen = function showBossStoryScreen() { // Show boss story overlay if (typeof bossStoryBg === "undefined") { bossStoryBg = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 0.8, scaleY: 0.8 }); bossStoryBg.alpha = 0.95; game.addChild(bossStoryBg); } else { bossStoryBg.visible = true; } if (typeof bossStoryTitle === "undefined") { bossStoryTitle = new Text2('Boss Defeated!', { size: 140, fill: 0xFFECB3, font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); bossStoryTitle.anchor.set(0.5, 0); bossStoryTitle.x = 2048 / 2; bossStoryTitle.y = 2732 / 2 - 420; game.addChild(bossStoryTitle); } else { bossStoryTitle.visible = true; } if (typeof bossStoryBody === "undefined") { bossStoryBody = new Text2('', { size: 90, fill: 0xFFFFFF, font: "GillSans-Bold,Impact,'Arial Black',Tahoma" }); bossStoryBody.anchor.set(0.5, 0); bossStoryBody.x = 2048 / 2; bossStoryBody.y = 2732 / 2 - 220; game.addChild(bossStoryBody); } else { bossStoryBody.visible = true; } if (typeof bossStoryContinueBtn === "undefined") { bossStoryContinueBtn = LK.getAsset('arena_border', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 420, scaleX: 0.18, scaleY: 0.08 }); bossStoryContinueBtn.alpha = 0.85; bossStoryContinueBtn.tint = 0x4CAF50; game.addChild(bossStoryContinueBtn); bossStoryContinueLabel = new Text2('Continue', { size: 90, fill: 0xFFFFFF }); bossStoryContinueLabel.anchor.set(0.5, 0.5); bossStoryContinueLabel.x = bossStoryContinueBtn.x; bossStoryContinueLabel.y = bossStoryContinueBtn.y + 2; game.addChild(bossStoryContinueLabel); } else { bossStoryContinueBtn.visible = true; bossStoryContinueLabel.visible = true; } // Set boss story text based on bossChoice var storyMap = { "Inferno": "Inferno was once the god of fire,\na mighty guardian of flame and warmth.\n\nWhen the Celestials descended and warred with the gods,\nInferno failed to protect his kin.\n\nStripped of his divine title,\nhe now roams the world as a troublemaker with fire powers,\nhis pride smoldering and his purpose lost.", "Verdant": "Verdant, spirit of the wild,\ngrew jealous of the sun.\n\nIts vines now lash out at all\nwho enter its domain.", "Solaris": "Solaris, the radiant,\nonce brought light to the world.\n\nNow, it blinds and scorches\nwith unyielding fury.", "Abyss": "Abyss, the endless void,\nwas a silent watcher.\n\nIts hunger for souls has turned it\ninto a devourer of hope.", "Tempest": "Tempest, the wandering wind,\nwas a gentle breeze.\n\nBetrayed, it became a storm\nthat never rests.", "Prism": "Prism, the fractured light,\nwas whole once.\n\nBroken by envy,\nit now splits reality itself.", "Obsidian": "Obsidian, the silent shadow,\nwas a protector.\n\nDarkness twisted it into\na bringer of despair.", "Blizzard": "Blizzard, the frozen heart,\nonce cherished warmth.\n\nNow, it seeks to encase\nthe world in ice.", "Ember": "Ember, the last spark,\nclings to life.\n\nIts loneliness fuels a desperate,\nexplosive rage." }; var bossStory = storyMap[bossChoice && bossChoice.name] || "This boss has a mysterious past..."; bossStoryBody.setText(bossStory); // Set up tap to continue game._bossStoryScreenActive = true; game._bossStoryScreenHandler = function (x, y, obj) { // Only respond if story screen is active if (!game._bossStoryScreenActive) return; var dx = x - bossStoryContinueBtn.x; var dy = y - bossStoryContinueBtn.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 300) { // Hide story UI bossStoryBg.visible = false; bossStoryTitle.visible = false; bossStoryBody.visible = false; bossStoryContinueBtn.visible = false; bossStoryContinueLabel.visible = false; game._bossStoryScreenActive = false; // Remove handler game.down = originalGameDown; // Show win LK.showYouWin(); } }; game.down = game._bossStoryScreenHandler; }; // Set game.down to dialog handler // Hide gameplay UI timeTxt.visible = false; bossNameTxt.visible = false; bossLivesTxt.visible = false; bossHealthBar.visible = false; bossHealthBarBg.visible = false; bossHealthBarFrame.visible = false; arenaBorder.visible = false; if (bossBgContainer) bossBgContainer.visible = false; // --- Boss dialog lines (speech bubble) --- // (Speech bubble and dialog removed) if (boss && boss.asset) { // Animate boss falling out of stage, then show story var startY = boss.y; var endY = boss.y + 1200; var startAlpha = boss.asset.alpha; tween(boss, { y: endY }, { duration: 900, onUpdate: function onUpdate() { // Fade out boss asset as it falls if (boss.asset) boss.asset.alpha = Math.max(0, startAlpha * (1 - (boss.y - startY) / (endY - startY))); }, onFinish: function onFinish() { if (boss && boss.parent) boss.parent.removeChild(boss); // Now show the story screen showBossStoryScreen(); } }); } else { // If boss asset missing, just show story showBossStoryScreen(); } return; } } }, 20000); // 20,000 ms = 20 seconds // Show boss healthbar bossHealthBarBg.visible = true; bossHealthBar.visible = true; bossHealthBarFrame.visible = true; // Reset state projectiles = []; survivalTime = 0; playerLives = 5; livesTxt.setText('Lives: ' + playerLives); gameStarted = true; } // Boss attack patterns function bossAttack() { if (!boss || !player) return; var bossType = bossChoice; var projAsset = bossType.proj; var cx = boss.x, cy = boss.y; var px = player.x, py = player.y; var phase = boss.phase; var n, angle, i, p, speed, spread, baseAngle, offset, j, delay, targetX, targetY; // Each boss has a set of possible attacks, randomly chosen var attackList = []; if (bossType.name === "Inferno") { attackList = ["spiral", // Rotating spiral "double_spiral", // Two spirals, offset "wall", // Horizontal/vertical wall "cross", // Cross pattern "aimed_burst" // Burst of aimed shots ]; } else if (bossType.name === "Verdant") { attackList = ["burst", // All directions "flower", // Petal/flower pattern "arc", // Arc sweep "ring", // Expanding ring "random_bounce" // Random directions ]; } else if (bossType.name === "Solaris") { attackList = ["aimed", // Aimed shots "triple_aimed", // Three aimed shots "sniper", // Single fast shot "spread", // Wide spread "rain" // Rain from above ]; } else if (bossType.name === "Abyss") { attackList = ["vortex", // swirling projectiles "blackhole", // projectiles curve toward player "void_ring", // slow expanding ring "chaos", // random directions, random speeds "gravity" // projectiles accelerate toward player ]; } else if (bossType.name === "Tempest") { attackList = ["wind", // fast horizontal projectiles "gust", // diagonal waves "cyclone", // spiral from arena edge "twister", // projectiles curve in S-shape "scatter" // random scatter ]; } else if (bossType.name === "Prism") { attackList = ["prism", // rainbow spread "reflect", // bouncing projectiles "laser", // straight fast lines "split", // projectiles split mid-air "mirror" // projectiles reflect off walls ]; } else if (bossType.name === "Obsidian") { // Black boss: chaos, void_ring, sniper, mirror, gravity // Make Obsidian harder: more chaos, more projectiles, faster, less delay attackList = ["chaos", // random directions, random speeds "void_ring", // slow expanding ring "sniper", // single fast shot with indicator "mirror", // bouncing projectiles "gravity" // projectiles accelerate toward player ]; } else if (bossType.name === "Blizzard") { // White boss: ring, wall, cross, rain, spread, snowstorm (signature attack is now 'blizzard_maelstrom') attackList = ["ring", // expanding ring "wall", // wall of projectiles "cross", // cross pattern "rain", // rain from above "spread", // wide spread "snowstorm" // new snowstorm attack ]; } else if (bossType.name === "Ember") { // Orange boss: burst, flower, arc, aimed_burst, random_bounce attackList = ["burst", // all directions, embers spin "flower", // petal/flower pattern "arc", // arc sweep "aimed_burst", // burst of aimed shots "random_bounce" // random bounce ]; } else if (bossType.name === "Titan") { // Titan: wall, cross, stomp, quake, titan_crush (signature) attackList = ["wall", // horizontal/vertical wall "cross", // cross pattern "stomp", // heavy downward projectiles "quake", // ring + random bounces "titan_crush" // signature: 2 huge walls from both sides, then cross ]; } else if (bossType.name === "Venom") { // Venom: burst, arc, venom_spread, poison_cloud, toxic_wave (signature) attackList = ["burst", // all directions "arc", // arc sweep "venom_spread", // aimed spread with poison "poison_cloud", // slow, bouncing poison balls "toxic_wave" // signature: 2 waves of fast, bouncing poison projectiles ]; } else { attackList = [bossType.pattern]; } // Cycle through attacks in order for predictability if (!boss._attackIndex) boss._attackIndex = 0; // --- Signature attack logic --- // Every 4th attack, use the boss's signature attack var signatureMap = { "Inferno": "hellfire", "Verdant": "overgrow", "Solaris": "solar_beam", "Abyss": "void_maw", // Tempest's new signature attack "Tempest": "storm_barrage", "Prism": "prismatic_burst", // Obsidian's new signature attack (changed to void_strike) "Obsidian": "void_strike", "Blizzard": "blizzard_maelstrom", "Ember": "supernova", "Titan": "titan_crush", "Venom": "toxic_wave" }; var signatureAttack = signatureMap[bossType.name]; var useSignature = boss._attackIndex > 0 && boss._attackIndex % 4 === 0 && signatureAttack; var pattern; if (useSignature) { pattern = signatureAttack; // Play unique signature sound for each boss var sigSoundMap = { "Inferno": "sig_inferno", "Verdant": "sig_verdant", "Solaris": "sig_solaris", "Abyss": "sig_abyss", "Tempest": "sig_tempest", "Prism": "sig_prism", "Obsidian": "sig_obsidian", "Blizzard": "sig_blizzard", "Ember": "sig_ember", "Titan": "sig_titan", "Venom": "sig_venom" }; var sigSoundId = sigSoundMap[bossType.name]; if (sigSoundId) { var sigSound = LK.getSound(sigSoundId); if (sigSound) sigSound.play(); } } else { pattern = attackList[boss._attackIndex % attackList.length]; } boss._attackIndex++; // --- Titan Attacks --- if (pattern === "stomp") { // Heavy downward projectiles, slow but big // Make Titan easier: fewer projectiles, slower speed n = 2 + Math.floor(phase / 3); for (i = 0; i < n; i++) { p = new Projectile(); p.init("stomp", projAsset); p.x = ARENA_X + ARENA_W / (n + 1) * (i + 1); p.y = ARENA_Y + 40; p.speedX = 0; p.speedY = 4.2 + phase * 0.12; p.radius = 60; if (p.asset) { p.asset.scale.x = 1.3; p.asset.scale.y = 1.3; p.asset.alpha = 0.85; } projectiles.push(p); game.addChild(p); } return; } if (pattern === "quake") { // Ring + random bounces // Make Titan easier: fewer projectiles, slower speed n = 5 + Math.floor(phase * 0.5); for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("quake", projAsset); p.x = cx; p.y = cy; speed = 1.7 + phase * 0.09; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; p.bounce = true; projectiles.push(p); game.addChild(p); } return; } // --- Venom Attacks --- if (pattern === "venom_spread") { // Aimed spread with poison // Make Venom easier: fewer projectiles, slower speed, less spread n = 3; var toPlayer = Math.atan2(py - cy, px - cx); spread = Math.PI / 7; for (i = 0; i < n; i++) { angle = toPlayer + spread * (i - (n - 1) / 2) / n; p = new Projectile(); p.init("venom_spread", projAsset); p.x = cx; p.y = cy; speed = 3.2 + phase * 0.12; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; p.bounce = Math.random() < 0.3; projectiles.push(p); game.addChild(p); } return; } if (pattern === "poison_cloud") { // Slow, bouncing poison balls // Make Venom easier: fewer projectiles, slower speed n = 2 + Math.floor(phase / 3); for (i = 0; i < n; i++) { angle = Math.random() * 2 * Math.PI; p = new Projectile(); p.init("poison_cloud", projAsset); p.x = cx; p.y = cy; speed = 1.2 + Math.random() * 0.7 + phase * 0.05; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; p.bounce = true; p.radius = 40; if (p.asset) { p.asset.alpha = 0.7; } projectiles.push(p); game.addChild(p); } return; } // --- Signature Attacks for 4th row bosses --- // Spectra: Prismatic Burst (already implemented as pattern "prismatic_burst") // Titan: Titan Crush - 2 huge walls from left and right, then cross if (pattern === "titan_crush") { // 2 huge vertical walls from left and right, then cross // Make Titan's signature easier: fewer walls, slower speed var wallN = 6 + Math.floor(phase * 0.4); for (i = 0; i < wallN; i++) { // Left wall p = new Projectile(); p.init("titan_crush", projAsset); p.x = ARENA_X + 40; p.y = ARENA_Y + ARENA_H / (wallN + 1) * (i + 1); p.speedX = 5.2 + phase * 0.18; p.speedY = 0; p.radius = 50; if (p.asset) { p.asset.scale.x = 1.2; p.asset.scale.y = 1.2; p.asset.alpha = 0.85; } projectiles.push(p); game.addChild(p); // Right wall p = new Projectile(); p.init("titan_crush", projAsset); p.x = ARENA_X + ARENA_W - 40; p.y = ARENA_Y + ARENA_H / (wallN + 1) * (i + 1); p.speedX = -5.2 - phase * 0.18; p.speedY = 0; p.radius = 50; if (p.asset) { p.asset.scale.x = 1.2; p.asset.scale.y = 1.2; p.asset.alpha = 0.85; } projectiles.push(p); game.addChild(p); } // After 0.7s, cross pattern from center (slower, easier) LK.setTimeout(function () { var dirs = [[0, 1], [0, -1], [1, 0], [-1, 0]]; for (var j = 0; j < dirs.length; j++) { var p2 = new Projectile(); p2.init("titan_crush_cross", projAsset); p2.x = cx; p2.y = cy; var speed2 = 6.2 + phase * 0.18; p2.speedX = dirs[j][0] * speed2; p2.speedY = dirs[j][1] * speed2; p2.radius = 60; if (p2.asset) { p2.asset.scale.x = 1.3; p2.asset.scale.y = 1.3; p2.asset.alpha = 0.85; } projectiles.push(p2); game.addChild(p2); } }, 700); return; } // Venom: Toxic Wave - 2 waves of fast, bouncing poison projectiles if (pattern === "toxic_wave") { // Make Venom's signature easier: only 1 wave, fewer projectiles, slower speed var waveCount = 1; var waveDelay = 350; for (var w = 0; w < waveCount; w++) { (function (waveIdx) { LK.setTimeout(function () { var n = 4 + Math.floor(phase * 0.4); for (i = 0; i < n; i++) { angle = Math.PI / 2 + (i - (n - 1) / 2) * 0.18; p = new Projectile(); p.init("toxic_wave", projAsset); p.x = cx; p.y = cy; speed = 4.2 + phase * 0.13 + waveIdx * 0.3; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; p.bounce = true; p.radius = 38; if (p.asset) { p.asset.alpha = 0.8; } projectiles.push(p); game.addChild(p); } }, waveIdx * waveDelay); })(w); } return; } // --- Signature Attacks --- // Inferno: Hellfire - massive spiral of fast projectiles if (pattern === "hellfire") { n = 18 + Math.floor(phase * 1.2); baseAngle = boss.timeAlive * 0.12 % (2 * Math.PI); for (i = 0; i < n; i++) { angle = baseAngle + 2 * Math.PI / n * i; p = new Projectile(); p.init("hellfire", projAsset); p.x = cx; p.y = cy; speed = 8.5 + phase * 0.5; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } return; } // Verdant: Overgrow - 2 expanding rings, fewer projectiles, slower, more delay for easier dodging if (pattern === "overgrow") { var ringCount = 2; // was 3 for (var r = 0; r < ringCount; r++) { (function (ringIdx) { LK.setTimeout(function () { var ringN = 6 + Math.floor(phase * 0.5) + ringIdx * 1; // was 8 + ... + ringIdx*2 var ringSpeed = 1.5 + phase * 0.09 + ringIdx * 0.4; // was 2.2 + ... + ringIdx*0.7 for (i = 0; i < ringN; i++) { angle = 2 * Math.PI / ringN * i; p = new Projectile(); p.init("overgrow", projAsset); p.x = cx; p.y = cy; p.speedX = Math.cos(angle) * ringSpeed; p.speedY = Math.sin(angle) * ringSpeed; p.bounce = true; projectiles.push(p); game.addChild(p); } }, ringIdx * 400); // was 250, now 400ms between rings })(r); } return; } // Solaris: Solar Beam - giant laser line with warning, then instant fast shot if (pattern === "solar_beam") { var toPlayer = Math.atan2(py - cy, px - cx); // Draw big warning indicator var indicator = LK.getAsset('proj_' + bossType.asset.split('_')[1], { anchorX: 0.5, anchorY: 0.5, x: cx, y: cy, scaleX: 0.7, scaleY: 0.22, rotation: toPlayer }); indicator.width = 1800; indicator.alpha = 0.55; game.addChild(indicator); tween(indicator, { alpha: 0 }, { duration: 900, onFinish: function onFinish() { if (indicator.parent) indicator.parent.removeChild(indicator); } }); // Fire huge, fast projectile after warning LK.setTimeout(function () { var p = new Projectile(); p.init("solar_beam", projAsset); p.x = cx; p.y = cy; var speed = 18 + phase * 0.7; p.speedX = Math.cos(toPlayer) * speed; p.speedY = Math.sin(toPlayer) * speed; p.radius = 80; // much larger hitbox if (p.asset) { p.asset.scale.x = 2.2; p.asset.scale.y = 2.2; p.asset.alpha = 0.85; } projectiles.push(p); game.addChild(p); }, 900); return; } // Abyss: Void Maw - giant void appears, pulls player toward center, then explodes in a massive ring if (pattern === "void_maw") { // 1. Spawn a giant void at arena center var voidCenterX = ARENA_X + ARENA_W / 2; var voidCenterY = ARENA_Y + ARENA_H / 2; var voidProj = new Projectile(); voidProj.init("void_maw", projAsset); voidProj.x = voidCenterX; voidProj.y = voidCenterY; voidProj.speedX = 0; voidProj.speedY = 0; voidProj.radius = 180; if (voidProj.asset) { voidProj.asset.scale.x = 3.2; voidProj.asset.scale.y = 3.2; voidProj.asset.alpha = 0.7; } voidProj._voidMawActive = true; projectiles.push(voidProj); game.addChild(voidProj); // 2. Pull player toward center for 1.5s (simulate gravity, easier) var pullDuration = 1500; // was 1200 var pullStrength = 0.13 + 0.02 * phase; // was 0.22 + 0.04 * phase var pullFrames = Math.floor(pullDuration / 16.67); var pullTick = 0; voidProj._pullInterval = LK.setInterval(function () { if (!player || !voidProj.parent) return; var dx = voidCenterX - player.x; var dy = voidCenterY - player.y; var dist = Math.sqrt(dx * dx + dy * dy) || 1; // Only pull if not already at center if (dist > 10) { player.x += dx / dist * pullStrength * 18; player.y += dy / dist * pullStrength * 18; // Clamp to arena var minX = ARENA_X + player.radius; var maxX = ARENA_X + ARENA_W - player.radius; var minY = ARENA_Y + player.radius; var maxY = ARENA_Y + ARENA_H - player.radius; player.x = Math.max(minX, Math.min(maxX, player.x)); player.y = Math.max(minY, Math.min(maxY, player.y)); } pullTick++; if (pullTick >= pullFrames) { LK.clearInterval(voidProj._pullInterval); } }, 16); // 3. Animate void growing, then shrinking if (voidProj.asset) { var growTick = 0; voidProj.update = function () { growTick++; if (growTick < pullFrames / 2) { // Grow var scale = 3.2 + growTick * 0.012; this.asset.scale.x = this.asset.scale.y = scale; this.asset.alpha = 0.7 + 0.15 * Math.sin(growTick / 10); } else { // Shrink var scale = 3.2 + (pullFrames - growTick) * 0.012; this.asset.scale.x = this.asset.scale.y = Math.max(2.2, scale); this.asset.alpha = 0.7 + 0.15 * Math.sin(growTick / 10); } }; } // 4. After 1.5s, explode in a smaller, slower ring and remove void LK.setTimeout(function () { // Easier: fewer projectiles, slower speed var ringN = 10 + Math.floor(phase * 0.7); // was 18 + Math.floor(phase * 1.2) var ringSpeed = 3.8 + phase * 0.13; // was 6.2 + phase * 0.25 for (var i = 0; i < ringN; i++) { var angle = 2 * Math.PI / ringN * i; var p = new Projectile(); p.init("void_maw_burst", projAsset); p.x = voidCenterX; p.y = voidCenterY; p.speedX = Math.cos(angle) * ringSpeed; p.speedY = Math.sin(angle) * ringSpeed; p.radius = 38; if (p.asset) { p.asset.alpha = 0.85; p.asset.scale.x = p.asset.scale.y = 1.18; } projectiles.push(p); game.addChild(p); } // Remove the void if (voidProj.parent) voidProj.parent.removeChild(voidProj); var idx = projectiles.indexOf(voidProj); if (idx >= 0) projectiles.splice(idx, 1); }, pullDuration); return; } // Tempest: Storm Barrage - waves of projectiles from all four sides, with random gaps and swirling wind projectiles if (pattern === "storm_barrage") { var sides = ["top", "bottom", "left", "right"]; var barrageWaves = 3 + Math.floor(phase / 2); var waveDelay = 320; for (var w = 0; w < barrageWaves; w++) { (function (waveIdx) { LK.setTimeout(function () { for (var s = 0; s < sides.length; s++) { var side = sides[s]; var count = 6 + Math.floor(phase * 0.5); var gapIdx = Math.floor(Math.random() * count); for (var i = 0; i < count; i++) { if (i === gapIdx) continue; // leave a gap for dodging var p = new Projectile(); p.init("storm_barrage", projAsset); if (side === "top") { p.x = ARENA_X + ARENA_W / (count + 1) * (i + 1); p.y = ARENA_Y - 40; p.speedX = 0; p.speedY = 6.5 + phase * 0.18 + waveIdx * 0.3; } else if (side === "bottom") { p.x = ARENA_X + ARENA_W / (count + 1) * (i + 1); p.y = ARENA_Y + ARENA_H + 40; p.speedX = 0; p.speedY = -6.5 - phase * 0.18 - waveIdx * 0.3; } else if (side === "left") { p.x = ARENA_X - 40; p.y = ARENA_Y + ARENA_H / (count + 1) * (i + 1); p.speedX = 6.5 + phase * 0.18 + waveIdx * 0.3; p.speedY = 0; } else if (side === "right") { p.x = ARENA_X + ARENA_W + 40; p.y = ARENA_Y + ARENA_H / (count + 1) * (i + 1); p.speedX = -6.5 - phase * 0.18 - waveIdx * 0.3; p.speedY = 0; } p.bounce = false; projectiles.push(p); game.addChild(p); } } // Add swirling wind projectiles from boss center for extra challenge var swirlN = 2 + Math.floor(phase / 2); for (var j = 0; j < swirlN; j++) { var swirlAngle = boss.timeAlive * 0.09 + 2 * Math.PI / swirlN * j + waveIdx * 0.3; var swirlP = new Projectile(); swirlP.init("storm_barrage_swirl", projAsset); swirlP.x = cx; swirlP.y = cy; var swirlSpeed = 4.2 + phase * 0.13 + waveIdx * 0.2; swirlP.speedX = Math.cos(swirlAngle) * swirlSpeed; swirlP.speedY = Math.sin(swirlAngle) * swirlSpeed; swirlP.bounce = Math.random() < 0.3; projectiles.push(swirlP); game.addChild(swirlP); } }, waveIdx * waveDelay); })(w); } return; } // Prism: Prismatic Burst - 6 rainbow rings, each a different color, expanding out if (pattern === "prismatic_burst") { var colorIds = ["proj_red", "proj_yellow", "proj_green", "proj_blue", "proj_purple", "proj_gray"]; for (var c = 0; c < 6; c++) { (function (colorIdx) { LK.setTimeout(function () { var ringN = 7 + Math.floor(phase * 0.7); var ringSpeed = 3.5 + phase * 0.18 + colorIdx * 0.5; for (i = 0; i < ringN; i++) { angle = 2 * Math.PI / ringN * i; p = new Projectile(); p.init("prismatic_burst", colorIds[colorIdx]); p.x = cx; p.y = cy; p.speedX = Math.cos(angle) * ringSpeed; p.speedY = Math.sin(angle) * ringSpeed; projectiles.push(p); game.addChild(p); } }, colorIdx * 120); })(c); } return; } // Obsidian: Shatter - 5 lines of projectiles radiate out, then each line shatters into a spread after a delay if (pattern === "obsidian_shatter") { var lineCount = 5; var baseN = 4 + Math.floor(phase * 0.5); var baseSpeed = 4.2 + phase * 0.18; var shatterDelay = 600; for (var l = 0; l < lineCount; l++) { (function (lineIdx) { var angle = boss.timeAlive * 0.07 + 2 * Math.PI / lineCount * lineIdx; for (var i = 0; i < baseN; i++) { (function (segIdx) { var p = new Projectile(); p.init("obsidian_shatter", projAsset); p.x = cx + Math.cos(angle) * (60 + segIdx * 60); p.y = cy + Math.sin(angle) * (60 + segIdx * 60); p.speedX = Math.cos(angle) * baseSpeed; p.speedY = Math.sin(angle) * baseSpeed; p._shatterOriginAngle = angle; projectiles.push(p); game.addChild(p); // After a delay, shatter this projectile into a spread LK.setTimeout(function () { if (!p.parent) return; var shatterN = 4 + Math.floor(phase * 0.4); var shatterSpread = Math.PI / 2.2; for (var si = 0; si < shatterN; si++) { var shatterAngle = angle - shatterSpread / 2 + shatterSpread * (si / (shatterN - 1)); var sp = new Projectile(); sp.init("obsidian_shatter_frag", projAsset); sp.x = p.x; sp.y = p.y; sp.speedX = Math.cos(shatterAngle) * (3.2 + phase * 0.13); sp.speedY = Math.sin(shatterAngle) * (3.2 + phase * 0.13); // 30% chance to bounce for chaos if (Math.random() < 0.3) sp.bounce = true; projectiles.push(sp); game.addChild(sp); } p.destroy(); var idx = projectiles.indexOf(p); if (idx >= 0) projectiles.splice(idx, 1); }, shatterDelay + segIdx * 60); })(i); } })(l); } return; } // Obsidian: Void Strike - 4 sniper shots from arena edges, all aimed at player, with indicators if (pattern === "void_strike") { // Obsidian: Make signature attack harder: more snipers, faster, less warning if (bossType.name === "Obsidian") { // 8 snipers from arena edges and midpoints var edgePoints = [[ARENA_X + 80, ARENA_Y + 80], [ARENA_X + ARENA_W - 80, ARENA_Y + 80], [ARENA_X + 80, ARENA_Y + ARENA_H - 80], [ARENA_X + ARENA_W - 80, ARENA_Y + ARENA_H - 80], [ARENA_X + ARENA_W / 2, ARENA_Y + 80], [ARENA_X + ARENA_W / 2, ARENA_Y + ARENA_H - 80], [ARENA_X + 80, ARENA_Y + ARENA_H / 2], [ARENA_X + ARENA_W - 80, ARENA_Y + ARENA_H / 2]]; for (i = 0; i < edgePoints.length; i++) { (function (idx) { var ex = edgePoints[idx][0]; var ey = edgePoints[idx][1]; var toPlayer = Math.atan2(player.y - ey, player.x - ex); var indicator = LK.getAsset('proj_black', { anchorX: 0.5, anchorY: 0.5, x: ex, y: ey, scaleX: 0.5, scaleY: 0.15, rotation: toPlayer }); indicator.width = 1200; indicator.alpha = 0.45; game.addChild(indicator); tween(indicator, { alpha: 0 }, { duration: 600, // shorter warning onFinish: function onFinish() { if (indicator.parent) indicator.parent.removeChild(indicator); } }); LK.setTimeout(function () { var p = new Projectile(); p.init("void_strike", projAsset); p.x = ex; p.y = ey; var speed = 18 + phase * 0.9; // much faster p.speedX = Math.cos(toPlayer) * speed; p.speedY = Math.sin(toPlayer) * speed; // Obsidian projectiles do NOT bounce p.bounce = false; projectiles.push(p); game.addChild(p); }, 600); })(i); } return; } else { var edgePoints = [[ARENA_X + 80, ARENA_Y + 80], [ARENA_X + ARENA_W - 80, ARENA_Y + 80], [ARENA_X + 80, ARENA_Y + ARENA_H - 80], [ARENA_X + ARENA_W - 80, ARENA_Y + ARENA_H - 80]]; for (i = 0; i < edgePoints.length; i++) { (function (idx) { var ex = edgePoints[idx][0]; var ey = edgePoints[idx][1]; var toPlayer = Math.atan2(player.y - ey, player.x - ex); var indicator = LK.getAsset('proj_black', { anchorX: 0.5, anchorY: 0.5, x: ex, y: ey, scaleX: 0.5, scaleY: 0.15, rotation: toPlayer }); indicator.width = 1200; indicator.alpha = 0.45; game.addChild(indicator); tween(indicator, { alpha: 0 }, { duration: 900, onFinish: function onFinish() { if (indicator.parent) indicator.parent.removeChild(indicator); } }); LK.setTimeout(function () { var p = new Projectile(); p.init("void_strike", projAsset); p.x = ex; p.y = ey; var speed = 13 + phase * 0.7; p.speedX = Math.cos(toPlayer) * speed; p.speedY = Math.sin(toPlayer) * speed; projectiles.push(p); game.addChild(p); }, 900); })(i); } return; } } // Blizzard: Frost Nova - signature attack: 3 expanding icy rings, then a barrage of fast icicles from above if (pattern === "blizzard_maelstrom") { // 1. Spawn 3 expanding icy rings from the boss var ringCount = 3; for (var r = 0; r < ringCount; r++) { (function (ringIdx) { LK.setTimeout(function () { var n = 8 + Math.floor(phase * 0.7) + ringIdx * 2; var ringSpeed = 2.2 + phase * 0.13 + ringIdx * 0.7; for (var i = 0; i < n; i++) { var angle = 2 * Math.PI / n * i; var p = new Projectile(); p.init("frost_nova_ring", projAsset); p.x = cx; p.y = cy; p.speedX = Math.cos(angle) * ringSpeed; p.speedY = Math.sin(angle) * ringSpeed; if (p.asset) { p.asset.alpha = 0.85; p.asset.scale.x = p.asset.scale.y = 1.1 + 0.08 * ringIdx; } projectiles.push(p); game.addChild(p); } }, ringIdx * 350); })(r); } // 2. After a short delay, spawn a barrage of fast icicles from above LK.setTimeout(function () { var icicleN = 14 + Math.floor(phase * 1.1); for (var i = 0; i < icicleN; i++) { (function (idx) { LK.setTimeout(function () { var p = new Projectile(); p.init("frost_nova_icicle", projAsset); p.x = ARENA_X + Math.random() * ARENA_W; p.y = ARENA_Y - 60 - Math.random() * 120; p.speedX = (Math.random() - 0.5) * 1.2; p.speedY = 8.5 + phase * 0.22; if (p.asset) { p.asset.rotation = Math.PI / 2 + (Math.random() - 0.5) * 0.2; p.asset.alpha = 0.92; } projectiles.push(p); game.addChild(p); }, Math.floor(idx * 600 / icicleN)); })(i); } }, 900); // 3. After another short delay, spawn a final small ring for surprise LK.setTimeout(function () { var n = 6 + Math.floor(phase * 0.5); for (var i = 0; i < n; i++) { var angle = 2 * Math.PI / n * i; var p = new Projectile(); p.init("frost_nova_final", projAsset); p.x = cx; p.y = cy; var speed = 4.2 + phase * 0.13; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; if (p.asset) { p.asset.alpha = 0.8; p.asset.scale.x = p.asset.scale.y = 1.05; } projectiles.push(p); game.addChild(p); } }, 1700); return; } // Ember: Supernova - store up 10 embers, then fire all at player with longer indicators and slower speed if (pattern === "supernova") { if (!boss._supernovaStored) boss._supernovaStored = []; var nSuper = 10; // was 16 // Store embers in a big circle for (i = 0; i < nSuper; i++) { angle = 2 * Math.PI / nSuper * i; p = new Projectile(); p.init("supernova", projAsset); var orbitRadius = 260 + phase * 12; var orbitSpeed = 0.028 + phase * 0.0015; // slower orbit p._emberOrbitAngle = angle; p._emberOrbitRadius = orbitRadius; p._emberOrbitSpeed = orbitSpeed; p._emberOrbitCenter = { x: cx, y: cy }; p.x = cx + Math.cos(angle) * orbitRadius; p.y = cy + Math.sin(angle) * orbitRadius; p.speedX = 0; p.speedY = 0; p._emberSpin = true; boss._supernovaStored.push(p); game.addChild(p); } // Show huge sniper indicators for (var si = 0; si < boss._supernovaStored.length; si++) { var sp = boss._supernovaStored[si]; var sniperAngle = Math.atan2(player.y - sp.y, player.x - sp.x); var indicator = LK.getAsset('proj_orange', { anchorX: 0.5, anchorY: 0.5, x: sp.x, y: sp.y, scaleX: 0.7, scaleY: 0.18, rotation: sniperAngle }); indicator.width = 1200; indicator.alpha = 0.45; game.addChild(indicator); tween(indicator, { alpha: 0 }, { duration: 1000, //{cZ} // was 700, now 1000ms onFinish: function onFinish() { if (indicator.parent) indicator.parent.removeChild(indicator); } }); } // After indicator, fire all at player at lower speed LK.setTimeout(function () { for (var si = 0; si < boss._supernovaStored.length; si++) { var sp = boss._supernovaStored[si]; var sniperAngle = Math.atan2(player.y - sp.y, player.x - sp.x); var sniperSpeed = 10 + phase * 0.5; // was 15 + phase*0.9 sp.speedX = Math.cos(sniperAngle) * sniperSpeed; sp.speedY = Math.sin(sniperAngle) * sniperSpeed; sp._emberSpin = false; projectiles.push(sp); } boss._supernovaStored = []; }, 1000); //{de} // was 700, now 1000ms return; } // --- Abyss Attacks --- if (pattern === "vortex") { // Swirling projectiles inwards toward player n = 7 + Math.floor(phase * 0.7); var swirlBase = boss.timeAlive * 0.04 % (2 * Math.PI); for (i = 0; i < n; i++) { angle = swirlBase + 2 * Math.PI / n * i; p = new Projectile(); p.init("vortex", projAsset); p.x = cx + Math.cos(angle) * 400; p.y = cy + Math.sin(angle) * 400; var toPlayer = Math.atan2(py - p.y, px - p.x); speed = 2.8 + phase * 0.18; p.speedX = Math.cos(toPlayer) * speed; p.speedY = Math.sin(toPlayer) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "blackhole") { // Projectiles curve toward player (simulate gravity) n = 5 + Math.floor(phase / 2); for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("blackhole", projAsset); p.x = cx; p.y = cy; var baseSpeed = 3.2 + phase * 0.13; p.speedX = Math.cos(angle) * baseSpeed; p.speedY = Math.sin(angle) * baseSpeed; // Add gravity effect in update p._gravityToPlayer = true; projectiles.push(p); game.addChild(p); } } else if (pattern === "void_ring") { // Obsidian: Make void_ring attack harder: more projectiles, faster speed if (bossType.name === "Obsidian") { n = 18 + Math.floor(phase * 1.2); // was 10 + ... for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("void_ring", projAsset); p.x = cx; p.y = cy; speed = 2.8 + phase * 0.18; // much faster p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; // Obsidian projectiles do NOT bounce p.bounce = false; projectiles.push(p); game.addChild(p); } } else { // Slow expanding ring n = 10 + Math.floor(phase * 0.8); for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("void_ring", projAsset); p.x = cx; p.y = cy; speed = 1.3 + phase * 0.07; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } } else if (pattern === "chaos") { // Obsidian: Make chaos attack harder: more projectiles, higher speed, more randomness if (bossType.name === "Obsidian") { n = 12 + Math.floor(phase * 1.2); // was 6 + ... for (i = 0; i < n; i++) { angle = Math.random() * 2 * Math.PI; p = new Projectile(); p.init("chaos", projAsset); p.x = cx; p.y = cy; speed = 3.5 + Math.random() * (7.5 + phase * 0.4); // much faster p.speedX = Math.cos(angle) * speed * (0.8 + Math.random() * 0.4); // add more randomness p.speedY = Math.sin(angle) * speed * (0.8 + Math.random() * 0.4); // Obsidian projectiles do NOT bounce p.bounce = false; projectiles.push(p); game.addChild(p); } } else { // Random directions, random speeds n = 6 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { angle = Math.random() * 2 * Math.PI; p = new Projectile(); p.init("chaos", projAsset); p.x = cx; p.y = cy; speed = 1.5 + Math.random() * (4.5 + phase * 0.2); p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } } else if (pattern === "gravity") { // Projectiles accelerate toward player after a delay n = 4 + Math.floor(phase / 2); for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("gravity", projAsset); p.x = cx; p.y = cy; speed = 2.5 + phase * 0.13; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; // After 0.7s, home in on player (function (proj) { LK.setTimeout(function () { var dx = player.x - proj.x; var dy = player.y - proj.y; var mag = Math.sqrt(dx * dx + dy * dy) || 1; var newSpeed = 5.5 + phase * 0.2; proj.speedX = dx / mag * newSpeed; proj.speedY = dy / mag * newSpeed; }, 700); })(p); projectiles.push(p); game.addChild(p); } // --- Tempest Attacks --- } else if (pattern === "wind") { // Fast horizontal projectiles from left or right var fromLeft = Math.random() < 0.5; n = 5 + Math.floor(phase / 2); for (i = 0; i < n; i++) { p = new Projectile(); p.init("wind", projAsset); p.x = fromLeft ? ARENA_X + 40 : ARENA_X + ARENA_W - 40; p.y = ARENA_Y + 200 + (ARENA_H - 400) * (i / (n - 1)); p.speedX = (fromLeft ? 1 : -1) * (8.5 + phase * 0.4); p.speedY = 0; projectiles.push(p); game.addChild(p); } } else if (pattern === "gust") { // Diagonal waves n = 4 + Math.floor(phase / 2); for (i = 0; i < n; i++) { p = new Projectile(); p.init("gust", projAsset); p.x = ARENA_X + 100 + (ARENA_W - 200) * (i / (n - 1)); p.y = ARENA_Y + 40; var angle = Math.PI / 4 + (Math.random() - 0.5) * 0.3; p.speedX = Math.cos(angle) * (4.5 + phase * 0.2); p.speedY = Math.sin(angle) * (4.5 + phase * 0.2); projectiles.push(p); game.addChild(p); } } else if (pattern === "cyclone") { // Spiral from arena edge toward center n = 7 + Math.floor(phase * 0.7); var spiralBase = boss.timeAlive * 0.05 % (2 * Math.PI); for (i = 0; i < n; i++) { angle = spiralBase + 2 * Math.PI / n * i; p = new Projectile(); p.init("cyclone", projAsset); p.x = cx + Math.cos(angle) * (ARENA_W / 2 - 100); p.y = cy + Math.sin(angle) * (ARENA_H / 2 - 100); var toCenter = Math.atan2(cy - p.y, cx - p.x); speed = 3.8 + phase * 0.18; p.speedX = Math.cos(toCenter) * speed; p.speedY = Math.sin(toCenter) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "twister") { // Projectiles curve in S-shape (simulate by changing speedY over time) n = 5 + Math.floor(phase / 2); for (i = 0; i < n; i++) { p = new Projectile(); p.init("twister", projAsset); p.x = ARENA_X + 100 + (ARENA_W - 200) * (i / (n - 1)); p.y = ARENA_Y + 40; p.speedX = 0; p.speedY = 5.5 + phase * 0.2; // S-curve: alternate left/right p._twistDir = i % 2 === 0 ? 1 : -1; p._twistTick = 0; projectiles.push(p); game.addChild(p); } } else if (pattern === "scatter") { // Random scatter from boss n = 7 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { angle = Math.random() * 2 * Math.PI; p = new Projectile(); p.init("scatter", projAsset); p.x = cx; p.y = cy; speed = 3.5 + Math.random() * 2.5 + phase * 0.13; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } // --- Prism Attacks --- } else if (pattern === "prism") { // Rainbow spread (simulate with color asset, but all yellow for now) n = 6 + Math.floor(phase * 0.7); var spread = Math.PI * 2 / 3; var baseAngle = -Math.PI / 3; for (i = 0; i < n; i++) { angle = baseAngle + spread * (i / (n - 1)); p = new Projectile(); p.init("prism", projAsset); p.x = cx; p.y = cy; speed = 5.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "reflect") { // Bouncing projectiles n = 4 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { angle = Math.PI / 6 + Math.PI * 2 / n * i; p = new Projectile(); p.init("reflect", projAsset); p.x = cx; p.y = cy; speed = 4.5 + phase * 0.18; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; p.bounce = true; projectiles.push(p); game.addChild(p); } } else if (pattern === "laser") { // Straight fast lines n = 2 + Math.floor(phase / 2); for (i = 0; i < n; i++) { angle = Math.PI / 2 + (i - (n - 1) / 2) * 0.12; p = new Projectile(); p.init("laser", projAsset); p.x = cx; p.y = cy; speed = 11 + phase * 0.5; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "split") { // Projectiles split mid-air n = 3 + Math.floor(phase / 2); for (i = 0; i < n; i++) { angle = Math.PI / 2 + (i - (n - 1) / 2) * 0.18; p = new Projectile(); p.init("split", projAsset); p.x = cx; p.y = cy; speed = 5.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; // After 0.6s, split into two (function (proj, ang) { LK.setTimeout(function () { for (var s = -1; s <= 1; s += 2) { var child = new Projectile(); child.init("split_child", projAsset); child.x = proj.x; child.y = proj.y; var splitAngle = ang + s * Math.PI / 8; var splitSpeed = 4.5 + phase * 0.18; child.speedX = Math.cos(splitAngle) * splitSpeed; child.speedY = Math.sin(splitAngle) * splitSpeed; projectiles.push(child); game.addChild(child); } }, 600); })(p, angle); projectiles.push(p); game.addChild(p); } } else if (pattern === "mirror") { // Projectiles reflect off walls (simulate with bounce) n = 4 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { angle = Math.PI / 2 + (i - (n - 1) / 2) * 0.22; p = new Projectile(); p.init("mirror", projAsset); p.x = cx; p.y = cy; speed = 5.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; // Obsidian projectiles do NOT bounce if (bossType.name === "Obsidian") { p.bounce = false; } else { p.bounce = true; } projectiles.push(p); game.addChild(p); } // --- Inferno Attacks --- } else if (pattern === "spiral") { // Rotating spiral n = 5 + Math.floor(phase * 0.7); baseAngle = boss.timeAlive * 0.07 % (2 * Math.PI); for (i = 0; i < n; i++) { angle = baseAngle + 2 * Math.PI / n * i; p = new Projectile(); p.init("spiral", projAsset); p.x = cx; p.y = cy; speed = 4.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "double_spiral") { // Two spirals, offset n = 6 + Math.floor(phase * 0.7); baseAngle = boss.timeAlive * 0.09 % (2 * Math.PI); for (j = 0; j < 2; j++) { offset = j * Math.PI / n; for (i = 0; i < n; i++) { angle = baseAngle + 2 * Math.PI / n * i + offset; p = new Projectile(); p.init("double_spiral", projAsset); p.x = cx; p.y = cy; speed = 3.8 + phase * 0.18; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } } else if (pattern === "wall") { // Shoots a wall of projectiles horizontally or vertically var vertical = Math.random() < 0.5; if (bossType.name === "Blizzard") { // Blizzard: easier - fewer projectiles, slower if (vertical) { n = 3 + Math.floor(phase / 3); // much fewer projectiles } else { n = 7 + Math.floor(phase / 2.5); // fewer projectiles } for (i = 0; i < n; i++) { p = new Projectile(); p.init("wall", projAsset); if (vertical) { p.x = ARENA_X + ARENA_W / (n + 1) * (i + 1); p.y = cy; p.speedX = 0; p.speedY = 3.5 + phase * 0.13; // slower } else { p.x = cx; p.y = ARENA_Y + ARENA_H / (n + 1) * (i + 1); p.speedX = 3.5 + phase * 0.13; // slower p.speedY = 0; } projectiles.push(p); game.addChild(p); } } else { // Reduce number of projectiles for vertical (downward) wall for more spacing if (vertical) { n = 4 + Math.floor(phase / 3); // was 10 + Math.floor(phase / 2) } else { n = 6 + Math.floor(phase / 2); } for (i = 0; i < n; i++) { p = new Projectile(); p.init("wall", projAsset); if (vertical) { p.x = ARENA_X + ARENA_W / (n + 1) * (i + 1); p.y = cy; p.speedX = 0; p.speedY = 4.5 + phase * 0.22; } else { p.x = cx; p.y = ARENA_Y + ARENA_H / (n + 1) * (i + 1); p.speedX = 4.5 + phase * 0.22; p.speedY = 0; } projectiles.push(p); game.addChild(p); } } } else if (pattern === "cross") { // Shoots in a cross pattern (up, down, left, right) if (bossType.name === "Blizzard") { // Blizzard: 8 directions, easier - slower speed var dirs = [[0, 1], [0, -1], [1, 0], [-1, 0], [1, 1], [-1, 1], [1, -1], [-1, -1]]; for (i = 0; i < dirs.length; i++) { p = new Projectile(); p.init("cross", projAsset); p.x = cx; p.y = cy; speed = 4.5 + phase * 0.13; // slower var norm = Math.sqrt(dirs[i][0] * dirs[i][0] + dirs[i][1] * dirs[i][1]) || 1; p.speedX = dirs[i][0] / norm * speed; p.speedY = dirs[i][1] / norm * speed; projectiles.push(p); game.addChild(p); } } else { var dirs = [[0, 1], [0, -1], [1, 0], [-1, 0]]; for (i = 0; i < dirs.length; i++) { p = new Projectile(); p.init("cross", projAsset); p.x = cx; p.y = cy; speed = 6.5 + phase * 0.22; p.speedX = dirs[i][0] * speed; p.speedY = dirs[i][1] * speed; projectiles.push(p); game.addChild(p); } } } else if (pattern === "aimed_burst") { // Burst of aimed shots with spread n = 3 + Math.floor(phase / 2); var toPlayer = Math.atan2(py - cy, px - cx); spread = Math.PI / 6 + Math.PI / 32 * phase; for (i = 0; i < n; i++) { angle = toPlayer + spread * (i - (n - 1) / 2) / n; p = new Projectile(); p.init("aimed_burst", projAsset); p.x = cx; p.y = cy; speed = 5.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } // --- Verdant Attacks --- } else if (pattern === "burst") { // All directions, but for Ember, embers spin around the boss n = 5 + Math.floor(phase * 0.7); if (bossType.name === "Ember") { // --- Ember (orange boss) stores up projectiles for a big burst --- if (!boss._emberStored) boss._emberStored = []; // Store up to 12 projectiles before firing if (!boss._emberStoreTick) boss._emberStoreTick = 0; boss._emberStoreTick++; // Only store if not already at max if (boss._emberStored.length < 12) { for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("burst", projAsset); // Place at a radius from boss, and set up orbit parameters var orbitRadius = 180 + phase * 10; var orbitSpeed = 0.045 + phase * 0.003; // radians per frame p._emberOrbitAngle = angle; p._emberOrbitRadius = orbitRadius; p._emberOrbitSpeed = orbitSpeed; p._emberOrbitCenter = { x: cx, y: cy }; // Initial position p.x = cx + Math.cos(angle) * orbitRadius; p.y = cy + Math.sin(angle) * orbitRadius; // No initial speed, will be set in update p.speedX = 0; p.speedY = 0; // Mark as ember spin p._emberSpin = true; // Store for later burst boss._emberStored.push(p); game.addChild(p); } } else { // --- Fire all stored projectiles at player at high speed --- // Show sniper indicator for each projectile for (var si = 0; si < boss._emberStored.length; si++) { var sp = boss._emberStored[si]; // Calculate angle to player at this moment var sniperAngle = Math.atan2(player.y - sp.y, player.x - sp.x); // Draw indicator var indicator = LK.getAsset('proj_orange', { anchorX: 0.5, anchorY: 0.5, x: sp.x, y: sp.y, scaleX: 0.5, scaleY: 0.15, rotation: sniperAngle }); indicator.width = 900; indicator.alpha = 0.38; game.addChild(indicator); (function (ind) { tween(ind, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { if (ind.parent) ind.parent.removeChild(ind); } }); })(indicator); } // After indicator, fire all at high speed LK.setTimeout(function () { for (var si = 0; si < boss._emberStored.length; si++) { var sp = boss._emberStored[si]; // Calculate angle to player at this moment var sniperAngle = Math.atan2(player.y - sp.y, player.x - sp.x); var sniperSpeed = 11 + phase * 0.7; sp.speedX = Math.cos(sniperAngle) * sniperSpeed; sp.speedY = Math.sin(sniperAngle) * sniperSpeed; // Remove ember spin so they fly straight sp._emberSpin = false; // Add to projectiles array for collision/removal projectiles.push(sp); } boss._emberStored = []; }, 500); } } else { for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("burst", projAsset); p.x = cx; p.y = cy; speed = 2.2 + phase * 0.13; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } } else if (pattern === "flower") { // Petal/flower pattern (alternating speeds) // Make flower easier: fewer petals, slower speed n = 4 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("flower", projAsset); p.x = cx; p.y = cy; speed = (i % 2 === 0 ? 2.5 : 1.2) + phase * 0.09; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "arc") { // Arc sweep (quarter circle) // Make arc easier: fewer projectiles, slower speed n = 3 + Math.floor(phase * 0.7); var arcStart = Math.PI / 4; var arcEnd = Math.PI * 3 / 4; for (i = 0; i < n; i++) { angle = arcStart + (arcEnd - arcStart) * (i / (n - 1)); p = new Projectile(); p.init("arc", projAsset); p.x = cx; p.y = cy; speed = 2.5 + phase * 0.13; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "ring") { // Expanding ring (Blizzard: more projectiles, faster speed) if (bossType.name === "Blizzard") { n = 8 + Math.floor(phase * 0.7); // Reduced number of projectiles for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("ring", projAsset); p.x = cx; p.y = cy; speed = 1.2 + phase * 0.09; // Reduced speed p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else { // Make ring easier: fewer projectiles, slower speed n = 6 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i; p = new Projectile(); p.init("ring", projAsset); p.x = cx; p.y = cy; speed = 1.2 + phase * 0.07; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } } else if (pattern === "random_bounce") { // Deterministic directions, evenly spaced, for predictability // Make random_bounce easier: fewer projectiles, slower speed n = 2 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { angle = 2 * Math.PI / n * i + boss.timeAlive % 60 * 0.05; // slight rotation over time p = new Projectile(); p.init("random_bounce", projAsset); p.x = cx; p.y = cy; speed = 1.5 + i % 2 + phase * 0.07; // deterministic speed, slower p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; // Mark as bouncing (handled in update) p.bounce = true; projectiles.push(p); game.addChild(p); } // --- Solaris Attacks --- } else if (pattern === "aimed") { // Aimed shots at player, with some spread n = 1 + Math.floor(phase / 2); var toPlayer = Math.atan2(py - cy, px - cx); spread = Math.PI / 8 + Math.PI / 32 * phase; // Show indicator lines for each aimed shot for (i = 0; i < n; i++) { angle = toPlayer + spread * (i - (n - 1) / 2); // Draw indicator var indicator = LK.getAsset('proj_' + bossType.asset.split('_')[1], { anchorX: 0.5, anchorY: 0.5, x: cx, y: cy, scaleX: 0.5, scaleY: 0.15, rotation: angle }); // Stretch indicator to show path indicator.width = 900; indicator.alpha = 0.35; game.addChild(indicator); // Fade out and remove indicator after 0.7s (function (ind, angle) { tween(ind, { alpha: 0 }, { duration: 700, onFinish: function onFinish() { if (ind.parent) ind.parent.removeChild(ind); } }); // Fire projectile after indicator delay (0.7s) LK.setTimeout(function () { var p = new Projectile(); p.init("aimed", projAsset); p.x = cx; p.y = cy; var speed = 6.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); }, 700); })(indicator, angle); } } else if (pattern === "triple_aimed") { // Three aimed shots, tight spread n = 2; var toPlayer = Math.atan2(py - cy, px - cx); spread = Math.PI / 16; for (i = 0; i < n; i++) { angle = toPlayer + spread * (i - 1); p = new Projectile(); p.init("triple_aimed", projAsset); p.x = cx; p.y = cy; speed = 7.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else if (pattern === "sniper") { // Obsidian: Make sniper attack harder: faster projectile, shorter warning if (bossType.name === "Obsidian") { var toPlayer = Math.atan2(py - cy, px - cx); // Draw indicator var indicator = LK.getAsset('proj_' + bossType.asset.split('_')[1], { anchorX: 0.5, anchorY: 0.5, x: cx, y: cy, scaleX: 0.5, scaleY: 0.15, rotation: toPlayer }); indicator.width = 1200; indicator.alpha = 0.45; game.addChild(indicator); (function (ind, angle) { tween(ind, { alpha: 0 }, { duration: 600, // shorter warning onFinish: function onFinish() { if (ind.parent) ind.parent.removeChild(ind); } }); // Fire projectile after indicator delay (0.6s) LK.setTimeout(function () { var p = new Projectile(); p.init("sniper", projAsset); p.x = cx; p.y = cy; var speed = 15 + phase * 0.5; // much faster p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); }, 600); })(indicator, toPlayer); } else { // Single fast shot at player var toPlayer = Math.atan2(py - cy, px - cx); // Draw indicator var indicator = LK.getAsset('proj_' + bossType.asset.split('_')[1], { anchorX: 0.5, anchorY: 0.5, x: cx, y: cy, scaleX: 0.5, scaleY: 0.15, rotation: toPlayer }); indicator.width = 1200; indicator.alpha = 0.45; game.addChild(indicator); (function (ind, angle) { tween(ind, { alpha: 0 }, { duration: 900, onFinish: function onFinish() { if (ind.parent) ind.parent.removeChild(ind); } }); // Fire projectile after indicator delay (0.9s) LK.setTimeout(function () { var p = new Projectile(); p.init("sniper", projAsset); p.x = cx; p.y = cy; var speed = 10 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); }, 900); })(indicator, toPlayer); } } else if (pattern === "spread") { // Wide spread if (bossType.name === "Blizzard") { n = 5 + Math.floor(phase * 0.7); // fewer projectiles var toPlayer = Math.atan2(py - cy, px - cx); spread = Math.PI * 1.2; for (i = 0; i < n; i++) { angle = toPlayer - spread / 2 + spread * (i / (n - 1)); p = new Projectile(); p.init("spread", projAsset); p.x = cx; p.y = cy; speed = 3.5 + phase * 0.13; // slower p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } else { n = 4 + Math.floor(phase / 2); var toPlayer = Math.atan2(py - cy, px - cx); spread = Math.PI / 3; for (i = 0; i < n; i++) { angle = toPlayer - spread / 2 + spread * (i / (n - 1)); p = new Projectile(); p.init("spread", projAsset); p.x = cx; p.y = cy; speed = 5.5 + phase * 0.22; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } } else if (pattern === "rain") { // Rain from above (projectiles fall from random x at top) if (bossType.name === "Blizzard") { n = 7 + Math.floor(phase * 0.7); // fewer projectiles for (i = 0; i < n; i++) { p = new Projectile(); p.init("rain", projAsset); p.x = ARENA_X + Math.random() * ARENA_W; p.y = ARENA_Y + 40; p.speedX = (Math.random() - 0.5) * 1.0; // less drift p.speedY = 4.5 + phase * 0.13; // slower projectiles.push(p); game.addChild(p); } } else { n = 4 + Math.floor(phase * 0.7); for (i = 0; i < n; i++) { p = new Projectile(); p.init("rain", projAsset); p.x = ARENA_X + Math.random() * ARENA_W; p.y = ARENA_Y + 40; p.speedX = 0; p.speedY = 6.5 + phase * 0.22; projectiles.push(p); game.addChild(p); } } } else if (pattern === "snowstorm") { // Snowstorm: many slow projectiles fall from random X at top, with random drift if (bossType.name === "Blizzard") { n = 16 + Math.floor(phase * 1.2); // much fewer projectiles var stormDuration = 180 + Math.floor(phase * 18); // longer, less intense var stormW = ARENA_W + 300; // less wide var stormH = 300 + phase * 20; // less tall for (i = 0; i < n; i++) { (function (delayIdx) { LK.setTimeout(function () { var p = new Projectile(); p.init("snowstorm", projAsset); // Random X across a less wide arena, randomize a bit outside for edge effect p.x = ARENA_X - 100 + Math.random() * stormW; p.y = ARENA_Y - 40 - Math.random() * stormH; // Slower downward, less horizontal drift p.speedX = (Math.random() - 0.5) * (1.2 + 0.08 * phase); p.speedY = 1.2 + Math.random() * 0.8 + 0.08 * phase; // Give a little random rotation for visual effect if (p.asset) p.asset.rotation = Math.random() * Math.PI * 2; projectiles.push(p); game.addChild(p); }, Math.floor(delayIdx * stormDuration / n)); })(i); } } else { // Make it longer, bigger, and with more projectiles n = 28 + Math.floor(phase * 2.5); // More projectiles var stormDuration = 120 + Math.floor(phase * 18); // Lasts longer (was 1 attack, now spread over time) var stormW = ARENA_W + 400; // Wider spread var stormH = 400 + phase * 30; // Taller spawn area for (i = 0; i < n; i++) { (function (delayIdx) { LK.setTimeout(function () { var p = new Projectile(); p.init("snowstorm", projAsset); // Random X across a wider arena, randomize a bit outside for edge effect p.x = ARENA_X - 200 + Math.random() * stormW; p.y = ARENA_Y - 80 - Math.random() * stormH; // Slow downward, random horizontal drift (wider range) p.speedX = (Math.random() - 0.5) * (2.2 + 0.25 * phase); p.speedY = 2.2 + Math.random() * 1.8 + 0.18 * phase; // Give a little random rotation for visual effect if (p.asset) p.asset.rotation = Math.random() * Math.PI * 2; projectiles.push(p); game.addChild(p); }, Math.floor(delayIdx * stormDuration / n)); })(i); } } } else { // Fallback: default to spiral n = 8 + phase; baseAngle = boss.timeAlive * 0.07 % (2 * Math.PI); for (i = 0; i < n; i++) { angle = baseAngle + 2 * Math.PI / n * i; p = new Projectile(); p.init("spiral", projAsset); p.x = cx; p.y = cy; speed = 7 + phase * 0.7; p.speedX = Math.cos(angle) * speed; p.speedY = Math.sin(angle) * speed; projectiles.push(p); game.addChild(p); } } } // Main game update game.update = function () { if (!gameStarted) { // Animate menu background in if (menuBg.alpha < 0.18) menuBg.alpha += 0.02; if (bossSelectSubTxt.alpha < 1) bossSelectSubTxt.alpha += 0.05; return; } else { // Animate menu background out if (menuBg.alpha > 0) menuBg.alpha -= 0.04; if (bossSelectSubTxt.alpha > 0) bossSelectSubTxt.alpha -= 0.08; } // Animate boss-specific background if visible if (bossBgContainer && bossBgContainer.visible) { bossBgContainer._animTick = (bossBgContainer._animTick || 0) + 1; var t = bossBgContainer._animTick; var animType = bossBgContainer._animType; // Animate the first child (main background) if (bossBgContainer.children.length > 0) { var bg = bossBgContainer.children[0]; if (animType === "pulse") { // Inferno: pulsing scale bg.scale.x = bg.scale.y = 7.5 + Math.sin(t / 30) * 0.25; bg.alpha = 0.10 + 0.03 * Math.abs(Math.sin(t / 30)); } else if (animType === "wave") { // Verdant: slow scale wave bg.scale.x = 7.5 + Math.sin(t / 60) * 0.18; bg.scale.y = 7.5 + Math.cos(t / 60) * 0.18; bg.alpha = 0.10 + 0.02 * Math.abs(Math.cos(t / 60)); } else if (animType === "shine") { // Solaris: slow rotation bg.rotation = t / 180 % (2 * Math.PI); bg.alpha = 0.10 + 0.03 * Math.abs(Math.sin(t / 50)); } else if (animType === "vortex") { // Abyss: slow swirl and fade bg.rotation = t / 120 % (2 * Math.PI); bg.alpha = 0.10 + 0.04 * Math.abs(Math.sin(t / 80)); } else if (animType === "wind") { // Tempest: horizontal wave bg.x = 2048 / 2 + Math.sin(t / 40) * 60; bg.alpha = 0.10 + 0.03 * Math.abs(Math.sin(t / 40)); } else if (animType === "rainbow") { // Prism: rotate all children for (var c = 0; c < bossBgContainer.children.length; c++) { var child = bossBgContainer.children[c]; if (c === 0) { child.rotation = t / 180 % (2 * Math.PI); child.alpha = 0.10 + 0.02 * Math.abs(Math.sin(t / 60)); } else { // Rainbow ellipses rotate around center var angle = Math.PI * 2 * (c - 1) / 6 + t / 120; child.x = 2048 / 2 + Math.cos(angle) * 500; child.y = 2732 / 2 + Math.sin(angle) * 500; child.alpha = 0.09 + 0.01 * Math.abs(Math.sin(t / 40 + c)); } } } else { // Default: no animation bg.scale.x = bg.scale.y = 7.5; bg.alpha = 0.10; } } } // Update survival time survivalTime += 1 / 60; timeTxt.setText(survivalTime.toFixed(1) + "s"); // Update boss if (boss) { // Stop boss attacks if bossLives is 0 or less (boss defeated) if (bossLives <= 0) { // Do not update attack timer or attack boss.update(); } else { boss.update(); boss.attackTimer++; if (boss.attackTimer >= boss.attackInterval) { boss.attackTimer = 0; bossAttack(); } } // Update boss healthbar if (bossHealthBar && bossMaxHealth > 0) { var healthFrac = Math.max(0, Math.min(1, bossHealth / bossMaxHealth)); bossHealthBar.scale.x = 0.22 * healthFrac; // Color shifts from red (full) to yellow (mid) to gray (low) if (healthFrac > 0.5) { bossHealthBar.tint = 0xFF4444; } else if (healthFrac > 0.2) { bossHealthBar.tint = 0xFFDD44; } else { bossHealthBar.tint = 0xAAAAAA; } bossHealthBar.visible = true; bossHealthBarBg.visible = true; bossHealthBarFrame.visible = true; } } // Animate player and boss glows if present if (typeof playerGlow !== "undefined" && playerGlow && typeof playerGlow.update === "function") { playerGlow.update(); } if (typeof bossGlow !== "undefined" && bossGlow && typeof bossGlow.update === "function") { bossGlow.update(); } // Animate style select button feedback if style page is open if (stylePageBg && stylePageBg.visible) { for (var i = 0; i < styleSelectButtons.length; i++) { if (typeof styleSelectButtons[i].update === "function") styleSelectButtons[i].update(); if (styleSelectButtons[i]._border) styleSelectButtons[i]._border.visible = true; if (styleSelectButtons[i]._shadow) styleSelectButtons[i]._shadow.visible = true; } } // Update projectiles for (var i = projectiles.length - 1; i >= 0; i--) { var p = projectiles[i]; p.update(); // Remove if out of arena if (p.x < ARENA_X - 100 || p.x > ARENA_X + ARENA_W + 100 || p.y < ARENA_Y - 100 || p.y > ARENA_Y + ARENA_H + 100) { p.destroy(); projectiles.splice(i, 1); continue; } // Collision with player if (player._invuln && player._invuln > 0) { player._invuln--; continue; } var dx = p.x - player.x; var dy = p.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.radius + p.radius - 10) { // Player hit LK.effects.flashScreen(0xff0000, 800); playerLives--; livesTxt.setText('Lives: ' + playerLives); if (playerLives <= 0) { LK.showGameOver(); return; } else { // Remove all projectiles and respawn player at start position for (var j = 0; j < projectiles.length; j++) { projectiles[j].destroy(); } projectiles = []; player.x = 2048 / 2; player.y = 2732 / 2 + 600; clampPlayerStart(); // Brief invulnerability (skip collision for 120 frames = 2 seconds) player._invuln = 120; break; } } } }; // Prevent player from being placed in top left 100x100 function clampPlayerStart() { var minX = ARENA_X + player.radius; var minY = ARENA_Y + player.radius; if (player.x < minX) player.x = minX; if (player.y < minY) player.y = minY; } // On game reset, restore boss select UI LK.on('reset', function () { // Stop boss music on reset LK.stopMusic(); // Play menu music LK.playMusic('music_menu', { loop: true }); // Restore all boss select UI elements bossSelectTxt.visible = true; bossSelectSubTxt.visible = true; menuBg.visible = true; for (var i = 0; i < bossSelectButtons.length; i++) { bossSelectButtons[i].visible = true; bossSelectButtons[i]._label.visible = true; if (bossSelectButtons[i]._diffDots) { for (var d = 0; d < bossSelectButtons[i]._diffDots.length; d++) { bossSelectButtons[i]._diffDots[d].visible = true; } } } // Restore info/settings button and hide info page menuInfoBtn.visible = true; menuInfoBtnLabel.visible = true; menuStyleBtn.visible = true; menuStyleBtnLabel.visible = true; showInfoPage(false); infoPageOpen = false; showStylePage(false); stylePageOpen = false; arenaBorder.visible = false; timeTxt.visible = false; bossNameTxt.visible = false; if (bossBgContainer) bossBgContainer.visible = false; // Hide boss healthbar bossHealthBar.visible = false; bossHealthBarBg.visible = false; bossHealthBarFrame.visible = false; gameStarted = false; bossChoice = null; playerLives = 5; livesTxt.setText('Lives: ' + playerLives); // Reset boss lives bossLives = 4; bossMaxLives = 4; bossLivesTxt.setText('Lives: ' + bossLives + ' / ' + bossMaxLives); bossLivesTxt.visible = false; // Remove player, boss, projectiles if (player) { player.destroy(); player = null; } if (typeof playerGlow !== "undefined" && playerGlow && playerGlow.parent) { playerGlow.parent.removeChild(playerGlow); playerGlow = null; } if (typeof playerShadow !== "undefined" && playerShadow && playerShadow.parent) { playerShadow.parent.removeChild(playerShadow); playerShadow = null; } if (boss) { boss.destroy(); boss = null; } if (typeof bossGlow !== "undefined" && bossGlow && bossGlow.parent) { bossGlow.parent.removeChild(bossGlow); bossGlow = null; } if (typeof bossShadow !== "undefined" && bossShadow && bossShadow.parent) { bossShadow.parent.removeChild(bossShadow); bossShadow = null; } for (var i = 0; i < projectiles.length; i++) { projectiles[i].destroy(); } projectiles = []; // Clear boss damage timer if set if (typeof bossDamageTimer !== "undefined") { LK.clearInterval(bossDamageTimer); bossDamageTimer = undefined; } });
===================================================================
--- original.js
+++ change.js
@@ -395,25 +395,25 @@
/****
* Game Code
****/
-// If you decrement bossLives elsewhere in the code (e.g. on projectile collision or other damage), call bossHitEffect() at that point to trigger the visual effect.
-// Unique sprites for 4th row bosses
-// Arena border
-// Projectiles
-// Bosses
-// Player: Circle, blue
-// Bosses: Each boss will have a unique color and shape for now.
-// Arena settings
-// Abyss
-// Tempest
+// Signature ability sounds for each boss
+// Add missing boss music assets
+// Unique boss music (IDs are placeholders, replace with actual IDs as needed)
// Prism
-// Abyss
// Tempest
+// Abyss
// Prism
-// Unique boss music (IDs are placeholders, replace with actual IDs as needed)
-// Add missing boss music assets
-// Signature ability sounds for each boss
+// Tempest
+// Abyss
+// Arena settings
+// Bosses: Each boss will have a unique color and shape for now.
+// Player: Circle, blue
+// Bosses
+// Projectiles
+// Arena border
+// Unique sprites for 4th row bosses
+// If you decrement bossLives elsewhere in the code (e.g. on projectile collision or other damage), call bossHitEffect() at that point to trigger the visual effect.
var ARENA_W = 1800;
var ARENA_H = 2400;
var ARENA_X = (2048 - ARENA_W) / 2;
var ARENA_Y = (2732 - ARENA_H) / 2;
@@ -2139,11 +2139,11 @@
}
voidProj._voidMawActive = true;
projectiles.push(voidProj);
game.addChild(voidProj);
- // 2. Pull player toward center for 1.2s (simulate gravity)
- var pullDuration = 1200;
- var pullStrength = 0.22 + 0.04 * phase;
+ // 2. Pull player toward center for 1.5s (simulate gravity, easier)
+ var pullDuration = 1500; // was 1200
+ var pullStrength = 0.13 + 0.02 * phase; // was 0.22 + 0.04 * phase
var pullFrames = Math.floor(pullDuration / 16.67);
var pullTick = 0;
voidProj._pullInterval = LK.setInterval(function () {
if (!player || !voidProj.parent) return;
@@ -2184,13 +2184,13 @@
this.asset.alpha = 0.7 + 0.15 * Math.sin(growTick / 10);
}
};
}
- // 4. After 1.2s, explode in a massive ring and remove void
+ // 4. After 1.5s, explode in a smaller, slower ring and remove void
LK.setTimeout(function () {
- // Massive ring of projectiles
- var ringN = 18 + Math.floor(phase * 1.2);
- var ringSpeed = 6.2 + phase * 0.25;
+ // Easier: fewer projectiles, slower speed
+ var ringN = 10 + Math.floor(phase * 0.7); // was 18 + Math.floor(phase * 1.2)
+ var ringSpeed = 3.8 + phase * 0.13; // was 6.2 + phase * 0.25
for (var i = 0; i < ringN; i++) {
var angle = 2 * Math.PI / ringN * i;
var p = new Projectile();
p.init("void_maw_burst", projAsset);
make a red firey circle with black eyes that are mad and no mouth. In-Game asset. 2d. High contrast. No shadows
a happy blue circle simple. In-Game asset. 2d. High contrast. No shadows
a green circle that is very smart and has a monocle and has orange eyes. In-Game asset. 2d. High contrast. No shadows
only the ball
make a yellow circle that is like the sun that has sunglasses on. In-Game asset. 2d. High contrast. No shadows
only the sorce of the light
a grey circle with orange wyes and he is a space king. In-Game asset. 2d. High contrast. No shadows
make the beard a bit shorter
a purple circle with purple crystal armour and a sturn look on his face. only the head In-Game asset. 2d. High contrast. No shadows
a purple crystal. In-Game asset. 2d. High contrast. No shadows
a black circle that is dark and evil and has red eyes and an evil smirk. In-Game asset. 2d. High contrast. No shadows
remove the red aura and just have a red outline
a white circle with blue eyes and an icy face. In-Game asset. 2d. High contrast. No shadows
a snowflake. In-Game asset. 2d. High contrast. No shadows
remove the guns
a blue energy ball. In-Game asset. 2d. High contrast. No shadows
music_inferno
Music
music_verdant
Music
music_solaris
Music
music_abyss
Music
music_tempest
Music
music_prism
Music
music_obsidian
Music
music_blizzard
Music
music_ember
Music
music_menu
Music
sig_inferno
Sound effect
sig_verdant
Sound effect
sig_solaris
Sound effect
sig_abyss
Sound effect
sig_tempest
Sound effect
sig_prism
Sound effect
sig_obsidian
Sound effect
sig_blizzard
Sound effect
sig_ember
Sound effect