Code edit (5 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
add new meme and sound effect
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: resultText is not defined' in or related to this line: 'if (resultText && resultText.text === 'GAME OVER') {' Line Number: 1448
User prompt
outroMusic only plays when you write Game Over, it never plays at any other time. Game Music gameMusic3 only plays when the game is playing and Game Over is not written, set this very well.
Code edit (1 edits merged)
Please save this source code
User prompt
Add Meme12 and add sound effect for Meme12.
User prompt
Add sound effect for breast10 and breast11.
User prompt
Add sound effect for breast10 and breast11.
User prompt
Add a new meme. with sound effect.
User prompt
Add a new meme. with sound effect.
Code edit (1 edits merged)
Please save this source code
User prompt
This is fun.. feels a lot like the original and the gameplay controls are smooth.. I scored 18300. For this game, I would suggest adding a high score tally to track your progress so the player can push to beat their personal best scores. I'll definitely be back to try and top mine. 😊 *** I received such a comment, let's make the appropriate plugin.*** ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
make the bullets better. But don't make it rickety.
User prompt
Make a new acet for the spaceship.Make a new acet for the spaceship.Make a new acet for the spaceship.Make a new acet for the spaceship.
User prompt
Make a new acet for the spaceship.
User prompt
don't make the ship out of pixels, make it as an acet instead, and it'll be less cramped.
User prompt
Make the bullet a little bigger.
User prompt
Make the bullet a little bigger.
User prompt
The bullet should only consist of one large pixel, less than one can cause a crash.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var AnimatedLetter = Container.expand(function (letter, targetX, targetY, index) { var self = Container.call(this); self.letterText = new Text2(letter, { size: 200, fill: 0xFFFFFF }); self.letterText.anchor.set(0.5, 0.5); self.addChild(self.letterText); self.targetX = targetX; self.targetY = targetY; self.index = index; self.x = 2048 / 2; self.y = targetY + 150; self.alpha = 0; self.letterText.scaleX = 0.3; self.letterText.scaleY = 0.3; self.letterText.rotation = Math.PI * 0.5; self.animateIn = function () { // Slide from center to target position with fade in tween(self, { alpha: 1, x: self.targetX, y: self.targetY }, { duration: 1000, easing: tween.easeOut }); // Scale and rotate into place tween(self.letterText, { scaleX: 1, scaleY: 1, rotation: 0 }, { duration: 1200, easing: tween.elasticOut }); }; self.transformColor = function (delay) { // Staggered color transformation with delay LK.setTimeout(function () { // Add subtle pulse before color change tween(self.letterText, { scaleX: 1.1, scaleY: 1.1 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self.letterText, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeIn }); } }); // Color transformation wave tween(self.letterText, { tint: 0xff8000 }, { duration: 800, easing: tween.easeInOut }); }, delay); }; return self; }); var Asteroid = Container.expand(function (size, memeType) { var self = Container.call(this); // Size can be 0 (small), 1 (medium), or 2 (large) self.size = size || 2; // Create a container for the asteroid var asteroidGraphics = new Container(); self.addChild(asteroidGraphics); // Store the meme type or generate a random one if not provided self.memeType = memeType !== undefined ? memeType : Math.floor(Math.random() * 14); // Now 0-13 for 14 memes // Create the asteroid image based on size var assetId; if (self.size === 0) { assetId = 'memeSmall'; } else if (self.size === 1) { assetId = 'memeMedium'; } else { assetId = 'meme' + self.memeType; } // Add the meme image as the asteroid var memeImage = LK.getAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); asteroidGraphics.addChild(memeImage); // Random velocity var speed = (3 - self.size) * 0.8 + 0.5; // Smaller asteroids move faster var angle = Math.random() * Math.PI * 2; self.velocity = { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed }; // Random rotation self.rotationSpeed = (Math.random() - 0.5) * 0.05; self.update = function () { // Move self.x += self.velocity.x; self.y += self.velocity.y; // Rotate asteroidGraphics.rotation += self.rotationSpeed; // Wrap around screen edges - optimize by only checking when needed if (self.x < -50) { self.x = 2098; self.justWrapped = true; } else if (self.x > 2098) { self.x = -50; self.justWrapped = true; } else if (self.y < -50) { self.y = 2782; self.justWrapped = true; } else if (self.y > 2782) { self.y = -50; self.justWrapped = true; } else { self.justWrapped = false; } }; self.getPoints = function () { return (3 - self.size) * 100; // Small: 300, Medium: 200, Large: 100 }; return self; }); var AuraParticle = Container.expand(function () { var self = Container.call(this); // Create particle visual - turquoise and larger than thrust particles var particleGraphics = LK.getAsset('bulletShape', { anchorX: 0.5, anchorY: 0.5, width: 36, // Larger than thrust particles height: 36, tint: 0x40E0D0 // Turquoise color for aura }); particleGraphics.alpha = 1.0; self.addChild(particleGraphics); // Particle properties self.velocity = { x: 0, y: 0 }; self.lifespan = 45; // Longer lifespan than thrust particles self.age = 0; self.update = function () { // Move according to velocity self.x += self.velocity.x; self.y += self.velocity.y; // Age the particle self.age++; // Pulse the particle size for more dynamic effect var pulseScale = 1 + 0.2 * Math.sin(self.age * 0.2); particleGraphics.scale.set(pulseScale, pulseScale); // Fade out as it ages, with a faster tail-end fade var lifeRatio = self.age / self.lifespan; particleGraphics.alpha = 0.9 * (1 - lifeRatio * lifeRatio); // Return true if particle should be removed return self.age >= self.lifespan; }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); // Create a container for pixel art bullet var bulletGraphics = new Container(); self.addChild(bulletGraphics); // Create a solid bullet (a single large pixel) var bulletSize = 60; // Size of the bullet - increased to 60 for better visibility // Create a single solid element for the bullet var bulletSprite = LK.getAsset('bulletShape', { anchorX: 0.5, anchorY: 0.5, width: bulletSize, height: bulletSize, tint: 0xFFFF00 // Bright yellow color for better visibility }); bulletGraphics.addChild(bulletSprite); self.speed = 10; self.velocity = { x: 0, y: 0 }; self.lifespan = 180; // 3 seconds at 60fps - triple the firing range self.age = 0; self.update = function () { self.x += self.velocity.x; self.y += self.velocity.y; self.age++; // Wrap around screen edges - use else-if to avoid redundant checks if (self.x < 0) { self.x = 2048; } else if (self.x > 2048) { self.x = 0; } if (self.y < 0) { self.y = 2732; } else if (self.y > 2732) { self.y = 0; } }; return self; }); var Button = Container.expand(function (assetId) { var self = Container.call(this); // Create button as a solid shape var buttonGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); buttonGraphics.alpha = 0.5; // Determine button shape based on assetId var isCircle = assetId === 'fireButton' || assetId === 'forwardButton'; var buttonSize = 60; // Radius or half-width self.isPressed = false; // Add aura ready text if this is the fire button self.isFireButton = assetId === 'fireButton'; if (self.isFireButton) { self.auraReadyText = new Text2('AURA READY!', { size: 30, fill: 0xFFFF00 }); self.auraReadyText.anchor.set(0.5, 0.5); self.auraReadyText.y = -90; // Position above the button self.auraReadyText.visible = false; // Initially hidden self.addChild(self.auraReadyText); } self.down = function (x, y, obj) { self.isPressed = true; buttonGraphics.alpha = 0.8; }; self.up = function (x, y, obj) { self.isPressed = false; buttonGraphics.alpha = 0.5; }; // Method to update aura ready text visibility self.updateAuraText = function (isAuraReady, cooldownRemaining) { if (self.isFireButton && self.auraReadyText) { // Initialize style property if it doesn't exist if (!self.auraReadyText.style) { self.auraReadyText.style = {}; } if (isAuraReady) { self.auraReadyText.visible = true; self.auraReadyText.setText('AURA READY!'); self.auraReadyText.style.fill = 0xFFFF00; // Yellow when ready // Add pulsing animation when aura is ready if (!self.pulseAnimation) { self.pulseAnimation = true; pulsateAuraText(); } } else { // Show countdown when not ready if (cooldownRemaining !== undefined) { var secondsRemaining = Math.ceil(cooldownRemaining / 60); // Convert frames to seconds var secondsRemaining = Math.ceil(cooldownRemaining / 60); // Convert frames to seconds self.auraReadyText.visible = true; self.auraReadyText.setText('AURA: ' + secondsRemaining + 's'); self.auraReadyText.style.fill = 0x3399FF; // Blue for cooldown } else { self.auraReadyText.visible = false; } self.pulseAnimation = false; } } function pulsateAuraText() { if (!self.pulseAnimation) { return; } tween(self.auraReadyText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { if (!self.pulseAnimation) { return; } tween(self.auraReadyText, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeInOut, onFinish: pulsateAuraText }); } }); } }; return self; }); var CreeperAsteroid = Container.expand(function () { var self = Container.call(this); // Create a container for the asteroid var asteroidGraphics = new Container(); self.addChild(asteroidGraphics); // Always use meme9 (the Troll Face) as the creeper meme self.memeType = 9; // Add the meme image as the asteroid var memeImage = LK.getAsset('meme' + self.memeType, { anchorX: 0.5, anchorY: 0.5 }); asteroidGraphics.addChild(memeImage); // Explosive properties self.isExplosive = true; self.explosionRadius = 300; // Random velocity - slower than normal asteroids var speed = 0.9; var angle = Math.random() * Math.PI * 2; self.velocity = { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed }; // Random rotation - slower to appear more menacing self.rotationSpeed = (Math.random() - 0.5) * 0.03; // Pulsating effect self.pulseDirection = 1; self.pulseAmount = 0; self.pulseMax = 0.15; self.pulseSpeed = 0.008; self.update = function () { // Move self.x += self.velocity.x; self.y += self.velocity.y; // Rotate asteroidGraphics.rotation += self.rotationSpeed; // Pulsating effect self.pulseAmount += self.pulseDirection * self.pulseSpeed; if (self.pulseAmount >= self.pulseMax) { self.pulseDirection = -1; } else if (self.pulseAmount <= 0) { self.pulseDirection = 1; } // Apply pulse to scale var pulseScale = 1 + self.pulseAmount; asteroidGraphics.scale.set(pulseScale, pulseScale); // Make it glow green occasionally if (LK.ticks % 60 < 15) { asteroidGraphics.tint = 0x88FF88; } else { asteroidGraphics.tint = 0xFFFFFF; } // Wrap around screen edges if (self.x < -50) { self.x = 2098; } if (self.x > 2098) { self.x = -50; } if (self.y < -50) { self.y = 2782; } if (self.y > 2782) { self.y = -50; } }; self.getPoints = function () { return 200; // More points than regular asteroids }; self.explode = function () { // Create explosion visual effect var explosion = new Container(); var explosionGraphic = LK.getAsset('forwardButton', { anchorX: 0.5, anchorY: 0.5, width: self.explosionRadius * 2, height: self.explosionRadius * 2, tint: 0x88FF88 // Green explosion }); explosionGraphic.alpha = 0.7; explosion.addChild(explosionGraphic); explosion.x = self.x; explosion.y = self.y; game.addChild(explosion); // Animate the explosion - optimized for better performance tween(explosionGraphic, { alpha: 0, width: self.explosionRadius * 2.2, // Slightly reduced final size height: self.explosionRadius * 2.2 // Slightly reduced final size }, { duration: 400, // Slightly faster animation easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(explosion); } }); // Play specific explosion sound LK.getSound('meme9Explosion').play(); }; return self; }); var CreeperIntroSequence = Container.expand(function () { var self = Container.call(this); // Layers for effects var starfieldLayer = new Container(); var creeperLayer = new Container(); var textLayer = new Container(); var shipLayer = new Container(); self.addChild(starfieldLayer); self.addChild(creeperLayer); self.addChild(textLayer); self.addChild(shipLayer); // Create starfield with green tint - significantly reduced count for better performance var stars = []; for (var i = 0; i < 20; i++) { var star = LK.getAsset('bulletShape', { anchorX: 0.5, anchorY: 0.5, width: 3 + Math.random() * 5, height: 3 + Math.random() * 5, x: Math.random() * 2048, y: Math.random() * 2732, tint: 0x88FF88 // Green stars }); star.alpha = 0.3 + Math.random() * 0.7; star.velocity = 10 + Math.random() * 20; stars.push(star); starfieldLayer.addChild(star); } // Create creeper silhouettes in background var creepers = []; for (var j = 0; j < 6; j++) { var creeper = LK.getAsset('meme9', { anchorX: 0.5, anchorY: 0.5, x: 200 + Math.random() * 1600, y: -300 - Math.random() * 600, tint: 0x44AA44 // Dark green silhouettes }); creeper.alpha = 0.3; creeper.scale.set(0.3, 0.3); creepers.push(creeper); creeperLayer.addChild(creeper); } // Warning text var warningText = new Text2('CAUTION: CREEPER TERRITORY!', { size: 80, fill: 0x88FF88 // Green text }); warningText.anchor.set(0.5, 0.5); warningText.x = 2048 / 2; warningText.y = 2732 / 2; warningText.alpha = 0; textLayer.addChild(warningText); // Subtitle warning var subtitleText = new Text2('BEWARE OF EXPLOSIVES!', { size: 60, fill: 0xFF3333 // Red text for emphasis }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 2048 / 2; subtitleText.y = 2732 / 2 + 100; subtitleText.alpha = 0; textLayer.addChild(subtitleText); // Hero ship var heroShip = new Ship(); heroShip.x = -100; heroShip.y = 2732 / 2; shipLayer.addChild(heroShip); // Animation sequence self.phase = 0; self.timer = 0; self.finished = false; self.update = function () { self.timer++; // Update stars for (var i = 0; i < stars.length; i++) { var star = stars[i]; star.y += star.velocity; if (star.y > 2732) { star.y = -10; star.x = Math.random() * 2048; } } // Animation phases switch (self.phase) { case 0: // Ship enters from left (0-2s) tween(heroShip, { x: 2048 / 2 }, { duration: 2000, easing: tween.easeOut }); self.phase = 1; break; case 1: // Creepers approach (2-4s) for (var j = 0; j < creepers.length; j++) { creepers[j].y += 3; creepers[j].scale.x += 0.003; creepers[j].scale.y += 0.003; } if (self.timer === 120) { // After 2s, show warning text tween(warningText, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); // Make text glitch effect LK.setInterval(function () { warningText.scale.set(1 + Math.random() * 0.05, 1 + Math.random() * 0.05); warningText.x = 2048 / 2 + (Math.random() * 20 - 10); }, 80); self.phase = 2; } break; case 2: // Warning display (4-6s) for (var k = 0; k < creepers.length; k++) { creepers[k].y += 3; creepers[k].scale.x += 0.004; creepers[k].scale.y += 0.004; } if (self.timer === 240) { // After 4s, show subtitle tween(subtitleText, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); // Flash the screen green briefly LK.effects.flashScreen(0x88FF88, 300); self.phase = 3; } break; case 3: // Subtitle and prepare to end (6-7s) if (self.timer === 360) { // After 6s, finish intro tween(warningText, { alpha: 0 }, { duration: 500 }); tween(subtitleText, { alpha: 0 }, { duration: 500 }); tween(heroShip, { x: 2048 + 100 }, { duration: 1000, easing: tween.easeIn }); self.phase = 4; } break; case 4: // Fade out and end (7-8s) if (self.timer === 420) { self.finished = true; } break; } return self.finished; }; self.skip = function () { // Do nothing - disable skipping }; return self; }); var GlaudIntroSequence = Container.expand(function () { var self = Container.call(this); var particles = []; var letters = []; var particlePool = []; // Create wireframe ball var wireframeBall = new WireframeBall(); wireframeBall.x = 2048 / 2; wireframeBall.y = 2732 / 2; wireframeBall.scaleX = 0.1; wireframeBall.scaleY = 0.1; wireframeBall.alpha = 0; self.addChild(wireframeBall); // Create GLAUD letters var letterPositions = [{ letter: 'G', x: 2048 / 2 - 400, y: 2732 / 2 }, { letter: 'L', x: 2048 / 2 - 200, y: 2732 / 2 }, { letter: 'A', x: 2048 / 2, y: 2732 / 2 }, { letter: 'U', x: 2048 / 2 + 200, y: 2732 / 2 }, { letter: 'D', x: 2048 / 2 + 400, y: 2732 / 2 }]; for (var i = 0; i < letterPositions.length; i++) { var letterData = letterPositions[i]; var animatedLetter = new AnimatedLetter(letterData.letter, letterData.x, letterData.y, i); letters.push(animatedLetter); self.addChild(animatedLetter); } // Initialize particle pool for better performance function initParticlePool() { for (var i = 0; i < 50; i++) { var particle = new GlaudParticle('energy'); particle.active = false; particlePool.push(particle); } } function getPooledParticle() { for (var i = 0; i < particlePool.length; i++) { if (!particlePool[i].active) { particlePool[i].active = true; return particlePool[i]; } } // If pool is empty, create new particle var newParticle = new GlaudParticle('energy'); newParticle.active = true; particlePool.push(newParticle); return newParticle; } function returnParticleToPool(particle) { particle.active = false; if (particle.parent) { particle.parent.removeChild(particle); } } // Initialize intro sequence with chained animations function initIntroSequence() { // Start ball growth animation startBallGrowth(); } function startBallGrowth() { LK.getSound('ballGrow').play(); // Wireframe ball growth with rotation acceleration tween(wireframeBall, { scaleX: 3.2, scaleY: 3.2, alpha: 1 }, { duration: 3000, easing: tween.easeInOut, onFinish: function onFinish() { // Brief pause at maximum size, then start contraction LK.setTimeout(function () { startBallContraction(); }, 500); } }); // Gradually increase rotation speeds tween(wireframeBall, { rotationSpeedX: 0.08, rotationSpeedY: 0.1, rotationSpeedZ: 0.06 }, { duration: 3000, easing: tween.easeInOut }); // Add energy buildup with sparkle intensity LK.setTimeout(function () { // Final pulse before contraction tween(wireframeBall, { scaleX: 3.5, scaleY: 3.5 }, { duration: 200, easing: tween.easeInOut }); // Maximum rotation speed for instability effect wireframeBall.rotationSpeedX = 0.12; wireframeBall.rotationSpeedY = 0.15; wireframeBall.rotationSpeedZ = 0.1; }, 2000); } function startBallContraction() { LK.getSound('ballContract').play(); // Dramatic wireframe ball implosion with extreme rotation wireframeBall.rotationSpeedX = 0.8; wireframeBall.rotationSpeedY = 1.0; wireframeBall.rotationSpeedZ = 0.6; tween(wireframeBall, { scaleX: 0, scaleY: 0, alpha: 0 }, { duration: 600, easing: tween.easeInQuad, onFinish: function onFinish() { wireframeBall.alpha = 0; // Start letter formation after brief pause LK.setTimeout(function () { startLetterFormation(); }, 200); } }); // Delay explosion slightly for implosion effect LK.setTimeout(function () { createParticleExplosion(); }, 300); } function startLetterFormation() { // Animate letters appearing one by one animateLetterSequence(0); } function animateLetterSequence(index) { if (index >= letters.length) { // All letters have appeared, start color transformation LK.setTimeout(function () { startColorTransformation(); }, 500); return; } LK.getSound('letterAppear').play(); letters[index].animateIn(); // Schedule next letter with delay LK.setTimeout(function () { animateLetterSequence(index + 1); }, 250); } function startColorTransformation() { LK.getSound('colorTransform').play(); // Staggered color transformation for wave effect var completedTransforms = 0; for (var i = 0; i < letters.length; i++) { (function (letterIndex) { LK.setTimeout(function () { letters[letterIndex].transformColor(0); completedTransforms++; // Check if all transformations are complete if (completedTransforms === letters.length) { LK.setTimeout(function () { startFinalEmphasis(); }, 800); } }, letterIndex * 150); })(i); } } function startFinalEmphasis() { // Overall luminosity surge for all letters for (var i = 0; i < letters.length; i++) { tween(letters[i].letterText, { tint: 0xffaa00, // Brighter orange/yellow scaleX: 1.08, scaleY: 1.08 }, { duration: 500, easing: tween.easeOut }); } // Start progressive fade-out after surge LK.setTimeout(function () { startProgressiveFadeOut(); }, 500); } function startProgressiveFadeOut() { // Unified Power Display (0 - 0.8 Seconds) startUnifiedPowerDisplay(); } function startUnifiedPowerDisplay() { // Screen flash to white briefly for dramatic effect LK.effects.flashScreen(0xffffff, 200); // Make all letters shine with intense light simultaneously for (var i = 0; i < letters.length; i++) { // Multiple tween stages for more dramatic buildup tween(letters[i].letterText, { tint: 0xffdd00, // Bright gold first scaleX: 1.25, scaleY: 1.25 }, { duration: 300, easing: tween.easeOut, onFinish: function (targetLetter) { return function () { tween(targetLetter.letterText, { tint: 0xffffff, // Then to brilliant white scaleX: 1.4, scaleY: 1.4 }, { duration: 500, easing: tween.easeInOut }); }; }(letters[i]) }); } // Create god rays emanating from the text createGodRays(); // Create energy particles swirling around letters createEnergySwirl(); // Schedule the spectacular disappearance LK.setTimeout(function () { startSpectacularDisappearance(); }, 800); } function createGodRays() { // Create multiple layers of light rays for depth for (var layer = 0; layer < 2; layer++) { var rayCount = layer === 0 ? 16 : 8; var rayLength = layer === 0 ? 4 : 6; for (var i = 0; i < rayCount; i++) { var ray = self.addChild(LK.getAsset('wireframeEdge', { anchorX: 0.5, anchorY: 0, x: 2048 / 2, y: 2732 / 2, rotation: Math.PI * 2 * i / rayCount + layer * Math.PI / 16, scaleX: 0.3 + layer * 0.2, scaleY: rayLength, tint: layer === 0 ? 0xffaa00 : 0xffffff, alpha: 0 })); // Staggered ray appearance LK.setTimeout(function (currentRay, delayTime) { return function () { tween(currentRay, { alpha: 0.6 + Math.random() * 0.3, scaleY: rayLength + 2 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Pulse the rays before fade tween(currentRay, { alpha: 0.9, scaleY: rayLength + 3 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(currentRay, { alpha: 0 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { currentRay.destroy(); } }); } }); } }); }; }(ray, i * 50 + layer * 200), i * 50 + layer * 200); } } } function createEnergySwirl() { // Create swirling energy particles around the text for (var i = 0; i < 30; i++) { var energyParticle = self.addChild(LK.getAsset('sparkle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + Math.cos(i * 0.5) * 200, y: 2732 / 2 + Math.sin(i * 0.5) * 200, tint: 0xffdd00, alpha: 0 })); // Animate particles in spiral motion var particleDelay = i * 30; LK.setTimeout(function (particle, index) { return function () { particle.alpha = 0.8; // Create spiral motion var _spiralTween = function spiralTween(currentAngle) { var radius = 150 + Math.sin(currentAngle * 0.1) * 50; var newX = 2048 / 2 + Math.cos(currentAngle) * radius; var newY = 2732 / 2 + Math.sin(currentAngle) * radius; tween(particle, { x: newX, y: newY, rotation: currentAngle }, { duration: 100, easing: tween.linear, onFinish: function onFinish() { if (currentAngle < Math.PI * 6) { _spiralTween(currentAngle + 0.3); } else { // Burst toward center tween(particle, { x: 2048 / 2, y: 2732 / 2, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 200, easing: tween.easeInQuad, onFinish: function onFinish() { particle.destroy(); } }); } } }); }; _spiralTween(index * 0.5); }; }(energyParticle, i), particleDelay); } } function startSpectacularDisappearance() { // Create dramatic screen distortion effect for (var i = 0; i < letters.length; i++) { var letter = letters[i]; // Add violent shaking before implosion var shakeIntensity = 15; var shakeCount = 0; var shakeInterval = LK.setInterval(function (targetLetter) { return function () { targetLetter.x += (Math.random() - 0.5) * shakeIntensity; targetLetter.y += (Math.random() - 0.5) * shakeIntensity; shakeCount++; if (shakeCount > 10) { LK.clearInterval(shakeInterval); } }; }(letter), 50); } // Enhanced implosion with multiple stages LK.setTimeout(function () { var centerX = 2048 / 2; var centerY = 2732 / 2; // Stage 1: Rapid acceleration toward center for (var i = 0; i < letters.length; i++) { var letter = letters[i]; tween(letter, { x: centerX, y: centerY, scaleX: 0.3, scaleY: 0.3 }, { duration: 180, easing: tween.easeInCubic }); tween(letter.letterText, { tint: 0xffffff, alpha: 1.2 }, { duration: 180 }); } // Stage 2: Final compression LK.setTimeout(function () { for (var i = 0; i < letters.length; i++) { var letter = letters[i]; tween(letter, { scaleX: 0.05, scaleY: 0.05 }, { duration: 80, easing: tween.easeInQuart }); } }, 120); // Create the enhanced bright point and burst LK.setTimeout(function () { createEnhancedBrightPointAndBurst(); }, 200); }, 500); } function createEnhancedBrightPointAndBurst() { // Create multiple concentric energy rings var rings = []; for (var r = 0; r < 3; r++) { var ring = self.addChild(LK.getAsset('wireframeJoint', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 0.1 + r * 0.1, scaleY: 0.1 + r * 0.1, tint: r === 0 ? 0xffffff : r === 1 ? 0xffdd00 : 0xffaa00, alpha: 1 })); rings.push(ring); } // Intense multi-stage flash var flashStage = 0; var flashInterval = LK.setInterval(function () { // Screen flash with varying intensities var flashColors = [0xffffff, 0xffdd00, 0xffffff, 0xffaa00]; LK.effects.flashScreen(flashColors[flashStage % flashColors.length], 150); for (var i = 0; i < rings.length; i++) { tween(rings[i], { scaleX: (flashStage + 1) * 0.5 + i * 0.2, scaleY: (flashStage + 1) * 0.5 + i * 0.2, alpha: 1 - flashStage * 0.15 }, { duration: 120, easing: tween.easeOut }); } flashStage++; if (flashStage >= 4) { LK.clearInterval(flashInterval); // Final burst LK.setTimeout(function () { createMegaEnergyBurst(); // Hide all letters for (var i = 0; i < letters.length; i++) { letters[i].visible = false; } // Clean up rings for (var i = 0; i < rings.length; i++) { rings[i].destroy(); } }, 200); } }, 150); } function createMegaEnergyBurst() { // Create multiple expanding shockwaves for (var wave = 0; wave < 4; wave++) { LK.setTimeout(function (waveIndex) { return function () { var shockwave = self.addChild(LK.getAsset('forwardButton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, width: 30, height: 30, tint: waveIndex === 0 ? 0xffffff : waveIndex % 2 === 0 ? 0xffdd00 : 0xffaa00, alpha: 0.9 })); // Animate shockwave expanding with easing tween(shockwave, { width: 1200 + waveIndex * 200, height: 1200 + waveIndex * 200, alpha: 0 }, { duration: 600 + waveIndex * 100, easing: tween.easeOutCubic, onFinish: function onFinish() { shockwave.destroy(); } }); }; }(wave), wave * 150); } // Create spectacular particle explosion for (var i = 0; i < 50; i++) { LK.setTimeout(function (particleIndex) { return function () { var particle = self.addChild(LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, tint: particleIndex % 3 === 0 ? 0xffffff : particleIndex % 3 === 1 ? 0xffdd00 : 0xffaa00 })); var angle = Math.random() * Math.PI * 2; var speed = 12 + Math.random() * 20; var distance = 40 + Math.random() * 50; var targetX = particle.x + Math.cos(angle) * distance; var targetY = particle.y + Math.sin(angle) * distance; // Multi-stage particle motion tween(particle, { x: targetX, y: targetY, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOutQuad, onFinish: function onFinish() { // Second stage - continue outward and fade var finalX = particle.x + Math.cos(angle) * 30; var finalY = particle.y + Math.sin(angle) * 30; tween(particle, { x: finalX, y: finalY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 400 + Math.random() * 300, easing: tween.easeOutCubic, onFinish: function onFinish() { particle.destroy(); } }); } }); }; }(i), Math.random() * 200); } // Create lingering energy wisps for (var w = 0; w < 15; w++) { LK.setTimeout(function () { var wisp = self.addChild(LK.getAsset('sparkle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + (Math.random() - 0.5) * 100, y: 2732 / 2 + (Math.random() - 0.5) * 100, tint: 0xffdd00, alpha: 0.6 })); // Gentle floating motion before fade tween(wisp, { x: wisp.x + (Math.random() - 0.5) * 200, y: wisp.y + (Math.random() - 0.5) * 200, alpha: 0, rotation: Math.PI * 2 }, { duration: 1500 + Math.random() * 1000, easing: tween.easeOutSine, onFinish: function onFinish() { wisp.destroy(); } }); }, w * 100 + Math.random() * 300); } // Extended silence then finish with dramatic fade to black LK.setTimeout(function () { // Final dramatic fade to black LK.effects.flashScreen(0x000000, 1500); LK.setTimeout(function () { self.finished = true; }, 1500); }, 1200); } function createLetterParticles(letter) { // Create small orange light particles when letter fades for (var i = 0; i < 8; i++) { var particle = self.addChild(LK.getAsset('particle', { anchorX: 0.5, anchorY: 0.5, x: letter.x + (Math.random() - 0.5) * 100, y: letter.y + (Math.random() - 0.5) * 100 })); particle.tint = 0xff8000; // Orange particles particle.scaleX = 0.3 + Math.random() * 0.4; particle.scaleY = particle.scaleX; var angle = Math.random() * Math.PI * 2; var speed = 3 + Math.random() * 5; var targetX = particle.x + Math.cos(angle) * speed * 20; var targetY = particle.y + Math.sin(angle) * speed * 20; tween(particle, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 600 + Math.random() * 400, easing: tween.easeOut, onFinish: function onFinish() { particle.destroy(); } }); } } function createParticleExplosion() { // Create wireframe edge fragments for better visual impact var fragmentsToCreate = Math.min(wireframeBall.edges.length, 15); // Fewer but more prominent fragments for (var i = 0; i < fragmentsToCreate; i++) { if (Math.random() < 0.9) { var edgeIndex = Math.floor(Math.random() * wireframeBall.edges.length); var edge = wireframeBall.edges[edgeIndex]; var fragment = self.addChild(LK.getAsset('wireframeEdge', { anchorX: 0.5, anchorY: 0.5, x: wireframeBall.x + edge.x, y: wireframeBall.y + edge.y, rotation: edge.rotation, scaleX: edge.scaleX, scaleY: edge.scaleY, alpha: edge.alpha })); // Calculate scatter direction from center var centerX = wireframeBall.x; var centerY = wireframeBall.y; var fragmentX = fragment.x; var fragmentY = fragment.y; var dirX = fragmentX - centerX; var dirY = fragmentY - centerY; var distance = Math.sqrt(dirX * dirX + dirY * dirY); if (distance === 0) { distance = 1; } dirX /= distance; dirY /= distance; var speed = 15 + Math.random() * 25; var targetX = fragmentX + dirX * speed * 20; var targetY = fragmentY + dirY * speed * 20; // Add random spinning var rotationSpeed = (Math.random() - 0.5) * 0.3; // Animate fragment scattering tween(fragment, { x: targetX, y: targetY, alpha: 0, scaleX: 0.1, scaleY: 0.1, rotation: fragment.rotation + rotationSpeed * 10 }, { duration: 1000 + Math.random() * 800, easing: tween.easeOut, onFinish: function onFinish() { fragment.destroy(); } }); } } // Create energy burst from joints (reduced count) var jointsToCreate = Math.min(wireframeBall.joints.length, 6); for (var i = 0; i < jointsToCreate; i++) { var jointIndex = Math.floor(Math.random() * wireframeBall.joints.length); var joint = wireframeBall.joints[jointIndex]; var energyBurst = self.addChild(LK.getAsset('wireframeJoint', { anchorX: 0.5, anchorY: 0.5, x: wireframeBall.x + joint.x, y: wireframeBall.y + joint.y, scaleX: 1.5, scaleY: 1.5 })); var angle = Math.random() * Math.PI * 2; var speed = 8 + Math.random() * 12; var targetX = energyBurst.x + Math.cos(angle) * speed * 15; var targetY = energyBurst.y + Math.sin(angle) * speed * 15; tween(energyBurst, { x: targetX, y: targetY, alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 800 + Math.random() * 600, easing: tween.easeOut, onFinish: function onFinish() { energyBurst.destroy(); } }); } // Create main particle burst using pooled particles for (var i = 0; i < 15; i++) { var particle = getPooledParticle(); if (particle) { particle.x = wireframeBall.x; particle.y = wireframeBall.y; var angle = Math.PI * 2 * i / 15; var speed = 10 + Math.random() * 18; particle.velocityX = Math.cos(angle) * speed; particle.velocityY = Math.sin(angle) * speed; particle.life = 1.0; particle.markForDestroy = false; particles.push(particle); self.addChild(particle); } } } // Initialize particle pool and start sequence initParticlePool(); initIntroSequence(); self.finished = false; self.update = function () { // Update active particles efficiently for (var i = particles.length - 1; i >= 0; i--) { var particle = particles[i]; if (particle.markForDestroy) { returnParticleToPool(particle); particles.splice(i, 1); } } return self.finished; }; return self; }); var GlaudParticle = Container.expand(function (particleType) { var self = Container.call(this); var particleGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.life = 1.0; self.maxLife = 1.0; self.fadeSpeed = 0.015 + Math.random() * 0.01; self.rotationSpeed = (Math.random() - 0.5) * 0.2; self.initialScale = 0.5 + Math.random() * 0.5; particleGraphics.scaleX = self.initialScale; particleGraphics.scaleY = self.initialScale; if (particleType === 'energy') { particleGraphics.tint = 0x4a90e2; } else { particleGraphics.tint = 0xffffff; } self.update = function () { self.x += self.velocityX; self.y += self.velocityY; self.velocityX *= 0.98; // Add friction self.velocityY *= 0.98; particleGraphics.rotation += self.rotationSpeed; self.life -= self.fadeSpeed; var fadeRatio = self.life / self.maxLife; particleGraphics.alpha = fadeRatio; particleGraphics.scaleX = self.initialScale * fadeRatio; particleGraphics.scaleY = self.initialScale * fadeRatio; if (self.life <= 0) { self.markForDestroy = true; } }; return self; }); var IntroSequence = Container.expand(function () { var self = Container.call(this); // Layers for effects var starfieldLayer = new Container(); var memesLayer = new Container(); var textLayer = new Container(); var shipLayer = new Container(); var titleLayer = new Container(); self.addChild(starfieldLayer); self.addChild(memesLayer); self.addChild(textLayer); self.addChild(shipLayer); self.addChild(titleLayer); // Create starfield - significantly reduced number for better performance var stars = []; for (var i = 0; i < 20; i++) { var star = LK.getAsset('bulletShape', { anchorX: 0.5, anchorY: 0.5, width: 3 + Math.random() * 5, height: 3 + Math.random() * 5, x: Math.random() * 2048, y: Math.random() * 2732, tint: 0xFFFFFF }); star.alpha = 0.3 + Math.random() * 0.7; star.velocity = 10 + Math.random() * 20; stars.push(star); starfieldLayer.addChild(star); } // Create distant meme silhouettes var silhouettes = []; for (var j = 0; j < 10; j++) { var memeType = Math.floor(Math.random() * 14); // Now 0-13 for 14 memes var meme = LK.getAsset('meme' + memeType, { anchorX: 0.5, anchorY: 0.5, x: 200 + Math.random() * 1600, y: -300 - Math.random() * 600, tint: 0x333333 }); meme.alpha = 0.3; meme.scale.set(0.3, 0.3); silhouettes.push(meme); memesLayer.addChild(meme); } // Emergency broadcast text var emergencyText = new Text2('MEME OVERLOAD DETECTED', { size: 80, fill: 0xFF3333 }); emergencyText.anchor.set(0.5, 0.5); emergencyText.x = 2048 / 2; emergencyText.y = 2732 / 2; emergencyText.alpha = 0; textLayer.addChild(emergencyText); // Hero ship var heroShip = new Ship(); heroShip.x = 2048 / 2; heroShip.y = 2732 + 100; heroShip.alpha = 0; shipLayer.addChild(heroShip); // Title var titleText = new Text2('MEMES ASTEROID WAR', { size: 120, fill: 0xFF8000 }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 2 - 300; titleText.alpha = 0; titleLayer.addChild(titleText); // Call to action var ctaText = new Text2('TAP TO DEFEND THE INTERNET', { size: 60, fill: 0xFFFFFF }); ctaText.anchor.set(0.5, 0.5); ctaText.x = 2048 / 2; ctaText.y = 2732 / 2 + 300; ctaText.alpha = 0; titleLayer.addChild(ctaText); // Animation sequence self.phase = 0; self.timer = 0; self.finished = false; self.update = function () { self.timer++; // Update stars for (var i = 0; i < stars.length; i++) { var star = stars[i]; star.y += star.velocity; if (star.y > 2732) { star.y = -10; star.x = Math.random() * 2048; } } // Animation phases switch (self.phase) { case 0: // Starfield intro (0-2s) if (self.timer === 60) { // After 1s, start meme approach self.phase = 1; } break; case 1: // Meme approach (2-4s) for (var j = 0; j < silhouettes.length; j++) { silhouettes[j].y += 3; silhouettes[j].scale.x += 0.003; silhouettes[j].scale.y += 0.003; } if (self.timer === 180) { // After 3s, show emergency text self.phase = 2; tween(emergencyText, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); // Make text glitch effect LK.setInterval(function () { emergencyText.scale.set(1 + Math.random() * 0.05, 1 + Math.random() * 0.05); emergencyText.x = 2048 / 2 + (Math.random() * 20 - 10); }, 80); } break; case 2: // Emergency broadcast (4-6s) for (var k = 0; k < silhouettes.length; k++) { silhouettes[k].y += 3; silhouettes[k].scale.x += 0.004; silhouettes[k].scale.y += 0.004; } if (self.timer === 300) { // After 5s, fade out emergency text and show ship self.phase = 3; tween(emergencyText, { alpha: 0 }, { duration: 500 }); // Animate hero ship rising heroShip.alpha = 1; tween(heroShip, { y: 2732 / 2 + 200 }, { duration: 1500, easing: tween.easeOut }); } break; case 3: // Ship reveal (6-8s) if (self.timer === 420) { // After 7s, show title self.phase = 4; // Animate title card tween(titleText, { alpha: 1 }, { duration: 800, easing: tween.easeOut }); // Create distortion effect on title var titleDistort = LK.setInterval(function () { titleText.scale.set(1 + Math.random() * 0.03, 1 + Math.random() * 0.03); if (self.timer > 480) { LK.clearInterval(titleDistort); titleText.scale.set(1, 1); } }, 50); } break; case 4: // Title card (8-10s) if (self.timer === 540) { // After 9s, show CTA self.phase = 5; tween(ctaText, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); // Make CTA pulse LK.setInterval(function () { if (ctaText.alpha === 1) { tween(ctaText, { alpha: 0.5 }, { duration: 800 }); } else { tween(ctaText, { alpha: 1 }, { duration: 800 }); } }, 1600); } break; case 5: // CTA (10s+) if (self.timer === 660) { // After 11s total, finish intro self.finished = true; } break; } return self.finished; }; self.skip = function () { self.finished = true; }; return self; }); var OutroSequence = Container.expand(function () { var self = Container.call(this); // Layers for effects var starfieldLayer = new Container(); var memesLayer = new Container(); var textLayer = new Container(); var shipLayer = new Container(); self.addChild(starfieldLayer); self.addChild(memesLayer); self.addChild(textLayer); self.addChild(shipLayer); // Create starfield - significantly reduced number for better performance var stars = []; for (var i = 0; i < 20; i++) { var star = LK.getAsset('bulletShape', { anchorX: 0.5, anchorY: 0.5, width: 3 + Math.random() * 5, height: 3 + Math.random() * 5, x: Math.random() * 2048, y: Math.random() * 2732, tint: 0xFFFFFF }); star.alpha = 0.3 + Math.random() * 0.7; star.velocity = 10 + Math.random() * 20; stars.push(star); starfieldLayer.addChild(star); } // Create exploding memes var memes = []; for (var j = 0; j < 20; j++) { var memeType = Math.floor(Math.random() * 14); // 0-13 for 14 memes var meme = LK.getAsset('meme' + memeType, { anchorX: 0.5, anchorY: 0.5, x: Math.random() * 2048, y: Math.random() * 2732, scaleX: 0.2, scaleY: 0.2 }); meme.rotationSpeed = (Math.random() - 0.5) * 0.1; meme.velocity = { x: (Math.random() - 0.5) * 8, y: (Math.random() - 0.5) * 8 }; memes.push(meme); memesLayer.addChild(meme); } // Game result text (Victory or Game Over) var resultText; if (score > (storage.highScore || 0) && score > 0) { resultText = new Text2('VICTORY!', { size: 120, fill: 0xFF8000 }); } else { resultText = new Text2('GAME OVER', { size: 120, fill: 0xFF3333 // Red color for Game Over }); } resultText.anchor.set(0.5, 0.5); resultText.x = 2048 / 2; resultText.y = 2732 / 2 - 300; resultText.alpha = 0; textLayer.addChild(resultText); // Score text var finalScoreText = new Text2('FINAL SCORE: ' + score, { size: 80, fill: 0xFFFFFF }); finalScoreText.anchor.set(0.5, 0.5); finalScoreText.x = 2048 / 2; finalScoreText.y = 2732 / 2; finalScoreText.alpha = 0; textLayer.addChild(finalScoreText); // Add message based on high score var messageText; if (score > (storage.highScore || 0) && score > 0) { messageText = new Text2('AMAZING JOB! NEW HIGH SCORE!', { size: 60, fill: 0xFFD700 // Gold color }); } else { messageText = new Text2('DON\'T GIVE UP! YOU\'LL DO BETTER NEXT TIME!', { size: 60, fill: 0x3399FF // Blue color }); } messageText.anchor.set(0.5, 0.5); messageText.x = 2048 / 2; messageText.y = 2732 / 2 + 100; messageText.alpha = 0; textLayer.addChild(messageText); // Continue text var continueText = new Text2('TAP TO CONTINUE', { size: 60, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 2048 / 2; continueText.y = 2732 / 2 + 300; continueText.alpha = 0; textLayer.addChild(continueText); // Animation sequence self.phase = 0; self.timer = 0; self.finished = false; self.update = function () { self.timer++; // Update stars - moving faster for outro for (var i = 0; i < stars.length; i++) { var star = stars[i]; star.y -= star.velocity; // Moving upward instead of downward if (star.y < -10) { star.y = 2732 + 10; star.x = Math.random() * 2048; } } // Update memes - spinning and moving across screen for (var j = 0; j < memes.length; j++) { var meme = memes[j]; meme.rotation += meme.rotationSpeed; meme.x += meme.velocity.x; meme.y += meme.velocity.y; // Bounce off edges if (meme.x < 0 || meme.x > 2048) { meme.velocity.x *= -1; } if (meme.y < 0 || meme.y > 2732) { meme.velocity.y *= -1; } } // Animation phases switch (self.phase) { case 0: // Initial pause (0-1s) if (self.timer === 60) { // After 1s, show result text self.phase = 1; tween(resultText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, easing: tween.elasticOut, onFinish: function onFinish() { tween(resultText, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeOut }); } }); } break; case 1: // Victory text (1-3s) if (self.timer === 180) { // After 3s, show score self.phase = 2; tween(finalScoreText, { alpha: 1 }, { duration: 800, easing: tween.easeOut }); } break; case 2: // Score display (3-5s) if (self.timer === 240) { // After 4s, show motivational message tween(messageText, { alpha: 1 }, { duration: 800, easing: tween.easeOut }); } if (self.timer === 300) { // After 5s, show continue prompt self.phase = 3; tween(continueText, { alpha: 1 }, { duration: 500, easing: tween.easeOut }); // Pulse the continue text LK.setInterval(function () { if (continueText.alpha === 1) { tween(continueText, { alpha: 0.5 }, { duration: 800 }); } else { tween(continueText, { alpha: 1 }, { duration: 800 }); } }, 1600); } break; case 3: // Continue prompt (5s+) // Player can tap to continue if (self.timer > 360) { self.canExit = true; } break; } return self.finished; }; self.skip = function () { self.finished = true; }; return self; }); var Ship = Container.expand(function () { var self = Container.call(this); // Create a custom triangle ship var shipGraphics = new Container(); self.addChild(shipGraphics); // Create a pixel art triangle var triangleSize = 72; // Scale up to 1.5 times larger from 48 to 72 var pixelSize = 12; // Increase pixel size for more distinct lines var pixelGap = 1; // Gap between pixels for hollow effect // Create triangle points - pointing right by default (0 degrees = right) var points = [{ x: triangleSize, y: 0 }, // Front tip (pointing right) { x: -triangleSize / 2, y: -triangleSize / 2 }, // Top left { x: -triangleSize / 2, y: triangleSize / 2 } // Bottom left ]; // Draw the outline with pixel art style for (var i = 0; i < 3; i++) { var startPoint = points[i]; var endPoint = points[(i + 1) % 3]; // Calculate step count based on distance var dx = endPoint.x - startPoint.x; var dy = endPoint.y - startPoint.y; var steps = Math.max(Math.abs(dx), Math.abs(dy)) / pixelSize; // Draw pixels along the line for (var step = 0; step <= steps; step++) { var px = startPoint.x + dx * (step / steps); var py = startPoint.y + dy * (step / steps); var pixel = LK.getAsset('shipShape', { anchorX: 0.5, anchorY: 0.5, width: pixelSize - pixelGap, height: pixelSize - pixelGap, x: px, y: py, tint: 0xFF8000 // Orange color }); shipGraphics.addChild(pixel); } } shipGraphics.x = 0; shipGraphics.y = 0; // Ship properties self.rot = 0; // Start pointing right (0 radians) self.rotationSpeed = 0.05; // Reduced rotation speed for less sensitive steering self.isRotatingLeft = false; self.isRotatingRight = false; self.isFiring = false; self.fireDelay = 15; // frames between shots self.fireTimer = 0; self.invulnerable = false; self.invulnerableTime = 0; // Aura ability properties self.auraReady = false; self.auraCooldown = 0; self.auraCooldownMax = 900; // 15 seconds (60fps * 15) - reduced from 20s self.auraActive = false; self.auraActiveTime = 0; self.auraActiveTimeMax = 180; // 3 seconds (increased from 2s) // Physics-based movement properties - much simpler direct movement self.thrustPower = 0.20; // Increased thrust power for faster acceleration self.dragFactor = 0.97; // Slightly increased drag to slow down faster self.maxSpeed = 8; // Increased maximum speed self.velocity = { x: 0, y: 0 }; // Track previous position for movement calculations self.lastX = 0; self.lastY = 0; // Cache rotation values for optimization self.lastRot = self.rot; self.dirX = Math.cos(self.rot); self.dirY = Math.sin(self.rot); // Apply rotation to ship graphics shipGraphics.rotation = self.rot; self.update = function () { // Store last position self.lastX = self.x; self.lastY = self.y; // Handle rotation if (self.isRotatingLeft) { self.rot -= self.rotationSpeed; } if (self.isRotatingRight) { self.rot += self.rotationSpeed; } // Only update rotation visual when it changes to reduce unnecessary calculations if (self.lastRot !== self.rot) { shipGraphics.rotation = self.rot; self.lastRot = self.rot; // Pre-calculate direction vectors when rotation changes self.dirX = Math.cos(self.rot); self.dirY = Math.sin(self.rot); } // Use cached direction values // Apply thrust force in direction ship is facing (always moving) self.velocity.x += self.dirX * self.thrustPower; self.velocity.y += self.dirY * self.thrustPower; // Apply drag/friction self.velocity.x *= self.dragFactor; self.velocity.y *= self.dragFactor; // Calculate squared speed once for both checks var speedSquared = self.velocity.x * self.velocity.x + self.velocity.y * self.velocity.y; var maxSpeedSquared = self.maxSpeed * self.maxSpeed; // Only calculate actual speed when needed var speed = 0; if (speedSquared > maxSpeedSquared) { // Only calculate square root when we need to limit speed speed = Math.sqrt(speedSquared); // Apply speed limit var limitFactor = self.maxSpeed / speed; self.velocity.x *= limitFactor; self.velocity.y *= limitFactor; speed = self.maxSpeed; } else { // Approximate speed for particle effects without expensive sqrt speed = Math.abs(self.velocity.x) + Math.abs(self.velocity.y) * 0.5; } // Create thrust particles - optimize by creating fewer particles if (speed > 0.5 && LK.ticks % 6 == 0) { // Create particle every 6 ticks instead of 3 to reduce particle count var particle = new ThrustParticle(); // Position at the back of the ship (opposite of direction) var backX = self.x - self.dirX * triangleSize * 0.5; var backY = self.y - self.dirY * triangleSize * 0.5; // Add some randomness backX += (Math.random() - 0.5) * 10; backY += (Math.random() - 0.5) * 10; particle.x = backX; particle.y = backY; // Set velocity opposite to ship direction with some randomness particle.velocity.x = -self.dirX * (1 + Math.random()) + (Math.random() - 0.5) * 0.5; particle.velocity.y = -self.dirY * (1 + Math.random()) + (Math.random() - 0.5) * 0.5; // Add to game via event if (typeof game.addThrustParticle === 'function') { game.addThrustParticle(particle); } } // Update position based on velocity self.x += self.velocity.x; self.y += self.velocity.y; // Wrap around screen edges if (self.x < 0) { self.x = 2048; } if (self.x > 2048) { self.x = 0; } if (self.y < 0) { self.y = 2732; } if (self.y > 2732) { self.y = 0; } // Handle firing if (self.isFiring) { if (self.fireTimer <= 0) { self.fireTimer = self.fireDelay; return true; // Signal to create a bullet } } if (self.fireTimer > 0) { self.fireTimer--; } // Handle invulnerability if (self.invulnerable) { self.invulnerableTime--; shipGraphics.alpha = Math.sin(LK.ticks * 0.5) * 0.5 + 0.5; if (self.invulnerableTime <= 0) { self.invulnerable = false; shipGraphics.alpha = 1; } } // Handle Aura cooldown and activation if (!self.auraReady && !self.auraActive) { self.auraCooldown++; // Calculate remaining cooldown for display self.remainingCooldown = self.auraCooldownMax - self.auraCooldown; if (self.auraCooldown >= self.auraCooldownMax) { self.auraReady = true; self.auraCooldown = 0; self.remainingCooldown = 0; } } // Handle active Aura effects if (self.auraActive) { // Pulse the ship with a white glow when aura is active shipGraphics.tint = 0xFFFFFF; self.auraActiveTime++; if (self.auraActiveTime >= self.auraActiveTimeMax) { self.auraActive = false; self.auraActiveTime = 0; shipGraphics.tint = 0xFFFFFF; // Reset tint // Start cooldown again self.auraCooldown = 0; self.auraReady = false; } } else { // Reset tint when aura is not active shipGraphics.tint = 0xFF8000; } return false; }; self.makeInvulnerable = function (frames) { self.invulnerable = true; self.invulnerableTime = frames; }; // Activate aura ability self.activateAura = function () { // Only activate if aura is ready if (self.auraReady) { self.auraActive = true; self.auraActiveTime = 0; self.auraReady = false; // Create visual effect - ship glows white tween(shipGraphics, { tint: 0xFFFFFF }, { duration: 200 }); return true; } return false; }; return self; }); var ThrustParticle = Container.expand(function () { var self = Container.call(this); // Create particle visual - larger and more visible var particleGraphics = LK.getAsset('bulletShape', { anchorX: 0.5, anchorY: 0.5, width: 32, // Further increased size for better visibility // Increased size height: 32, // Further increased size for better visibility // Increased size tint: 0xFFAA00 // Brighter orange-yellow for better visibility }); particleGraphics.alpha = 1.0; // Fully opaque self.addChild(particleGraphics); // Particle properties self.velocity = { x: 0, y: 0 }; self.lifespan = 35; // Increased lifespan for better visibility self.age = 0; self.update = function () { // Move according to velocity self.x += self.velocity.x; self.y += self.velocity.y; // Age the particle self.age++; // Fade out as it ages, with a faster tail-end fade var lifeRatio = self.age / self.lifespan; particleGraphics.alpha = 0.9 * (1 - lifeRatio * lifeRatio); // Return true if particle should be removed return self.age >= self.lifespan; }; return self; }); var WireframeBall = Container.expand(function () { var self = Container.call(this); self.edges = []; self.joints = []; self.ballRadius = 80; self.rotationX = 0; self.rotationY = 0; self.rotationZ = 0; self.rotationSpeedX = 0.02; self.rotationSpeedY = 0.025; self.rotationSpeedZ = 0.015; // Create cube vertices mapped to sphere surface var cubeVertices = [[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1], // back face [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1] // front face ]; // Normalize vertices to sphere surface for (var i = 0; i < cubeVertices.length; i++) { var vertex = cubeVertices[i]; var length = Math.sqrt(vertex[0] * vertex[0] + vertex[1] * vertex[1] + vertex[2] * vertex[2]); vertex[0] = vertex[0] / length * self.ballRadius; vertex[1] = vertex[1] / length * self.ballRadius; vertex[2] = vertex[2] / length * self.ballRadius; } // Create cube edges (12 edges total) var cubeEdges = [[0, 1], [1, 2], [2, 3], [3, 0], // back face edges [4, 5], [5, 6], [6, 7], [7, 4], // front face edges [0, 4], [1, 5], [2, 6], [3, 7] // connecting edges ]; // Create wireframe edges for (var i = 0; i < cubeEdges.length; i++) { var edge = cubeEdges[i]; var vertex1 = cubeVertices[edge[0]]; var vertex2 = cubeVertices[edge[1]]; var edgeElement = self.attachAsset('wireframeEdge', { anchorX: 0.5, anchorY: 0.5 }); // Calculate edge center and rotation var centerX = (vertex1[0] + vertex2[0]) / 2; var centerY = (vertex1[1] + vertex2[1]) / 2; var centerZ = (vertex1[2] + vertex2[2]) / 2; var dx = vertex2[0] - vertex1[0]; var dy = vertex2[1] - vertex1[1]; var edgeLength = Math.sqrt(dx * dx + dy * dy); var rotation = Math.atan2(dy, dx); edgeElement.x = centerX; edgeElement.y = centerY; edgeElement.z = centerZ || 0; edgeElement.rotation = rotation; edgeElement.scaleY = edgeLength / 120; // Scale to fit edge length edgeElement.alpha = 0.8; edgeElement.originalX = centerX; edgeElement.originalY = centerY; edgeElement.originalZ = centerZ || 0; edgeElement.originalRotation = rotation; edgeElement.originalScaleY = edgeElement.scaleY; self.edges.push(edgeElement); } // Create joints at vertices for (var i = 0; i < cubeVertices.length; i++) { var vertex = cubeVertices[i]; var joint = self.attachAsset('wireframeJoint', { anchorX: 0.5, anchorY: 0.5 }); joint.x = vertex[0]; joint.y = vertex[1]; joint.z = vertex[2] || 0; joint.originalX = vertex[0]; joint.originalY = vertex[1]; joint.originalZ = vertex[2] || 0; joint.alpha = 0; joint.pulsePhase = Math.random() * Math.PI * 2; self.joints.push(joint); } self.project3D = function (x, y, z) { // Simple 3D to 2D projection with rotation var cosX = Math.cos(self.rotationX); var sinX = Math.sin(self.rotationX); var cosY = Math.cos(self.rotationY); var sinY = Math.sin(self.rotationY); var cosZ = Math.cos(self.rotationZ); var sinZ = Math.sin(self.rotationZ); // Rotate around X axis var y1 = y * cosX - z * sinX; var z1 = y * sinX + z * cosX; // Rotate around Y axis var x2 = x * cosY + z1 * sinY; var z2 = -x * sinY + z1 * cosY; // Rotate around Z axis var x3 = x2 * cosZ - y1 * sinZ; var y3 = x2 * sinZ + y1 * cosZ; return { x: x3, y: y3, z: z2 }; }; self.update = function () { // Update rotation self.rotationX += self.rotationSpeedX; self.rotationY += self.rotationSpeedY; self.rotationZ += self.rotationSpeedZ; // Update edge positions with 3D rotation for (var i = 0; i < self.edges.length; i++) { var edge = self.edges[i]; var projected = self.project3D(edge.originalX, edge.originalY, edge.originalZ); edge.x = projected.x; edge.y = projected.y; // Depth-based alpha and scale var depth = (projected.z + self.ballRadius) / (2 * self.ballRadius); edge.alpha = 0.4 + depth * 0.6; edge.scaleX = 0.6 + depth * 0.4; // Maintain original scale for length edge.scaleY = edge.originalScaleY * (0.8 + depth * 0.2); } // Update joints with pulsing effect for (var i = 0; i < self.joints.length; i++) { var joint = self.joints[i]; var projected = self.project3D(joint.originalX, joint.originalY, joint.originalZ); joint.x = projected.x; joint.y = projected.y; var depth = (projected.z + self.ballRadius) / (2 * self.ballRadius); joint.pulsePhase += 0.08; joint.alpha = Math.max(0, depth * 0.9 * Math.sin(joint.pulsePhase)); joint.scaleX = joint.scaleY = 0.7 + depth * 0.3 + Math.sin(joint.pulsePhase) * 0.15; } }; return self; }); /**** * Initialize Game ****/ // Function to add thrust particles - called from Ship update var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Placeholder ID, engine will assign // Placeholder ID, engine will assign // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Assuming ID is correct // Replace with actual ID // Replace with actual ID // Replace with actual ID // Music Tracks - assuming unique IDs exist for each // Replace with actual ID // Replace with actual ID // Replace with actual ID // Replace with actual ID // Replace with actual ID // Replace with actual ID // Replace with actual ID // Replace with actual ID // Replace with actual ID // Replace with actual ID // Meme explosion sounds - assuming unique IDs exist for each // Placeholder: Replace with actual music ID // Placeholder: Replace with actual music ID // Placeholder: Replace with actual music ID // Initialize music tracks // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Placeholder: Replace with actual sound ID or properties // Initialize meme explosion sounds // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Placeholder: Replace with actual sound ID // Initialize music tracks // Initialize meme explosion sounds // Add music assets // Add music assets // Sounds for each meme type // Game variables // Add sound assets for each meme asteroid (meme0 to meme9) // Sound for tapping to start // Sound for starting a new level // Sound for the rhythmic beat // Placeholder ID - Engine will handle // Placeholder ID - Engine will handle // Engine will assign actual ID // Engine will assign actual ID // Added new meme image asset // Added new meme explosion sound asset // Object pools for performance optimization var bulletPool = []; var asteroidPool = []; var thrustParticlePool = []; var auraParticlePool = []; var creeperAsteroidPool = []; var ship; var bullets = []; var asteroids = []; var thrustParticles = []; // Array to store thrust particles var score = 0; var lives = 3; var level = 1; var gameStarted = false; var gameOver = false; var introPlayed = false; var introSequence = null; var outroSequence = null; var outroPlayed = false; var highScore = 0; // This will be loaded from storage var highScoreTxt; // UI element for displaying the high score var newRecordShown = false; // Track if new record notification has been shown var nextExtraLifeScore = 20000; // Score threshold for the next extra life var extraLifeInterval = 20000; // Get an extra life every 20,000 points var maxLives = 5; // Maximum number of lives a player can have var triangleSize = 36; // Ship triangle size for bullet positioning (half of the size now) // Creeper Zone variables var inCreeperZone = false; var creeperIntroSequence = null; var creeperWaveCount = 0; var creeperMaxWaves = 3; // Number of waves to complete the Creeper Zone var glaudIntroSequence = null; var glaudIntroPlayed = false; // Function to play intro sequence function playIntroSequence() { // Clear any existing game objects bullets = []; asteroids = []; thrustParticles = []; // Check if GLAUD intro has been played if (!glaudIntroPlayed) { // Start with GLAUD intro sequence glaudIntroSequence = new GlaudIntroSequence(); game.addChild(glaudIntroSequence); // Setup handler for when GLAUD intro finishes LK.setInterval(function () { if (glaudIntroSequence && glaudIntroSequence.finished) { game.removeChild(glaudIntroSequence); glaudIntroSequence = null; glaudIntroPlayed = true; // After GLAUD intro, continue with main intro continueWithMainIntro(); LK.clearInterval(this); } }, 100); } else { // Skip GLAUD intro and go directly to main intro continueWithMainIntro(); } } function continueWithMainIntro() { // Play gameMusic3 for intro LK.playMusic('gameMusic3', { loop: true, fade: { start: 0, end: 1, duration: 1000 } }); introMusicPlayed = true; // Create and add intro sequence introSequence = new IntroSequence(); game.addChild(introSequence); // Setup a handler for when intro is finished LK.setInterval(function () { if (introSequence && introSequence.finished) { game.removeChild(introSequence); introSequence = null; introPlayed = true; // Keep gameMusic3 playing - don't stop it // Start game immediately initGame(); LK.clearInterval(this); } }, 100); } // UI elements var leftButton; var rightButton; var fireButton; var scoreTxt; var livesTxt; var levelTxt; var startText; // Initialize the game function initGame() { // Reset the new record notification flag newRecordShown = false; // Reset extra life score tracking nextExtraLifeScore = 20000; // Initialize object pools initObjectPools(); // Check if we need to play intro if (!gameStarted && !introPlayed) { playIntroSequence(); return; } // Create ship ship = new Ship(); ship.x = 2048 / 2; ship.y = 200; // Position at top middle ship.makeInvulnerable(120); // 2 seconds // Add visual effect to indicate ship is ready for movement tween(ship, { alpha: 0.5 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(ship, { alpha: 1 }, { duration: 500, easing: tween.easeInOut }); } }); game.addChild(ship); // Create control buttons leftButton = new Button('rotateLeftButton'); leftButton.x = 300; // Position on the left side leftButton.y = 2732 - 200; // Position at the bottom leftButton.scale.set(2, 2); // Make button bigger game.addChild(leftButton); // Fire button in the middle fireButton = new Button('fireButton'); fireButton.x = 2048 / 2; // Position in middle fireButton.y = 2732 - 200; // Position at the bottom fireButton.scale.set(2, 2); // Make button bigger game.addChild(fireButton); // No forward button needed - ship will constantly move forward // Right rotation button rightButton = new Button('rotateRightButton'); rightButton.x = 2048 - 300; // Position on the right side rightButton.y = 2732 - 200; // Position at the bottom rightButton.scale.set(2, 2); // Make button bigger game.addChild(rightButton); // Create UI text // Create Glaud text in orange var glaudText = new Text2('Glaud', { size: 60, fill: 0xFF8000 // Orange color }); glaudText.anchor.set(1, 0); // Right-align text glaudText.x = -20; // Add padding from right edge glaudText.y = 0; // Position at very top LK.gui.topRight.addChild(glaudText); scoreTxt = new Text2('SCORE: 0', { size: 40, fill: 0xFFFFFF }); scoreTxt.anchor.set(1, 0); // Right-align text scoreTxt.x = -20; // Add padding from right edge scoreTxt.y = 80; // Position further below the Glaud text LK.gui.topRight.addChild(scoreTxt); // Initialize high score from storage, defaulting to 0 if not found highScore = storage.highScore || 0; highScoreTxt = new Text2('BEST: ' + highScore, { size: 40, fill: 0xFFD700 // A nice gold color for the high score! }); highScoreTxt.anchor.set(1, 0); // Right-align text highScoreTxt.x = -20; // Add padding from right edge, same as scoreTxt highScoreTxt.y = 140; // Position below scoreTxt LK.gui.topRight.addChild(highScoreTxt); // Create a container for lives display var livesContainer = new Container(); livesContainer.x = 70; livesContainer.y = 50; LK.gui.top.addChild(livesContainer); // Add ship icons for lives instead of text var lifeIcons = []; for (var i = 0; i < 3; i++) { var lifeIcon = LK.getAsset('shipAsset', { anchorX: 0.5, anchorY: 0.5, width: 40, height: 40 }); lifeIcon.x = i * 50; // Space them horizontally livesContainer.addChild(lifeIcon); lifeIcons.push(lifeIcon); } // Store the icons and container for later access livesTxt = { icons: lifeIcons, container: livesContainer, update: function update(livesCount) { for (var i = 0; i < this.icons.length; i++) { this.icons[i].visible = i < livesCount; } } }; levelTxt = new Text2('LEVEL: 1', { size: 40, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); // Left-align text levelTxt.x = 180; // Move further right from top-left menu icon but slightly to the left levelTxt.y = 80; // Move lower down from top edge LK.gui.topLeft.addChild(levelTxt); // Add developer feature - double-tap on level to advance var lastLevelTapTime = 0; var devModeActive = false; levelTxt.interactive = true; levelTxt.down = function (x, y, obj) { var currentTime = Date.now(); // Check for double tap (within 300ms) if (currentTime - lastLevelTapTime < 300) { // Toggle dev mode on double tap devModeActive = !devModeActive; // Visual feedback that dev mode is active if (devModeActive) { // Initialize style property if it doesn't exist if (!levelTxt.style) { levelTxt.style = {}; } levelTxt.style.fill = 0xFF8000; // Orange to indicate dev mode // Show dev mode activated message var devModeText = new Text2('DEV MODE ACTIVE!', { size: 40, fill: 0xFF8000 }); devModeText.anchor.set(0.5, 0.5); devModeText.x = 2048 / 2; devModeText.y = 2732 / 2; game.addChild(devModeText); // Fade out after 2 seconds tween(devModeText, { alpha: 0 }, { duration: 1000, delay: 1000, onFinish: function onFinish() { game.removeChild(devModeText); } }); } else { levelTxt.style.fill = 0xFFFFFF; // Reset to white when disabled } } else if (devModeActive) { // In dev mode, single tap advances the level // Clear existing asteroids for (var i = asteroids.length - 1; i >= 0; i--) { game.removeChild(asteroids[i]); } asteroids = []; // Advance to next level level++; levelTxt.setText('LEVEL: ' + level); LK.getSound('levelStart').play(); // Create new asteroids for the next level createAsteroidsForLevel(level); } lastLevelTapTime = currentTime; }; // Start screen text if (!gameStarted) { // Create gray background for start text var startBackground = new Container(); var bgShape = LK.getAsset('bulletShape', { anchorX: 0.5, anchorY: 0.5, width: 1000, height: 250, tint: 0x333333 }); bgShape.alpha = 0.9; startBackground.addChild(bgShape); startText = new Text2('TAP TO START', { size: 140, // Slightly smaller size fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startBackground.addChild(startText); LK.gui.center.addChild(startBackground); } // Clear any existing game objects bullets = []; asteroids = []; thrustParticles = []; // Initialize asteroids for the first level createAsteroidsForLevel(level); } function createAsteroidsForLevel(level) { // Number of asteroids based on level var numAsteroids = Math.min(4 + level, 12); for (var i = 0; i < numAsteroids; i++) { // Create a random meme type (0-13) var memeType = Math.floor(Math.random() * 14); // Now 0-13 for 14 memes // Get asteroid from pool or create new one if pool is empty var asteroid = getFromPool(asteroidPool); if (!asteroid || asteroid.size !== 2) { // Try to find a size 2 asteroid specifically for (var j = 0; j < asteroidPool.length; j++) { if (!asteroidPool[j].active && asteroidPool[j].size === 2) { asteroid = asteroidPool[j]; break; } } // If no size 2 asteroid found, create a new one if (!asteroid || asteroid.size !== 2) { asteroid = new Asteroid(2, memeType); game.addChild(asteroid); asteroidPool.push(asteroid); } else { // Reset properties of pooled asteroid asteroid.memeType = memeType; } } else { // Reset properties of pooled asteroid asteroid.memeType = memeType; } // Reset asteroid state asteroid.active = true; asteroid.visible = true; asteroid.justWrapped = false; // Re-initialize velocity var speed = (3 - asteroid.size) * 0.8 + 0.5; var angle = Math.random() * Math.PI * 2; asteroid.velocity = { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed }; asteroid.rotationSpeed = (Math.random() - 0.5) * 0.05; // Apply special behavior based on meme type if (memeType === 7) { // "This is Fine" meme - moves slower asteroid.velocity.x *= 0.6; asteroid.velocity.y *= 0.6; } else if (memeType === 9) { // "Stonks" meme - worth more points asteroid.getPoints = function () { return (3 - this.size) * 150; // 50% more points }; } else if (memeType === 12) { // Fast meme asteroid.velocity.x *= 1.5; asteroid.velocity.y *= 1.5; } // Add difficulty scaling based on level if (level > 3) { // Scale asteroid speed with level var speedMultiplier = 1 + (level - 3) * 0.1; // 10% faster per level after 3 speedMultiplier = Math.min(speedMultiplier, 1.5); // Cap at 50% faster asteroid.velocity.x *= speedMultiplier; asteroid.velocity.y *= speedMultiplier; } // Position the asteroid away from the player do { asteroid.x = Math.random() * 2048; asteroid.y = Math.random() * 2732; } while (distance(asteroid.x, asteroid.y, ship.x, ship.y) < 300); asteroids.push(asteroid); } } function distance(x1, y1, x2, y2) { return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); } function createBullet() { // Get bullet from pool or create new one if pool is empty var bullet = getFromPool(bulletPool); if (!bullet) { bullet = new Bullet(); game.addChild(bullet); bulletPool.push(bullet); } // Reset bullet properties bullet.age = 0; bullet.active = true; bullet.visible = true; // Use ship's rotation directly var angle = ship.rot; // Calculate the position at the tip of the ship based on the triangle geometry // For the redesigned ship (pointing up at 0 rotation), the tip is -triangleSize units in Y var offsetX = Math.cos(angle) * triangleSize; var offsetY = Math.sin(angle) * triangleSize; // Position bullet at the tip of the ship bullet.x = ship.x + offsetX; bullet.y = ship.y + offsetY; // Set bullet velocity to match ship's exact facing direction bullet.velocity.x = Math.cos(angle) * bullet.speed; bullet.velocity.y = Math.sin(angle) * bullet.speed; // Add bullet to active bullets array bullets.push(bullet); // Play shoot sound LK.getSound('shoot').play(); } function updateAsteroids() { for (var i = asteroids.length - 1; i >= 0; i--) { var asteroid = asteroids[i]; asteroid.update(); // Check for collision with the ship if (!ship.invulnerable && asteroid.intersects(ship)) { // Player loses a life lives--; livesTxt.update(lives); // Play explosion sound LK.getSound('explosion').play(); // Flash screen LK.effects.flashScreen(0xFF0000, 500); // Make ship invulnerable for a few seconds ship.makeInvulnerable(180); // Game over if no lives left if (lives <= 0) { // Check if the current score is a new high score if (score > highScore) { highScore = score; // Update our game's high score variable storage.highScore = highScore; // Persist the new high score // The highScoreTxt will update automatically on game restart via initGame // Display a motivational message about the new high score var newHighScoreText = new Text2('NEW HIGH SCORE! AMAZING WORK!', { size: 60, fill: 0xFFD700 // Gold color }); newHighScoreText.anchor.set(0.5, 0.5); newHighScoreText.x = 2048 / 2; newHighScoreText.y = 2732 / 2 - 150; LK.gui.center.addChild(newHighScoreText); // Animate the text tween(newHighScoreText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { tween(newHighScoreText, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeIn }); } }); // Remove the text after 3 seconds LK.setTimeout(function () { LK.gui.center.removeChild(newHighScoreText); }, 3000); } else { // Display a motivational message for not beating the high score var motivationalText = new Text2('YOU\'LL GET IT NEXT TIME!', { size: 60, fill: 0x3399FF // Blue color }); motivationalText.anchor.set(0.5, 0.5); motivationalText.x = 2048 / 2; motivationalText.y = 2732 / 2 - 150; LK.gui.center.addChild(motivationalText); // Animate the text tween(motivationalText, { alpha: 0.7 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(motivationalText, { alpha: 1 }, { duration: 800, easing: tween.easeInOut }); } }); // Remove the text after 3 seconds LK.setTimeout(function () { LK.gui.center.removeChild(motivationalText); }, 3000); } LK.setScore(score); // Set the score for the platform's game over UI // Start the outro sequence instead of showing game over immediately playOutroSequence(); gameOver = true; return; } } } } function updateBullets() { for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; bullet.update(); // Remove bullets that have lived too long if (bullet.age > bullet.lifespan) { // Return bullet to pool instead of removing bullet.active = false; bullet.visible = false; bullets.splice(i, 1); continue; } // Check for collisions with asteroids var hitAsteroid = false; for (var j = asteroids.length - 1; j >= 0; j--) { var asteroid = asteroids[j]; // Skip intersection check if asteroid just wrapped (reduces false positives) if (asteroid.justWrapped) { continue; } if (bullet.intersects(asteroid)) { hitAsteroid = true; // Add score based on asteroid size score += asteroid.getPoints(); scoreTxt.setText('SCORE: ' + score); // Check if player earned an extra life if (score >= nextExtraLifeScore) { // Set next threshold regardless of whether we can get a life nextExtraLifeScore += extraLifeInterval; // Only award an extra life if below maximum if (lives < maxLives) { // Award an extra life lives++; // Update lives display livesTxt.update(lives); // Show extra life notification var extraLifeText = new Text2('EXTRA LIFE!', { size: 80, fill: 0x00FF00 // Bright green }); extraLifeText.anchor.set(0.5, 0.5); extraLifeText.x = 2048 / 2; extraLifeText.y = 2732 / 2 - 200; extraLifeText.alpha = 0; game.addChild(extraLifeText); // Animate the notification tween(extraLifeText, { alpha: 1, scaleX: 1.3, scaleY: 1.3 }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { // Wait 2 seconds before fading out LK.setTimeout(function () { tween(extraLifeText, { alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(extraLifeText); } }); }, 2000); } }); // Play a positive sound for extra life LK.getSound('levelStart').play(); } } // Check if player just beat the high score and notification hasn't been shown yet if (score > highScore && score - asteroid.getPoints() <= highScore && !newRecordShown) { // Set flag to true so we don't show it again this game newRecordShown = true; // Create a temporary notification var newRecordText = new Text2('NEW RECORD!', { size: 80, fill: 0xFFD700 // Gold color }); newRecordText.anchor.set(0.5, 0.5); newRecordText.x = 2048 / 2; newRecordText.y = 2732 / 2; newRecordText.alpha = 0; game.addChild(newRecordText); // Animate the notification tween(newRecordText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { // Wait 5 seconds before fading out LK.setTimeout(function () { tween(newRecordText, { alpha: 0 }, { duration: 1500, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(newRecordText); } }); }, 5000); } }); // Update high score highScore = score; highScoreTxt.setText('BEST: ' + highScore); } // Special handling for Creeper asteroids in Creeper Zone if (inCreeperZone && asteroid.isExplosive) { // Call the explode method asteroid.explode(); // Check if ship is within explosion radius var dist = distance(asteroid.x, asteroid.y, ship.x, ship.y); if (dist <= asteroid.explosionRadius && !ship.invulnerable) { // Player loses a life if within explosion radius lives--; livesTxt.update(lives); // Flash screen green LK.effects.flashScreen(0x88FF88, 300); // Make ship invulnerable ship.makeInvulnerable(180); // Game over if no lives left if (lives <= 0) { if (score > highScore) { highScore = score; storage.highScore = highScore; } LK.setScore(score); playOutroSequence(); gameOver = true; return; } } // Remove the asteroid game.removeChild(asteroid); asteroids.splice(j, 1); break; } else { // Get the asteroid's meme type, defaulting to 0 if undefined var memeType = asteroid.memeType !== undefined ? asteroid.memeType : 0; // Construct the specific meme explosion sound ID var memeExplosionSoundId = 'meme' + memeType + 'Explosion'; // Try to get the specific meme explosion sound var explosionSound = LK.getSound(memeExplosionSoundId); // Play the specific meme explosion sound or fallback to generic explosion if (explosionSound) { explosionSound.play(); } else { // Fallback to generic explosion sound console.log('Fallback explosion sound for memeType:', memeType); LK.getSound('explosion').play(); } // Break large asteroids (size 2) into smaller pieces if (asteroid.size === 2) { for (var k = 0; k < 2; k++) { // Get medium asteroid from pool or create new one var newAsteroid = null; // Try to find a size 1 asteroid in the pool for (var p = 0; p < asteroidPool.length; p++) { if (!asteroidPool[p].active && asteroidPool[p].size === 1) { newAsteroid = asteroidPool[p]; break; } } // If no pooled asteroid found, create a new one if (!newAsteroid) { newAsteroid = new Asteroid(1, asteroid.memeType); game.addChild(newAsteroid); asteroidPool.push(newAsteroid); } else { // Reset pooled asteroid properties newAsteroid.memeType = asteroid.memeType; // Reset velocity var speed = 0.8 * 2 + 0.5; // Speed for medium asteroid var angle = Math.random() * Math.PI * 2; newAsteroid.velocity = { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed }; newAsteroid.rotationSpeed = (Math.random() - 0.5) * 0.05; } newAsteroid.active = true; newAsteroid.visible = true; newAsteroid.justWrapped = false; newAsteroid.x = asteroid.x; newAsteroid.y = asteroid.y; asteroids.push(newAsteroid); } } // Return hit asteroid to pool instead of removing asteroid.active = false; asteroid.visible = false; asteroids.splice(j, 1); break; } } } if (hitAsteroid) { // Return bullet to pool instead of removing bullet.active = false; bullet.visible = false; bullets.splice(i, 1); } } // Check if all asteroids are destroyed if (asteroids.length === 0) { // Special handling for Creeper Zone if (inCreeperZone) { creeperWaveCount++; // Check if we've completed all Creeper Zone waves if (creeperWaveCount >= creeperMaxWaves) { // End Creeper Zone and continue to next level inCreeperZone = false; // Show "Area Clear" message var areaClearText = new Text2('AREA CLEAR!', { size: 80, fill: 0x88FF88 // Green text }); areaClearText.anchor.set(0.5, 0.5); areaClearText.x = 2048 / 2; areaClearText.y = 2732 / 2; areaClearText.alpha = 0; game.addChild(areaClearText); // Animate the message tween(areaClearText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { LK.setTimeout(function () { tween(areaClearText, { alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(areaClearText); } }); }, 2000); } }); // Return to normal music LK.playMusic('gameMusic3', { loop: true, fade: { start: 0, end: 1, duration: 1000 } }); // Move to level 10 level = 10; levelTxt.setText('LEVEL: ' + level); // Play level start sound LK.getSound('levelStart').play(); // Create new asteroids for the next level createAsteroidsForLevel(level); } else { // Create next wave of creeper asteroids var wavesLeft = creeperMaxWaves - creeperWaveCount; // Display wave counter var waveText = new Text2('WAVE ' + creeperWaveCount + ' COMPLETE! ' + wavesLeft + ' TO GO!', { size: 60, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0.5); waveText.x = 2048 / 2; waveText.y = 2732 / 2; game.addChild(waveText); // Fade out after 2 seconds tween(waveText, { alpha: 0 }, { duration: 1000, delay: 2000, onFinish: function onFinish() { game.removeChild(waveText); } }); // Create next wave - increase count with each wave createCreeperAsteroids(5 + creeperWaveCount); } } else if (level === 9) { // Start Creeper Zone after level 9 startCreeperZone(); } else { // Normal level progression // Next level level++; levelTxt.setText('LEVEL: ' + level); // Play level start sound LK.getSound('levelStart').play(); // Ensure gameMusic3 keeps playing - no music track changes if (!musicPlaying) { // If music isn't playing for some reason, restart gameMusic3 LK.playMusic('gameMusic3', { loop: true, fade: { start: 0, end: 1, duration: 1000 } }); musicPlaying = true; } // Create new asteroids for the next level createAsteroidsForLevel(level); } } } // Main game update function game.update = function () { // Update GLAUD intro sequence if active if (glaudIntroSequence) { glaudIntroSequence.update(); return; } // Update intro sequence if active if (introSequence) { introSequence.update(); return; } // Update Creeper Zone intro if active if (creeperIntroSequence) { creeperIntroSequence.update(); return; } // Update outro sequence if active if (outroSequence) { outroSequence.update(); return; } if (!gameStarted) { return; } // Update ship controls based on button state ship.isRotatingLeft = leftButton.isPressed; ship.isRotatingRight = rightButton.isPressed; ship.isFiring = fireButton.isPressed; // Update aura ready text on fire button with cooldown info fireButton.updateAuraText(ship.auraReady, ship.remainingCooldown); // Ship constantly moves forward, no need for isMoving toggle // Update ship if (ship.update()) { if (ship.auraReady) { // If aura is ready and firing, activate aura and create bullet if (ship.activateAura()) { // Destroy nearby asteroids when aura is activated destroyNearbyAsteroids(); // Visual effect for aura activation createAuraEffectParticles(); // Sound effect for aura activation LK.getSound('shoot').play(); } } createBullet(); } // Update bullets and check for collisions updateBullets(); // Update asteroids and check for collisions updateAsteroids(); // Update thrust particles updateThrustParticles(); }; // Game music and sound state variables var currentMusicTrack = 0; // Initialize to 0, will be set to 1 on game start var musicPlaying = false; // Track if music is currently playing var introMusicPlayed = false; // Track if intro music has played // Event handlers game.down = function (x, y, obj) { // Check if GLAUD intro is playing if (glaudIntroSequence && !glaudIntroSequence.finished) { glaudIntroSequence.finished = true; return; } // Check if intro is playing if (introSequence && !introSequence.finished) { introSequence.skip(); return; } // Check if Creeper Zone intro is playing - but don't allow skipping if (creeperIntroSequence && !creeperIntroSequence.finished) { // Don't allow skipping the creeper intro return; } // Check if outro is playing and can be exited if (outroSequence) { if (outroSequence.canExit) { outroSequence.skip(); } return; } if (!gameStarted) { gameStarted = true; LK.getSound('gameStart').play(); // Play start sound if (startText && startText.parent) { startText.parent.parent.removeChild(startText.parent); startText = null; } // Only play gameMusic3 LK.playMusic('gameMusic3', { loop: true, fade: { start: 0, end: 1, duration: 800 } }); // Set current track to match currentMusicTrack = 3; musicPlaying = true; } // Ensure buttons can detect touch events when pressed var local; var touchConsumed = false; // Check if we tapped on level text for dev mode if (levelTxt && levelTxt.getBounds) { var levelBounds = levelTxt.getBounds(); // Check if tap is within level text bounds if (x >= levelBounds.x && x <= levelBounds.x + levelBounds.width && y >= levelBounds.y && y <= levelBounds.y + levelBounds.height) { // Let the level text handle the tap through its own down method levelTxt.down(x, y, obj); touchConsumed = true; } } // Check left button if (!touchConsumed) { local = leftButton.toLocal({ x: x, y: y }); if (local.x >= -leftButton.width / 2 && local.x <= leftButton.width / 2 && local.y >= -leftButton.height / 2 && local.y <= leftButton.height / 2) { leftButton.down(local.x, local.y, {}); touchConsumed = true; } } // Check fire button only if touch not already consumed if (!touchConsumed) { local = fireButton.toLocal({ x: x, y: y }); if (local.x >= -fireButton.width / 2 && local.x <= fireButton.width / 2 && local.y >= -fireButton.height / 2 && local.y <= fireButton.height / 2) { fireButton.down(local.x, local.y, {}); touchConsumed = true; } } // Check right button only if touch not already consumed if (!touchConsumed) { local = rightButton.toLocal({ x: x, y: y }); if (local.x >= -rightButton.width / 2 && local.x <= rightButton.width / 2 && local.y >= -rightButton.height / 2 && local.y <= rightButton.height / 2) { rightButton.down(local.x, local.y, {}); touchConsumed = true; } } // Only fire a bullet if no button was pressed and game is active if (!touchConsumed && gameStarted && ship && !gameOver) { createBullet(); } }; // Add up handler to handle releasing buttons game.up = function (x, y, obj) { // Reset all button states when touch/click is released // Safely call button handlers by checking if they exist first if (leftButton && typeof leftButton.up === 'function') { leftButton.up(0, 0, {}); } if (fireButton && typeof fireButton.up === 'function') { fireButton.up(0, 0, {}); } if (rightButton && typeof rightButton.up === 'function') { rightButton.up(0, 0, {}); } }; // Initialize object pools with pre-populated objects function initObjectPools() { // Clear existing pools bulletPool = []; asteroidPool = []; thrustParticlePool = []; auraParticlePool = []; creeperAsteroidPool = []; // Pre-populate bullet pool (30 bullets should be enough for most gameplay) for (var i = 0; i < 30; i++) { var bullet = new Bullet(); bullet.visible = false; bullet.active = false; bulletPool.push(bullet); game.addChild(bullet); } // Pre-populate asteroid pool (30 for all sizes) for (var i = 0; i < 10; i++) { // Large asteroids (size 2) var asteroid = new Asteroid(2); asteroid.visible = false; asteroid.active = false; asteroidPool.push(asteroid); game.addChild(asteroid); // Medium asteroids (size 1) asteroid = new Asteroid(1); asteroid.visible = false; asteroid.active = false; asteroidPool.push(asteroid); game.addChild(asteroid); // Small asteroids (size 0) asteroid = new Asteroid(0); asteroid.visible = false; asteroid.active = false; asteroidPool.push(asteroid); game.addChild(asteroid); } // Pre-populate thrust particle pool (50 particles) for (var i = 0; i < 50; i++) { var particle = new ThrustParticle(); particle.visible = false; particle.active = false; thrustParticlePool.push(particle); game.addChild(particle); } // Pre-populate aura particle pool (24 particles) for (var i = 0; i < 24; i++) { var auraParticle = new AuraParticle(); auraParticle.visible = false; auraParticle.active = false; auraParticlePool.push(auraParticle); game.addChild(auraParticle); } // Pre-populate creeper asteroid pool (15 creepers) for (var i = 0; i < 15; i++) { var creeper = new CreeperAsteroid(); creeper.visible = false; creeper.active = false; creeperAsteroidPool.push(creeper); game.addChild(creeper); } } // Initialize the game when this script runs initGame(); // Function to add thrust particles - called from Ship update game.addThrustParticle = function (particle) { // If particle is from pool, just activate it if (particle.active === false) { particle.active = true; particle.visible = true; thrustParticles.push(particle); // Don't need to add to game since it's already a child } else { // If it's a new particle, try to get one from the pool first var pooledParticle = getFromPool(thrustParticlePool); if (pooledParticle) { // Reset particle properties pooledParticle.x = particle.x; pooledParticle.y = particle.y; pooledParticle.velocity.x = particle.velocity.x; pooledParticle.velocity.y = particle.velocity.y; pooledParticle.age = 0; pooledParticle.active = true; pooledParticle.visible = true; thrustParticles.push(pooledParticle); } else { // If pool is empty, use the provided particle thrustParticles.push(particle); game.addChild(particle); } } }; // Helper function to get object from pool function getFromPool(pool) { for (var i = 0; i < pool.length; i++) { if (!pool[i].active) { return pool[i]; } } return null; // Pool is empty } // Function to update thrust particles function updateThrustParticles() { for (var i = thrustParticles.length - 1; i >= 0; i--) { var particle = thrustParticles[i]; // If particle update returns true, it means the particle should be removed if (particle.update()) { // Return particle to pool instead of removing particle.active = false; particle.visible = false; thrustParticles.splice(i, 1); } } } // Function to destroy asteroids near the ship when aura is activated function destroyNearbyAsteroids() { var auraRadius = 350; // Slightly reduced radius for better balance // Create a visual ring effect for the aura var auraRing = new Container(); var auraRingGraphic = LK.getAsset('forwardButton', { anchorX: 0.5, anchorY: 0.5, width: auraRadius * 2, height: auraRadius * 2, tint: 0x40E0D0 // Turquoise color to match AuraParticle }); auraRingGraphic.alpha = 0.7; auraRing.addChild(auraRingGraphic); auraRing.x = ship.x; auraRing.y = ship.y; game.addChild(auraRing); // Animate the aura ring expanding then fading tween(auraRingGraphic, { alpha: 0, width: auraRadius * 2.5, height: auraRadius * 2.5 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(auraRing); } }); // Flash the ship with turquoise tween(ship, { alpha: 0.8 }, { duration: 200, easing: tween.easeInOut, onFinish: function onFinish() { tween(ship, { alpha: 1 }, { duration: 200, easing: tween.easeInOut }); } }); // Check all asteroids and destroy ones within the aura radius for (var i = asteroids.length - 1; i >= 0; i--) { var asteroid = asteroids[i]; var dist = distance(ship.x, ship.y, asteroid.x, asteroid.y); if (dist <= auraRadius) { // Different behavior based on asteroid size if (asteroid.size === 2) { // Large asteroids: Break into medium asteroids and score points score += asteroid.getPoints(); scoreTxt.setText('SCORE: ' + score); // Play the specific meme explosion sound var memeType = asteroid.memeType !== undefined ? asteroid.memeType : 0; var memeExplosionSoundId = 'meme' + memeType + 'Explosion'; var explosionSound = LK.getSound(memeExplosionSoundId); if (explosionSound) { explosionSound.play(); } else { LK.getSound('explosion').play(); } // Create medium asteroids for (var k = 0; k < 2; k++) { var newAsteroid = new Asteroid(1, asteroid.memeType); newAsteroid.x = asteroid.x + (Math.random() - 0.5) * 50; newAsteroid.y = asteroid.y + (Math.random() - 0.5) * 50; asteroids.push(newAsteroid); game.addChild(newAsteroid); } // Remove the large asteroid game.removeChild(asteroid); asteroids.splice(i, 1); } else { // Small and medium asteroids: Destroy completely and score double points score += asteroid.getPoints() * 2; // Double points for small/medium scoreTxt.setText('SCORE: ' + score); // Play explosion sound var memeTypeSmall = asteroid.memeType !== undefined ? asteroid.memeType : 0; var explosionSoundId = 'meme' + memeTypeSmall + 'Explosion'; var expSound = LK.getSound(explosionSoundId); if (expSound) { expSound.play(); } else { LK.getSound('explosion').play(); } // Remove the asteroid game.removeChild(asteroid); asteroids.splice(i, 1); } } } // Check if all asteroids are destroyed after aura if (asteroids.length === 0) { // Next level level++; levelTxt.setText('LEVEL: ' + level); LK.getSound('levelStart').play(); createAsteroidsForLevel(level); } } // Function to create particle effects when aura is activated function createAuraEffectParticles() { // Create a burst of particles from the ship - reduced for better performance for (var i = 0; i < 24; i++) { // Get particle from pool or create new one var particle = getFromPool(auraParticlePool); if (!particle) { particle = new AuraParticle(); game.addChild(particle); auraParticlePool.push(particle); } // Reset particle properties particle.age = 0; particle.active = true; particle.visible = true; // Calculate angle for circular burst var angle = i * (Math.PI * 2 / 24); // Position at the ship particle.x = ship.x; particle.y = ship.y; // Set velocity in all directions with varying speeds var speed = 4 + Math.random() * 5; // Variable speeds for more organic look particle.velocity.x = Math.cos(angle) * speed; particle.velocity.y = Math.sin(angle) * speed; // Add to active particles thrustParticles.push(particle); } // Create a flash effect LK.effects.flashObject(ship, 0x40E0D0, 500); } // Create Creeper Zone asteroids function createCreeperAsteroids(count) { // Create a specific number of creeper asteroids count = count || 5; // Default to 5 if not specified for (var i = 0; i < count; i++) { // Get creeper from pool or create new one var creeper = getFromPool(creeperAsteroidPool); if (!creeper) { creeper = new CreeperAsteroid(); game.addChild(creeper); creeperAsteroidPool.push(creeper); } // Reset creeper properties creeper.active = true; creeper.visible = true; // Reset velocity var speed = 0.9; var angle = Math.random() * Math.PI * 2; creeper.velocity = { x: Math.cos(angle) * speed, y: Math.sin(angle) * speed }; creeper.rotationSpeed = (Math.random() - 0.5) * 0.03; creeper.pulseDirection = 1; creeper.pulseAmount = 0; // Position the creeper away from the player do { creeper.x = Math.random() * 2048; creeper.y = Math.random() * 2732; } while (distance(creeper.x, creeper.y, ship.x, ship.y) < 350); asteroids.push(creeper); } } // Start the special Creeper Zone level function startCreeperZone() { // Set Creeper Zone flag inCreeperZone = true; creeperWaveCount = 0; // Clear any existing asteroids for (var i = asteroids.length - 1; i >= 0; i--) { game.removeChild(asteroids[i]); } asteroids = []; // Change music to the more tense gameMusic2 LK.playMusic('gameMusic2', { loop: true, fade: { start: 0, end: 1, duration: 1000 } }); // Create and play intro sequence creeperIntroSequence = new CreeperIntroSequence(); game.addChild(creeperIntroSequence); // Setup handler for when intro finishes LK.setInterval(function () { if (creeperIntroSequence && creeperIntroSequence.finished) { game.removeChild(creeperIntroSequence); creeperIntroSequence = null; // Start the first wave of creeper asteroids createCreeperAsteroids(5); // First wave has 5 creepers LK.clearInterval(this); } }, 100); } // Function to play outro sequence function playOutroSequence() { // Stop game music and start outro music LK.playMusic('outroMusic', { loop: true, fade: { start: 0, end: 1, duration: 1000 } }); // Create and add outro sequence outroSequence = new OutroSequence(); game.addChild(outroSequence); // Setup a handler for when outro is finished LK.setInterval(function () { if (outroSequence && outroSequence.finished) { game.removeChild(outroSequence); outroSequence = null; outroPlayed = true; // Finally show game over screen when player closes outro LK.showGameOver(); LK.clearInterval(this); } }, 100); } ;
===================================================================
--- original.js
+++ change.js
@@ -6,8 +6,76 @@
/****
* Classes
****/
+var AnimatedLetter = Container.expand(function (letter, targetX, targetY, index) {
+ var self = Container.call(this);
+ self.letterText = new Text2(letter, {
+ size: 200,
+ fill: 0xFFFFFF
+ });
+ self.letterText.anchor.set(0.5, 0.5);
+ self.addChild(self.letterText);
+ self.targetX = targetX;
+ self.targetY = targetY;
+ self.index = index;
+ self.x = 2048 / 2;
+ self.y = targetY + 150;
+ self.alpha = 0;
+ self.letterText.scaleX = 0.3;
+ self.letterText.scaleY = 0.3;
+ self.letterText.rotation = Math.PI * 0.5;
+ self.animateIn = function () {
+ // Slide from center to target position with fade in
+ tween(self, {
+ alpha: 1,
+ x: self.targetX,
+ y: self.targetY
+ }, {
+ duration: 1000,
+ easing: tween.easeOut
+ });
+ // Scale and rotate into place
+ tween(self.letterText, {
+ scaleX: 1,
+ scaleY: 1,
+ rotation: 0
+ }, {
+ duration: 1200,
+ easing: tween.elasticOut
+ });
+ };
+ self.transformColor = function (delay) {
+ // Staggered color transformation with delay
+ LK.setTimeout(function () {
+ // Add subtle pulse before color change
+ tween(self.letterText, {
+ scaleX: 1.1,
+ scaleY: 1.1
+ }, {
+ duration: 200,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ tween(self.letterText, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 200,
+ easing: tween.easeIn
+ });
+ }
+ });
+ // Color transformation wave
+ tween(self.letterText, {
+ tint: 0xff8000
+ }, {
+ duration: 800,
+ easing: tween.easeInOut
+ });
+ }, delay);
+ };
+ return self;
+});
var Asteroid = Container.expand(function (size, memeType) {
var self = Container.call(this);
// Size can be 0 (small), 1 (medium), or 2 (large)
self.size = size || 2;
@@ -45,20 +113,23 @@
self.x += self.velocity.x;
self.y += self.velocity.y;
// Rotate
asteroidGraphics.rotation += self.rotationSpeed;
- // Wrap around screen edges
+ // Wrap around screen edges - optimize by only checking when needed
if (self.x < -50) {
self.x = 2098;
- }
- if (self.x > 2098) {
+ self.justWrapped = true;
+ } else if (self.x > 2098) {
self.x = -50;
- }
- if (self.y < -50) {
+ self.justWrapped = true;
+ } else if (self.y < -50) {
self.y = 2782;
- }
- if (self.y > 2782) {
+ self.justWrapped = true;
+ } else if (self.y > 2782) {
self.y = -50;
+ self.justWrapped = true;
+ } else {
+ self.justWrapped = false;
}
};
self.getPoints = function () {
return (3 - self.size) * 100; // Small: 300, Medium: 200, Large: 100
@@ -128,19 +199,17 @@
self.update = function () {
self.x += self.velocity.x;
self.y += self.velocity.y;
self.age++;
- // Wrap around screen edges
+ // Wrap around screen edges - use else-if to avoid redundant checks
if (self.x < 0) {
self.x = 2048;
- }
- if (self.x > 2048) {
+ } else if (self.x > 2048) {
self.x = 0;
}
if (self.y < 0) {
self.y = 2732;
- }
- if (self.y > 2732) {
+ } else if (self.y > 2732) {
self.y = 0;
}
};
return self;
@@ -236,8 +305,1040 @@
}
};
return self;
});
+var CreeperAsteroid = Container.expand(function () {
+ var self = Container.call(this);
+ // Create a container for the asteroid
+ var asteroidGraphics = new Container();
+ self.addChild(asteroidGraphics);
+ // Always use meme9 (the Troll Face) as the creeper meme
+ self.memeType = 9;
+ // Add the meme image as the asteroid
+ var memeImage = LK.getAsset('meme' + self.memeType, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ asteroidGraphics.addChild(memeImage);
+ // Explosive properties
+ self.isExplosive = true;
+ self.explosionRadius = 300;
+ // Random velocity - slower than normal asteroids
+ var speed = 0.9;
+ var angle = Math.random() * Math.PI * 2;
+ self.velocity = {
+ x: Math.cos(angle) * speed,
+ y: Math.sin(angle) * speed
+ };
+ // Random rotation - slower to appear more menacing
+ self.rotationSpeed = (Math.random() - 0.5) * 0.03;
+ // Pulsating effect
+ self.pulseDirection = 1;
+ self.pulseAmount = 0;
+ self.pulseMax = 0.15;
+ self.pulseSpeed = 0.008;
+ self.update = function () {
+ // Move
+ self.x += self.velocity.x;
+ self.y += self.velocity.y;
+ // Rotate
+ asteroidGraphics.rotation += self.rotationSpeed;
+ // Pulsating effect
+ self.pulseAmount += self.pulseDirection * self.pulseSpeed;
+ if (self.pulseAmount >= self.pulseMax) {
+ self.pulseDirection = -1;
+ } else if (self.pulseAmount <= 0) {
+ self.pulseDirection = 1;
+ }
+ // Apply pulse to scale
+ var pulseScale = 1 + self.pulseAmount;
+ asteroidGraphics.scale.set(pulseScale, pulseScale);
+ // Make it glow green occasionally
+ if (LK.ticks % 60 < 15) {
+ asteroidGraphics.tint = 0x88FF88;
+ } else {
+ asteroidGraphics.tint = 0xFFFFFF;
+ }
+ // Wrap around screen edges
+ if (self.x < -50) {
+ self.x = 2098;
+ }
+ if (self.x > 2098) {
+ self.x = -50;
+ }
+ if (self.y < -50) {
+ self.y = 2782;
+ }
+ if (self.y > 2782) {
+ self.y = -50;
+ }
+ };
+ self.getPoints = function () {
+ return 200; // More points than regular asteroids
+ };
+ self.explode = function () {
+ // Create explosion visual effect
+ var explosion = new Container();
+ var explosionGraphic = LK.getAsset('forwardButton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: self.explosionRadius * 2,
+ height: self.explosionRadius * 2,
+ tint: 0x88FF88 // Green explosion
+ });
+ explosionGraphic.alpha = 0.7;
+ explosion.addChild(explosionGraphic);
+ explosion.x = self.x;
+ explosion.y = self.y;
+ game.addChild(explosion);
+ // Animate the explosion - optimized for better performance
+ tween(explosionGraphic, {
+ alpha: 0,
+ width: self.explosionRadius * 2.2,
+ // Slightly reduced final size
+ height: self.explosionRadius * 2.2 // Slightly reduced final size
+ }, {
+ duration: 400,
+ // Slightly faster animation
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ game.removeChild(explosion);
+ }
+ });
+ // Play specific explosion sound
+ LK.getSound('meme9Explosion').play();
+ };
+ return self;
+});
+var CreeperIntroSequence = Container.expand(function () {
+ var self = Container.call(this);
+ // Layers for effects
+ var starfieldLayer = new Container();
+ var creeperLayer = new Container();
+ var textLayer = new Container();
+ var shipLayer = new Container();
+ self.addChild(starfieldLayer);
+ self.addChild(creeperLayer);
+ self.addChild(textLayer);
+ self.addChild(shipLayer);
+ // Create starfield with green tint - significantly reduced count for better performance
+ var stars = [];
+ for (var i = 0; i < 20; i++) {
+ var star = LK.getAsset('bulletShape', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 3 + Math.random() * 5,
+ height: 3 + Math.random() * 5,
+ x: Math.random() * 2048,
+ y: Math.random() * 2732,
+ tint: 0x88FF88 // Green stars
+ });
+ star.alpha = 0.3 + Math.random() * 0.7;
+ star.velocity = 10 + Math.random() * 20;
+ stars.push(star);
+ starfieldLayer.addChild(star);
+ }
+ // Create creeper silhouettes in background
+ var creepers = [];
+ for (var j = 0; j < 6; j++) {
+ var creeper = LK.getAsset('meme9', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 200 + Math.random() * 1600,
+ y: -300 - Math.random() * 600,
+ tint: 0x44AA44 // Dark green silhouettes
+ });
+ creeper.alpha = 0.3;
+ creeper.scale.set(0.3, 0.3);
+ creepers.push(creeper);
+ creeperLayer.addChild(creeper);
+ }
+ // Warning text
+ var warningText = new Text2('CAUTION: CREEPER TERRITORY!', {
+ size: 80,
+ fill: 0x88FF88 // Green text
+ });
+ warningText.anchor.set(0.5, 0.5);
+ warningText.x = 2048 / 2;
+ warningText.y = 2732 / 2;
+ warningText.alpha = 0;
+ textLayer.addChild(warningText);
+ // Subtitle warning
+ var subtitleText = new Text2('BEWARE OF EXPLOSIVES!', {
+ size: 60,
+ fill: 0xFF3333 // Red text for emphasis
+ });
+ subtitleText.anchor.set(0.5, 0.5);
+ subtitleText.x = 2048 / 2;
+ subtitleText.y = 2732 / 2 + 100;
+ subtitleText.alpha = 0;
+ textLayer.addChild(subtitleText);
+ // Hero ship
+ var heroShip = new Ship();
+ heroShip.x = -100;
+ heroShip.y = 2732 / 2;
+ shipLayer.addChild(heroShip);
+ // Animation sequence
+ self.phase = 0;
+ self.timer = 0;
+ self.finished = false;
+ self.update = function () {
+ self.timer++;
+ // Update stars
+ for (var i = 0; i < stars.length; i++) {
+ var star = stars[i];
+ star.y += star.velocity;
+ if (star.y > 2732) {
+ star.y = -10;
+ star.x = Math.random() * 2048;
+ }
+ }
+ // Animation phases
+ switch (self.phase) {
+ case 0:
+ // Ship enters from left (0-2s)
+ tween(heroShip, {
+ x: 2048 / 2
+ }, {
+ duration: 2000,
+ easing: tween.easeOut
+ });
+ self.phase = 1;
+ break;
+ case 1:
+ // Creepers approach (2-4s)
+ for (var j = 0; j < creepers.length; j++) {
+ creepers[j].y += 3;
+ creepers[j].scale.x += 0.003;
+ creepers[j].scale.y += 0.003;
+ }
+ if (self.timer === 120) {
+ // After 2s, show warning text
+ tween(warningText, {
+ alpha: 1
+ }, {
+ duration: 500,
+ easing: tween.easeOut
+ });
+ // Make text glitch effect
+ LK.setInterval(function () {
+ warningText.scale.set(1 + Math.random() * 0.05, 1 + Math.random() * 0.05);
+ warningText.x = 2048 / 2 + (Math.random() * 20 - 10);
+ }, 80);
+ self.phase = 2;
+ }
+ break;
+ case 2:
+ // Warning display (4-6s)
+ for (var k = 0; k < creepers.length; k++) {
+ creepers[k].y += 3;
+ creepers[k].scale.x += 0.004;
+ creepers[k].scale.y += 0.004;
+ }
+ if (self.timer === 240) {
+ // After 4s, show subtitle
+ tween(subtitleText, {
+ alpha: 1
+ }, {
+ duration: 500,
+ easing: tween.easeOut
+ });
+ // Flash the screen green briefly
+ LK.effects.flashScreen(0x88FF88, 300);
+ self.phase = 3;
+ }
+ break;
+ case 3:
+ // Subtitle and prepare to end (6-7s)
+ if (self.timer === 360) {
+ // After 6s, finish intro
+ tween(warningText, {
+ alpha: 0
+ }, {
+ duration: 500
+ });
+ tween(subtitleText, {
+ alpha: 0
+ }, {
+ duration: 500
+ });
+ tween(heroShip, {
+ x: 2048 + 100
+ }, {
+ duration: 1000,
+ easing: tween.easeIn
+ });
+ self.phase = 4;
+ }
+ break;
+ case 4:
+ // Fade out and end (7-8s)
+ if (self.timer === 420) {
+ self.finished = true;
+ }
+ break;
+ }
+ return self.finished;
+ };
+ self.skip = function () {
+ // Do nothing - disable skipping
+ };
+ return self;
+});
+var GlaudIntroSequence = Container.expand(function () {
+ var self = Container.call(this);
+ var particles = [];
+ var letters = [];
+ var particlePool = [];
+ // Create wireframe ball
+ var wireframeBall = new WireframeBall();
+ wireframeBall.x = 2048 / 2;
+ wireframeBall.y = 2732 / 2;
+ wireframeBall.scaleX = 0.1;
+ wireframeBall.scaleY = 0.1;
+ wireframeBall.alpha = 0;
+ self.addChild(wireframeBall);
+ // Create GLAUD letters
+ var letterPositions = [{
+ letter: 'G',
+ x: 2048 / 2 - 400,
+ y: 2732 / 2
+ }, {
+ letter: 'L',
+ x: 2048 / 2 - 200,
+ y: 2732 / 2
+ }, {
+ letter: 'A',
+ x: 2048 / 2,
+ y: 2732 / 2
+ }, {
+ letter: 'U',
+ x: 2048 / 2 + 200,
+ y: 2732 / 2
+ }, {
+ letter: 'D',
+ x: 2048 / 2 + 400,
+ y: 2732 / 2
+ }];
+ for (var i = 0; i < letterPositions.length; i++) {
+ var letterData = letterPositions[i];
+ var animatedLetter = new AnimatedLetter(letterData.letter, letterData.x, letterData.y, i);
+ letters.push(animatedLetter);
+ self.addChild(animatedLetter);
+ }
+ // Initialize particle pool for better performance
+ function initParticlePool() {
+ for (var i = 0; i < 50; i++) {
+ var particle = new GlaudParticle('energy');
+ particle.active = false;
+ particlePool.push(particle);
+ }
+ }
+ function getPooledParticle() {
+ for (var i = 0; i < particlePool.length; i++) {
+ if (!particlePool[i].active) {
+ particlePool[i].active = true;
+ return particlePool[i];
+ }
+ }
+ // If pool is empty, create new particle
+ var newParticle = new GlaudParticle('energy');
+ newParticle.active = true;
+ particlePool.push(newParticle);
+ return newParticle;
+ }
+ function returnParticleToPool(particle) {
+ particle.active = false;
+ if (particle.parent) {
+ particle.parent.removeChild(particle);
+ }
+ }
+ // Initialize intro sequence with chained animations
+ function initIntroSequence() {
+ // Start ball growth animation
+ startBallGrowth();
+ }
+ function startBallGrowth() {
+ LK.getSound('ballGrow').play();
+ // Wireframe ball growth with rotation acceleration
+ tween(wireframeBall, {
+ scaleX: 3.2,
+ scaleY: 3.2,
+ alpha: 1
+ }, {
+ duration: 3000,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ // Brief pause at maximum size, then start contraction
+ LK.setTimeout(function () {
+ startBallContraction();
+ }, 500);
+ }
+ });
+ // Gradually increase rotation speeds
+ tween(wireframeBall, {
+ rotationSpeedX: 0.08,
+ rotationSpeedY: 0.1,
+ rotationSpeedZ: 0.06
+ }, {
+ duration: 3000,
+ easing: tween.easeInOut
+ });
+ // Add energy buildup with sparkle intensity
+ LK.setTimeout(function () {
+ // Final pulse before contraction
+ tween(wireframeBall, {
+ scaleX: 3.5,
+ scaleY: 3.5
+ }, {
+ duration: 200,
+ easing: tween.easeInOut
+ });
+ // Maximum rotation speed for instability effect
+ wireframeBall.rotationSpeedX = 0.12;
+ wireframeBall.rotationSpeedY = 0.15;
+ wireframeBall.rotationSpeedZ = 0.1;
+ }, 2000);
+ }
+ function startBallContraction() {
+ LK.getSound('ballContract').play();
+ // Dramatic wireframe ball implosion with extreme rotation
+ wireframeBall.rotationSpeedX = 0.8;
+ wireframeBall.rotationSpeedY = 1.0;
+ wireframeBall.rotationSpeedZ = 0.6;
+ tween(wireframeBall, {
+ scaleX: 0,
+ scaleY: 0,
+ alpha: 0
+ }, {
+ duration: 600,
+ easing: tween.easeInQuad,
+ onFinish: function onFinish() {
+ wireframeBall.alpha = 0;
+ // Start letter formation after brief pause
+ LK.setTimeout(function () {
+ startLetterFormation();
+ }, 200);
+ }
+ });
+ // Delay explosion slightly for implosion effect
+ LK.setTimeout(function () {
+ createParticleExplosion();
+ }, 300);
+ }
+ function startLetterFormation() {
+ // Animate letters appearing one by one
+ animateLetterSequence(0);
+ }
+ function animateLetterSequence(index) {
+ if (index >= letters.length) {
+ // All letters have appeared, start color transformation
+ LK.setTimeout(function () {
+ startColorTransformation();
+ }, 500);
+ return;
+ }
+ LK.getSound('letterAppear').play();
+ letters[index].animateIn();
+ // Schedule next letter with delay
+ LK.setTimeout(function () {
+ animateLetterSequence(index + 1);
+ }, 250);
+ }
+ function startColorTransformation() {
+ LK.getSound('colorTransform').play();
+ // Staggered color transformation for wave effect
+ var completedTransforms = 0;
+ for (var i = 0; i < letters.length; i++) {
+ (function (letterIndex) {
+ LK.setTimeout(function () {
+ letters[letterIndex].transformColor(0);
+ completedTransforms++;
+ // Check if all transformations are complete
+ if (completedTransforms === letters.length) {
+ LK.setTimeout(function () {
+ startFinalEmphasis();
+ }, 800);
+ }
+ }, letterIndex * 150);
+ })(i);
+ }
+ }
+ function startFinalEmphasis() {
+ // Overall luminosity surge for all letters
+ for (var i = 0; i < letters.length; i++) {
+ tween(letters[i].letterText, {
+ tint: 0xffaa00,
+ // Brighter orange/yellow
+ scaleX: 1.08,
+ scaleY: 1.08
+ }, {
+ duration: 500,
+ easing: tween.easeOut
+ });
+ }
+ // Start progressive fade-out after surge
+ LK.setTimeout(function () {
+ startProgressiveFadeOut();
+ }, 500);
+ }
+ function startProgressiveFadeOut() {
+ // Unified Power Display (0 - 0.8 Seconds)
+ startUnifiedPowerDisplay();
+ }
+ function startUnifiedPowerDisplay() {
+ // Screen flash to white briefly for dramatic effect
+ LK.effects.flashScreen(0xffffff, 200);
+ // Make all letters shine with intense light simultaneously
+ for (var i = 0; i < letters.length; i++) {
+ // Multiple tween stages for more dramatic buildup
+ tween(letters[i].letterText, {
+ tint: 0xffdd00,
+ // Bright gold first
+ scaleX: 1.25,
+ scaleY: 1.25
+ }, {
+ duration: 300,
+ easing: tween.easeOut,
+ onFinish: function (targetLetter) {
+ return function () {
+ tween(targetLetter.letterText, {
+ tint: 0xffffff,
+ // Then to brilliant white
+ scaleX: 1.4,
+ scaleY: 1.4
+ }, {
+ duration: 500,
+ easing: tween.easeInOut
+ });
+ };
+ }(letters[i])
+ });
+ }
+ // Create god rays emanating from the text
+ createGodRays();
+ // Create energy particles swirling around letters
+ createEnergySwirl();
+ // Schedule the spectacular disappearance
+ LK.setTimeout(function () {
+ startSpectacularDisappearance();
+ }, 800);
+ }
+ function createGodRays() {
+ // Create multiple layers of light rays for depth
+ for (var layer = 0; layer < 2; layer++) {
+ var rayCount = layer === 0 ? 16 : 8;
+ var rayLength = layer === 0 ? 4 : 6;
+ for (var i = 0; i < rayCount; i++) {
+ var ray = self.addChild(LK.getAsset('wireframeEdge', {
+ anchorX: 0.5,
+ anchorY: 0,
+ x: 2048 / 2,
+ y: 2732 / 2,
+ rotation: Math.PI * 2 * i / rayCount + layer * Math.PI / 16,
+ scaleX: 0.3 + layer * 0.2,
+ scaleY: rayLength,
+ tint: layer === 0 ? 0xffaa00 : 0xffffff,
+ alpha: 0
+ }));
+ // Staggered ray appearance
+ LK.setTimeout(function (currentRay, delayTime) {
+ return function () {
+ tween(currentRay, {
+ alpha: 0.6 + Math.random() * 0.3,
+ scaleY: rayLength + 2
+ }, {
+ duration: 400,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ // Pulse the rays before fade
+ tween(currentRay, {
+ alpha: 0.9,
+ scaleY: rayLength + 3
+ }, {
+ duration: 200,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(currentRay, {
+ alpha: 0
+ }, {
+ duration: 300,
+ easing: tween.easeIn,
+ onFinish: function onFinish() {
+ currentRay.destroy();
+ }
+ });
+ }
+ });
+ }
+ });
+ };
+ }(ray, i * 50 + layer * 200), i * 50 + layer * 200);
+ }
+ }
+ }
+ function createEnergySwirl() {
+ // Create swirling energy particles around the text
+ for (var i = 0; i < 30; i++) {
+ var energyParticle = self.addChild(LK.getAsset('sparkle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2 + Math.cos(i * 0.5) * 200,
+ y: 2732 / 2 + Math.sin(i * 0.5) * 200,
+ tint: 0xffdd00,
+ alpha: 0
+ }));
+ // Animate particles in spiral motion
+ var particleDelay = i * 30;
+ LK.setTimeout(function (particle, index) {
+ return function () {
+ particle.alpha = 0.8;
+ // Create spiral motion
+ var _spiralTween = function spiralTween(currentAngle) {
+ var radius = 150 + Math.sin(currentAngle * 0.1) * 50;
+ var newX = 2048 / 2 + Math.cos(currentAngle) * radius;
+ var newY = 2732 / 2 + Math.sin(currentAngle) * radius;
+ tween(particle, {
+ x: newX,
+ y: newY,
+ rotation: currentAngle
+ }, {
+ duration: 100,
+ easing: tween.linear,
+ onFinish: function onFinish() {
+ if (currentAngle < Math.PI * 6) {
+ _spiralTween(currentAngle + 0.3);
+ } else {
+ // Burst toward center
+ tween(particle, {
+ x: 2048 / 2,
+ y: 2732 / 2,
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 200,
+ easing: tween.easeInQuad,
+ onFinish: function onFinish() {
+ particle.destroy();
+ }
+ });
+ }
+ }
+ });
+ };
+ _spiralTween(index * 0.5);
+ };
+ }(energyParticle, i), particleDelay);
+ }
+ }
+ function startSpectacularDisappearance() {
+ // Create dramatic screen distortion effect
+ for (var i = 0; i < letters.length; i++) {
+ var letter = letters[i];
+ // Add violent shaking before implosion
+ var shakeIntensity = 15;
+ var shakeCount = 0;
+ var shakeInterval = LK.setInterval(function (targetLetter) {
+ return function () {
+ targetLetter.x += (Math.random() - 0.5) * shakeIntensity;
+ targetLetter.y += (Math.random() - 0.5) * shakeIntensity;
+ shakeCount++;
+ if (shakeCount > 10) {
+ LK.clearInterval(shakeInterval);
+ }
+ };
+ }(letter), 50);
+ }
+ // Enhanced implosion with multiple stages
+ LK.setTimeout(function () {
+ var centerX = 2048 / 2;
+ var centerY = 2732 / 2;
+ // Stage 1: Rapid acceleration toward center
+ for (var i = 0; i < letters.length; i++) {
+ var letter = letters[i];
+ tween(letter, {
+ x: centerX,
+ y: centerY,
+ scaleX: 0.3,
+ scaleY: 0.3
+ }, {
+ duration: 180,
+ easing: tween.easeInCubic
+ });
+ tween(letter.letterText, {
+ tint: 0xffffff,
+ alpha: 1.2
+ }, {
+ duration: 180
+ });
+ }
+ // Stage 2: Final compression
+ LK.setTimeout(function () {
+ for (var i = 0; i < letters.length; i++) {
+ var letter = letters[i];
+ tween(letter, {
+ scaleX: 0.05,
+ scaleY: 0.05
+ }, {
+ duration: 80,
+ easing: tween.easeInQuart
+ });
+ }
+ }, 120);
+ // Create the enhanced bright point and burst
+ LK.setTimeout(function () {
+ createEnhancedBrightPointAndBurst();
+ }, 200);
+ }, 500);
+ }
+ function createEnhancedBrightPointAndBurst() {
+ // Create multiple concentric energy rings
+ var rings = [];
+ for (var r = 0; r < 3; r++) {
+ var ring = self.addChild(LK.getAsset('wireframeJoint', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 2732 / 2,
+ scaleX: 0.1 + r * 0.1,
+ scaleY: 0.1 + r * 0.1,
+ tint: r === 0 ? 0xffffff : r === 1 ? 0xffdd00 : 0xffaa00,
+ alpha: 1
+ }));
+ rings.push(ring);
+ }
+ // Intense multi-stage flash
+ var flashStage = 0;
+ var flashInterval = LK.setInterval(function () {
+ // Screen flash with varying intensities
+ var flashColors = [0xffffff, 0xffdd00, 0xffffff, 0xffaa00];
+ LK.effects.flashScreen(flashColors[flashStage % flashColors.length], 150);
+ for (var i = 0; i < rings.length; i++) {
+ tween(rings[i], {
+ scaleX: (flashStage + 1) * 0.5 + i * 0.2,
+ scaleY: (flashStage + 1) * 0.5 + i * 0.2,
+ alpha: 1 - flashStage * 0.15
+ }, {
+ duration: 120,
+ easing: tween.easeOut
+ });
+ }
+ flashStage++;
+ if (flashStage >= 4) {
+ LK.clearInterval(flashInterval);
+ // Final burst
+ LK.setTimeout(function () {
+ createMegaEnergyBurst();
+ // Hide all letters
+ for (var i = 0; i < letters.length; i++) {
+ letters[i].visible = false;
+ }
+ // Clean up rings
+ for (var i = 0; i < rings.length; i++) {
+ rings[i].destroy();
+ }
+ }, 200);
+ }
+ }, 150);
+ }
+ function createMegaEnergyBurst() {
+ // Create multiple expanding shockwaves
+ for (var wave = 0; wave < 4; wave++) {
+ LK.setTimeout(function (waveIndex) {
+ return function () {
+ var shockwave = self.addChild(LK.getAsset('forwardButton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 2732 / 2,
+ width: 30,
+ height: 30,
+ tint: waveIndex === 0 ? 0xffffff : waveIndex % 2 === 0 ? 0xffdd00 : 0xffaa00,
+ alpha: 0.9
+ }));
+ // Animate shockwave expanding with easing
+ tween(shockwave, {
+ width: 1200 + waveIndex * 200,
+ height: 1200 + waveIndex * 200,
+ alpha: 0
+ }, {
+ duration: 600 + waveIndex * 100,
+ easing: tween.easeOutCubic,
+ onFinish: function onFinish() {
+ shockwave.destroy();
+ }
+ });
+ };
+ }(wave), wave * 150);
+ }
+ // Create spectacular particle explosion
+ for (var i = 0; i < 50; i++) {
+ LK.setTimeout(function (particleIndex) {
+ return function () {
+ var particle = self.addChild(LK.getAsset('particle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2,
+ y: 2732 / 2,
+ tint: particleIndex % 3 === 0 ? 0xffffff : particleIndex % 3 === 1 ? 0xffdd00 : 0xffaa00
+ }));
+ var angle = Math.random() * Math.PI * 2;
+ var speed = 12 + Math.random() * 20;
+ var distance = 40 + Math.random() * 50;
+ var targetX = particle.x + Math.cos(angle) * distance;
+ var targetY = particle.y + Math.sin(angle) * distance;
+ // Multi-stage particle motion
+ tween(particle, {
+ x: targetX,
+ y: targetY,
+ scaleX: 1.5,
+ scaleY: 1.5
+ }, {
+ duration: 300,
+ easing: tween.easeOutQuad,
+ onFinish: function onFinish() {
+ // Second stage - continue outward and fade
+ var finalX = particle.x + Math.cos(angle) * 30;
+ var finalY = particle.y + Math.sin(angle) * 30;
+ tween(particle, {
+ x: finalX,
+ y: finalY,
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 400 + Math.random() * 300,
+ easing: tween.easeOutCubic,
+ onFinish: function onFinish() {
+ particle.destroy();
+ }
+ });
+ }
+ });
+ };
+ }(i), Math.random() * 200);
+ }
+ // Create lingering energy wisps
+ for (var w = 0; w < 15; w++) {
+ LK.setTimeout(function () {
+ var wisp = self.addChild(LK.getAsset('sparkle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 2048 / 2 + (Math.random() - 0.5) * 100,
+ y: 2732 / 2 + (Math.random() - 0.5) * 100,
+ tint: 0xffdd00,
+ alpha: 0.6
+ }));
+ // Gentle floating motion before fade
+ tween(wisp, {
+ x: wisp.x + (Math.random() - 0.5) * 200,
+ y: wisp.y + (Math.random() - 0.5) * 200,
+ alpha: 0,
+ rotation: Math.PI * 2
+ }, {
+ duration: 1500 + Math.random() * 1000,
+ easing: tween.easeOutSine,
+ onFinish: function onFinish() {
+ wisp.destroy();
+ }
+ });
+ }, w * 100 + Math.random() * 300);
+ }
+ // Extended silence then finish with dramatic fade to black
+ LK.setTimeout(function () {
+ // Final dramatic fade to black
+ LK.effects.flashScreen(0x000000, 1500);
+ LK.setTimeout(function () {
+ self.finished = true;
+ }, 1500);
+ }, 1200);
+ }
+ function createLetterParticles(letter) {
+ // Create small orange light particles when letter fades
+ for (var i = 0; i < 8; i++) {
+ var particle = self.addChild(LK.getAsset('particle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: letter.x + (Math.random() - 0.5) * 100,
+ y: letter.y + (Math.random() - 0.5) * 100
+ }));
+ particle.tint = 0xff8000; // Orange particles
+ particle.scaleX = 0.3 + Math.random() * 0.4;
+ particle.scaleY = particle.scaleX;
+ var angle = Math.random() * Math.PI * 2;
+ var speed = 3 + Math.random() * 5;
+ var targetX = particle.x + Math.cos(angle) * speed * 20;
+ var targetY = particle.y + Math.sin(angle) * speed * 20;
+ tween(particle, {
+ x: targetX,
+ y: targetY,
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1
+ }, {
+ duration: 600 + Math.random() * 400,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ particle.destroy();
+ }
+ });
+ }
+ }
+ function createParticleExplosion() {
+ // Create wireframe edge fragments for better visual impact
+ var fragmentsToCreate = Math.min(wireframeBall.edges.length, 15); // Fewer but more prominent fragments
+ for (var i = 0; i < fragmentsToCreate; i++) {
+ if (Math.random() < 0.9) {
+ var edgeIndex = Math.floor(Math.random() * wireframeBall.edges.length);
+ var edge = wireframeBall.edges[edgeIndex];
+ var fragment = self.addChild(LK.getAsset('wireframeEdge', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: wireframeBall.x + edge.x,
+ y: wireframeBall.y + edge.y,
+ rotation: edge.rotation,
+ scaleX: edge.scaleX,
+ scaleY: edge.scaleY,
+ alpha: edge.alpha
+ }));
+ // Calculate scatter direction from center
+ var centerX = wireframeBall.x;
+ var centerY = wireframeBall.y;
+ var fragmentX = fragment.x;
+ var fragmentY = fragment.y;
+ var dirX = fragmentX - centerX;
+ var dirY = fragmentY - centerY;
+ var distance = Math.sqrt(dirX * dirX + dirY * dirY);
+ if (distance === 0) {
+ distance = 1;
+ }
+ dirX /= distance;
+ dirY /= distance;
+ var speed = 15 + Math.random() * 25;
+ var targetX = fragmentX + dirX * speed * 20;
+ var targetY = fragmentY + dirY * speed * 20;
+ // Add random spinning
+ var rotationSpeed = (Math.random() - 0.5) * 0.3;
+ // Animate fragment scattering
+ tween(fragment, {
+ x: targetX,
+ y: targetY,
+ alpha: 0,
+ scaleX: 0.1,
+ scaleY: 0.1,
+ rotation: fragment.rotation + rotationSpeed * 10
+ }, {
+ duration: 1000 + Math.random() * 800,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ fragment.destroy();
+ }
+ });
+ }
+ }
+ // Create energy burst from joints (reduced count)
+ var jointsToCreate = Math.min(wireframeBall.joints.length, 6);
+ for (var i = 0; i < jointsToCreate; i++) {
+ var jointIndex = Math.floor(Math.random() * wireframeBall.joints.length);
+ var joint = wireframeBall.joints[jointIndex];
+ var energyBurst = self.addChild(LK.getAsset('wireframeJoint', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: wireframeBall.x + joint.x,
+ y: wireframeBall.y + joint.y,
+ scaleX: 1.5,
+ scaleY: 1.5
+ }));
+ var angle = Math.random() * Math.PI * 2;
+ var speed = 8 + Math.random() * 12;
+ var targetX = energyBurst.x + Math.cos(angle) * speed * 15;
+ var targetY = energyBurst.y + Math.sin(angle) * speed * 15;
+ tween(energyBurst, {
+ x: targetX,
+ y: targetY,
+ alpha: 0,
+ scaleX: 0.2,
+ scaleY: 0.2
+ }, {
+ duration: 800 + Math.random() * 600,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ energyBurst.destroy();
+ }
+ });
+ }
+ // Create main particle burst using pooled particles
+ for (var i = 0; i < 15; i++) {
+ var particle = getPooledParticle();
+ if (particle) {
+ particle.x = wireframeBall.x;
+ particle.y = wireframeBall.y;
+ var angle = Math.PI * 2 * i / 15;
+ var speed = 10 + Math.random() * 18;
+ particle.velocityX = Math.cos(angle) * speed;
+ particle.velocityY = Math.sin(angle) * speed;
+ particle.life = 1.0;
+ particle.markForDestroy = false;
+ particles.push(particle);
+ self.addChild(particle);
+ }
+ }
+ }
+ // Initialize particle pool and start sequence
+ initParticlePool();
+ initIntroSequence();
+ self.finished = false;
+ self.update = function () {
+ // Update active particles efficiently
+ for (var i = particles.length - 1; i >= 0; i--) {
+ var particle = particles[i];
+ if (particle.markForDestroy) {
+ returnParticleToPool(particle);
+ particles.splice(i, 1);
+ }
+ }
+ return self.finished;
+ };
+ return self;
+});
+var GlaudParticle = Container.expand(function (particleType) {
+ var self = Container.call(this);
+ var particleGraphics = self.attachAsset('particle', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.velocityX = 0;
+ self.velocityY = 0;
+ self.life = 1.0;
+ self.maxLife = 1.0;
+ self.fadeSpeed = 0.015 + Math.random() * 0.01;
+ self.rotationSpeed = (Math.random() - 0.5) * 0.2;
+ self.initialScale = 0.5 + Math.random() * 0.5;
+ particleGraphics.scaleX = self.initialScale;
+ particleGraphics.scaleY = self.initialScale;
+ if (particleType === 'energy') {
+ particleGraphics.tint = 0x4a90e2;
+ } else {
+ particleGraphics.tint = 0xffffff;
+ }
+ self.update = function () {
+ self.x += self.velocityX;
+ self.y += self.velocityY;
+ self.velocityX *= 0.98; // Add friction
+ self.velocityY *= 0.98;
+ particleGraphics.rotation += self.rotationSpeed;
+ self.life -= self.fadeSpeed;
+ var fadeRatio = self.life / self.maxLife;
+ particleGraphics.alpha = fadeRatio;
+ particleGraphics.scaleX = self.initialScale * fadeRatio;
+ particleGraphics.scaleY = self.initialScale * fadeRatio;
+ if (self.life <= 0) {
+ self.markForDestroy = true;
+ }
+ };
+ return self;
+});
var IntroSequence = Container.expand(function () {
var self = Container.call(this);
// Layers for effects
var starfieldLayer = new Container();
@@ -249,11 +1350,11 @@
self.addChild(memesLayer);
self.addChild(textLayer);
self.addChild(shipLayer);
self.addChild(titleLayer);
- // Create starfield
+ // Create starfield - significantly reduced number for better performance
var stars = [];
- for (var i = 0; i < 100; i++) {
+ for (var i = 0; i < 20; i++) {
var star = LK.getAsset('bulletShape', {
anchorX: 0.5,
anchorY: 0.5,
width: 3 + Math.random() * 5,
@@ -467,11 +1568,11 @@
self.addChild(starfieldLayer);
self.addChild(memesLayer);
self.addChild(textLayer);
self.addChild(shipLayer);
- // Create starfield
+ // Create starfield - significantly reduced number for better performance
var stars = [];
- for (var i = 0; i < 100; i++) {
+ for (var i = 0; i < 20; i++) {
var star = LK.getAsset('bulletShape', {
anchorX: 0.5,
anchorY: 0.5,
width: 3 + Math.random() * 5,
@@ -760,8 +1861,12 @@
};
// Track previous position for movement calculations
self.lastX = 0;
self.lastY = 0;
+ // Cache rotation values for optimization
+ self.lastRot = self.rot;
+ self.dirX = Math.cos(self.rot);
+ self.dirY = Math.sin(self.rot);
// Apply rotation to ship graphics
shipGraphics.rotation = self.rot;
self.update = function () {
// Store last position
@@ -773,42 +1878,55 @@
}
if (self.isRotatingRight) {
self.rot += self.rotationSpeed;
}
- // Apply rotation to ship visual
- shipGraphics.rotation = self.rot;
- // Calculate direction vector from rotation
- // 0 = right, PI/2 = down, PI = left, -PI/2 = up
- var dirX = Math.cos(self.rot);
- var dirY = Math.sin(self.rot);
+ // Only update rotation visual when it changes to reduce unnecessary calculations
+ if (self.lastRot !== self.rot) {
+ shipGraphics.rotation = self.rot;
+ self.lastRot = self.rot;
+ // Pre-calculate direction vectors when rotation changes
+ self.dirX = Math.cos(self.rot);
+ self.dirY = Math.sin(self.rot);
+ }
+ // Use cached direction values
// Apply thrust force in direction ship is facing (always moving)
- self.velocity.x += dirX * self.thrustPower;
- self.velocity.y += dirY * self.thrustPower;
+ self.velocity.x += self.dirX * self.thrustPower;
+ self.velocity.y += self.dirY * self.thrustPower;
// Apply drag/friction
self.velocity.x *= self.dragFactor;
self.velocity.y *= self.dragFactor;
- // Calculate current speed
- var speed = Math.sqrt(self.velocity.x * self.velocity.x + self.velocity.y * self.velocity.y);
- // Apply speed limit if necessary
- if (speed > self.maxSpeed) {
- self.velocity.x = self.velocity.x / speed * self.maxSpeed;
- self.velocity.y = self.velocity.y / speed * self.maxSpeed;
+ // Calculate squared speed once for both checks
+ var speedSquared = self.velocity.x * self.velocity.x + self.velocity.y * self.velocity.y;
+ var maxSpeedSquared = self.maxSpeed * self.maxSpeed;
+ // Only calculate actual speed when needed
+ var speed = 0;
+ if (speedSquared > maxSpeedSquared) {
+ // Only calculate square root when we need to limit speed
+ speed = Math.sqrt(speedSquared);
+ // Apply speed limit
+ var limitFactor = self.maxSpeed / speed;
+ self.velocity.x *= limitFactor;
+ self.velocity.y *= limitFactor;
+ speed = self.maxSpeed;
+ } else {
+ // Approximate speed for particle effects without expensive sqrt
+ speed = Math.abs(self.velocity.x) + Math.abs(self.velocity.y) * 0.5;
}
- // Create thrust particles
- if (speed > 0.5 && LK.ticks % 3 == 0) {
- // Create particle every 3 ticks if moving to improve smoothness
+ // Create thrust particles - optimize by creating fewer particles
+ if (speed > 0.5 && LK.ticks % 6 == 0) {
+ // Create particle every 6 ticks instead of 3 to reduce particle count
var particle = new ThrustParticle();
// Position at the back of the ship (opposite of direction)
- var backX = self.x - dirX * triangleSize * 0.5;
- var backY = self.y - dirY * triangleSize * 0.5;
+ var backX = self.x - self.dirX * triangleSize * 0.5;
+ var backY = self.y - self.dirY * triangleSize * 0.5;
// Add some randomness
backX += (Math.random() - 0.5) * 10;
backY += (Math.random() - 0.5) * 10;
particle.x = backX;
particle.y = backY;
// Set velocity opposite to ship direction with some randomness
- particle.velocity.x = -dirX * (1 + Math.random()) + (Math.random() - 0.5) * 0.5;
- particle.velocity.y = -dirY * (1 + Math.random()) + (Math.random() - 0.5) * 0.5;
+ particle.velocity.x = -self.dirX * (1 + Math.random()) + (Math.random() - 0.5) * 0.5;
+ particle.velocity.y = -self.dirY * (1 + Math.random()) + (Math.random() - 0.5) * 0.5;
// Add to game via event
if (typeof game.addThrustParticle === 'function') {
game.addThrustParticle(particle);
}
@@ -937,8 +2055,141 @@
return self.age >= self.lifespan;
};
return self;
});
+var WireframeBall = Container.expand(function () {
+ var self = Container.call(this);
+ self.edges = [];
+ self.joints = [];
+ self.ballRadius = 80;
+ self.rotationX = 0;
+ self.rotationY = 0;
+ self.rotationZ = 0;
+ self.rotationSpeedX = 0.02;
+ self.rotationSpeedY = 0.025;
+ self.rotationSpeedZ = 0.015;
+ // Create cube vertices mapped to sphere surface
+ var cubeVertices = [[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1],
+ // back face
+ [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1] // front face
+ ];
+ // Normalize vertices to sphere surface
+ for (var i = 0; i < cubeVertices.length; i++) {
+ var vertex = cubeVertices[i];
+ var length = Math.sqrt(vertex[0] * vertex[0] + vertex[1] * vertex[1] + vertex[2] * vertex[2]);
+ vertex[0] = vertex[0] / length * self.ballRadius;
+ vertex[1] = vertex[1] / length * self.ballRadius;
+ vertex[2] = vertex[2] / length * self.ballRadius;
+ }
+ // Create cube edges (12 edges total)
+ var cubeEdges = [[0, 1], [1, 2], [2, 3], [3, 0],
+ // back face edges
+ [4, 5], [5, 6], [6, 7], [7, 4],
+ // front face edges
+ [0, 4], [1, 5], [2, 6], [3, 7] // connecting edges
+ ];
+ // Create wireframe edges
+ for (var i = 0; i < cubeEdges.length; i++) {
+ var edge = cubeEdges[i];
+ var vertex1 = cubeVertices[edge[0]];
+ var vertex2 = cubeVertices[edge[1]];
+ var edgeElement = self.attachAsset('wireframeEdge', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Calculate edge center and rotation
+ var centerX = (vertex1[0] + vertex2[0]) / 2;
+ var centerY = (vertex1[1] + vertex2[1]) / 2;
+ var centerZ = (vertex1[2] + vertex2[2]) / 2;
+ var dx = vertex2[0] - vertex1[0];
+ var dy = vertex2[1] - vertex1[1];
+ var edgeLength = Math.sqrt(dx * dx + dy * dy);
+ var rotation = Math.atan2(dy, dx);
+ edgeElement.x = centerX;
+ edgeElement.y = centerY;
+ edgeElement.z = centerZ || 0;
+ edgeElement.rotation = rotation;
+ edgeElement.scaleY = edgeLength / 120; // Scale to fit edge length
+ edgeElement.alpha = 0.8;
+ edgeElement.originalX = centerX;
+ edgeElement.originalY = centerY;
+ edgeElement.originalZ = centerZ || 0;
+ edgeElement.originalRotation = rotation;
+ edgeElement.originalScaleY = edgeElement.scaleY;
+ self.edges.push(edgeElement);
+ }
+ // Create joints at vertices
+ for (var i = 0; i < cubeVertices.length; i++) {
+ var vertex = cubeVertices[i];
+ var joint = self.attachAsset('wireframeJoint', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ joint.x = vertex[0];
+ joint.y = vertex[1];
+ joint.z = vertex[2] || 0;
+ joint.originalX = vertex[0];
+ joint.originalY = vertex[1];
+ joint.originalZ = vertex[2] || 0;
+ joint.alpha = 0;
+ joint.pulsePhase = Math.random() * Math.PI * 2;
+ self.joints.push(joint);
+ }
+ self.project3D = function (x, y, z) {
+ // Simple 3D to 2D projection with rotation
+ var cosX = Math.cos(self.rotationX);
+ var sinX = Math.sin(self.rotationX);
+ var cosY = Math.cos(self.rotationY);
+ var sinY = Math.sin(self.rotationY);
+ var cosZ = Math.cos(self.rotationZ);
+ var sinZ = Math.sin(self.rotationZ);
+ // Rotate around X axis
+ var y1 = y * cosX - z * sinX;
+ var z1 = y * sinX + z * cosX;
+ // Rotate around Y axis
+ var x2 = x * cosY + z1 * sinY;
+ var z2 = -x * sinY + z1 * cosY;
+ // Rotate around Z axis
+ var x3 = x2 * cosZ - y1 * sinZ;
+ var y3 = x2 * sinZ + y1 * cosZ;
+ return {
+ x: x3,
+ y: y3,
+ z: z2
+ };
+ };
+ self.update = function () {
+ // Update rotation
+ self.rotationX += self.rotationSpeedX;
+ self.rotationY += self.rotationSpeedY;
+ self.rotationZ += self.rotationSpeedZ;
+ // Update edge positions with 3D rotation
+ for (var i = 0; i < self.edges.length; i++) {
+ var edge = self.edges[i];
+ var projected = self.project3D(edge.originalX, edge.originalY, edge.originalZ);
+ edge.x = projected.x;
+ edge.y = projected.y;
+ // Depth-based alpha and scale
+ var depth = (projected.z + self.ballRadius) / (2 * self.ballRadius);
+ edge.alpha = 0.4 + depth * 0.6;
+ edge.scaleX = 0.6 + depth * 0.4;
+ // Maintain original scale for length
+ edge.scaleY = edge.originalScaleY * (0.8 + depth * 0.2);
+ }
+ // Update joints with pulsing effect
+ for (var i = 0; i < self.joints.length; i++) {
+ var joint = self.joints[i];
+ var projected = self.project3D(joint.originalX, joint.originalY, joint.originalZ);
+ joint.x = projected.x;
+ joint.y = projected.y;
+ var depth = (projected.z + self.ballRadius) / (2 * self.ballRadius);
+ joint.pulsePhase += 0.08;
+ joint.alpha = Math.max(0, depth * 0.9 * Math.sin(joint.pulsePhase));
+ joint.scaleX = joint.scaleY = 0.7 + depth * 0.3 + Math.sin(joint.pulsePhase) * 0.15;
+ }
+ };
+ return self;
+});
/****
* Initialize Game
****/
@@ -1017,8 +2268,14 @@
// Engine will assign actual ID
// Engine will assign actual ID
// Added new meme image asset
// Added new meme explosion sound asset
+// Object pools for performance optimization
+var bulletPool = [];
+var asteroidPool = [];
+var thrustParticlePool = [];
+var auraParticlePool = [];
+var creeperAsteroidPool = [];
var ship;
var bullets = [];
var asteroids = [];
var thrustParticles = []; // Array to store thrust particles
@@ -1033,15 +2290,47 @@
var outroPlayed = false;
var highScore = 0; // This will be loaded from storage
var highScoreTxt; // UI element for displaying the high score
var newRecordShown = false; // Track if new record notification has been shown
+var nextExtraLifeScore = 20000; // Score threshold for the next extra life
+var extraLifeInterval = 20000; // Get an extra life every 20,000 points
+var maxLives = 5; // Maximum number of lives a player can have
var triangleSize = 36; // Ship triangle size for bullet positioning (half of the size now)
+// Creeper Zone variables
+var inCreeperZone = false;
+var creeperIntroSequence = null;
+var creeperWaveCount = 0;
+var creeperMaxWaves = 3; // Number of waves to complete the Creeper Zone
+var glaudIntroSequence = null;
+var glaudIntroPlayed = false;
// Function to play intro sequence
function playIntroSequence() {
// Clear any existing game objects
bullets = [];
asteroids = [];
thrustParticles = [];
+ // Check if GLAUD intro has been played
+ if (!glaudIntroPlayed) {
+ // Start with GLAUD intro sequence
+ glaudIntroSequence = new GlaudIntroSequence();
+ game.addChild(glaudIntroSequence);
+ // Setup handler for when GLAUD intro finishes
+ LK.setInterval(function () {
+ if (glaudIntroSequence && glaudIntroSequence.finished) {
+ game.removeChild(glaudIntroSequence);
+ glaudIntroSequence = null;
+ glaudIntroPlayed = true;
+ // After GLAUD intro, continue with main intro
+ continueWithMainIntro();
+ LK.clearInterval(this);
+ }
+ }, 100);
+ } else {
+ // Skip GLAUD intro and go directly to main intro
+ continueWithMainIntro();
+ }
+}
+function continueWithMainIntro() {
// Play gameMusic3 for intro
LK.playMusic('gameMusic3', {
loop: true,
fade: {
@@ -1078,8 +2367,12 @@
// Initialize the game
function initGame() {
// Reset the new record notification flag
newRecordShown = false;
+ // Reset extra life score tracking
+ nextExtraLifeScore = 20000;
+ // Initialize object pools
+ initObjectPools();
// Check if we need to play intro
if (!gameStarted && !introPlayed) {
playIntroSequence();
return;
@@ -1187,8 +2480,63 @@
levelTxt.anchor.set(0, 0); // Left-align text
levelTxt.x = 180; // Move further right from top-left menu icon but slightly to the left
levelTxt.y = 80; // Move lower down from top edge
LK.gui.topLeft.addChild(levelTxt);
+ // Add developer feature - double-tap on level to advance
+ var lastLevelTapTime = 0;
+ var devModeActive = false;
+ levelTxt.interactive = true;
+ levelTxt.down = function (x, y, obj) {
+ var currentTime = Date.now();
+ // Check for double tap (within 300ms)
+ if (currentTime - lastLevelTapTime < 300) {
+ // Toggle dev mode on double tap
+ devModeActive = !devModeActive;
+ // Visual feedback that dev mode is active
+ if (devModeActive) {
+ // Initialize style property if it doesn't exist
+ if (!levelTxt.style) {
+ levelTxt.style = {};
+ }
+ levelTxt.style.fill = 0xFF8000; // Orange to indicate dev mode
+ // Show dev mode activated message
+ var devModeText = new Text2('DEV MODE ACTIVE!', {
+ size: 40,
+ fill: 0xFF8000
+ });
+ devModeText.anchor.set(0.5, 0.5);
+ devModeText.x = 2048 / 2;
+ devModeText.y = 2732 / 2;
+ game.addChild(devModeText);
+ // Fade out after 2 seconds
+ tween(devModeText, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ delay: 1000,
+ onFinish: function onFinish() {
+ game.removeChild(devModeText);
+ }
+ });
+ } else {
+ levelTxt.style.fill = 0xFFFFFF; // Reset to white when disabled
+ }
+ } else if (devModeActive) {
+ // In dev mode, single tap advances the level
+ // Clear existing asteroids
+ for (var i = asteroids.length - 1; i >= 0; i--) {
+ game.removeChild(asteroids[i]);
+ }
+ asteroids = [];
+ // Advance to next level
+ level++;
+ levelTxt.setText('LEVEL: ' + level);
+ LK.getSound('levelStart').play();
+ // Create new asteroids for the next level
+ createAsteroidsForLevel(level);
+ }
+ lastLevelTapTime = currentTime;
+ };
// Start screen text
if (!gameStarted) {
// Create gray background for start text
var startBackground = new Container();
@@ -1222,9 +2570,43 @@
var numAsteroids = Math.min(4 + level, 12);
for (var i = 0; i < numAsteroids; i++) {
// Create a random meme type (0-13)
var memeType = Math.floor(Math.random() * 14); // Now 0-13 for 14 memes
- var asteroid = new Asteroid(2, memeType); // Start with large asteroids and specific meme type
+ // Get asteroid from pool or create new one if pool is empty
+ var asteroid = getFromPool(asteroidPool);
+ if (!asteroid || asteroid.size !== 2) {
+ // Try to find a size 2 asteroid specifically
+ for (var j = 0; j < asteroidPool.length; j++) {
+ if (!asteroidPool[j].active && asteroidPool[j].size === 2) {
+ asteroid = asteroidPool[j];
+ break;
+ }
+ }
+ // If no size 2 asteroid found, create a new one
+ if (!asteroid || asteroid.size !== 2) {
+ asteroid = new Asteroid(2, memeType);
+ game.addChild(asteroid);
+ asteroidPool.push(asteroid);
+ } else {
+ // Reset properties of pooled asteroid
+ asteroid.memeType = memeType;
+ }
+ } else {
+ // Reset properties of pooled asteroid
+ asteroid.memeType = memeType;
+ }
+ // Reset asteroid state
+ asteroid.active = true;
+ asteroid.visible = true;
+ asteroid.justWrapped = false;
+ // Re-initialize velocity
+ var speed = (3 - asteroid.size) * 0.8 + 0.5;
+ var angle = Math.random() * Math.PI * 2;
+ asteroid.velocity = {
+ x: Math.cos(angle) * speed,
+ y: Math.sin(angle) * speed
+ };
+ asteroid.rotationSpeed = (Math.random() - 0.5) * 0.05;
// Apply special behavior based on meme type
if (memeType === 7) {
// "This is Fine" meme - moves slower
asteroid.velocity.x *= 0.6;
@@ -1252,16 +2634,25 @@
asteroid.x = Math.random() * 2048;
asteroid.y = Math.random() * 2732;
} while (distance(asteroid.x, asteroid.y, ship.x, ship.y) < 300);
asteroids.push(asteroid);
- game.addChild(asteroid);
}
}
function distance(x1, y1, x2, y2) {
return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
}
function createBullet() {
- var bullet = new Bullet();
+ // Get bullet from pool or create new one if pool is empty
+ var bullet = getFromPool(bulletPool);
+ if (!bullet) {
+ bullet = new Bullet();
+ game.addChild(bullet);
+ bulletPool.push(bullet);
+ }
+ // Reset bullet properties
+ bullet.age = 0;
+ bullet.active = true;
+ bullet.visible = true;
// Use ship's rotation directly
var angle = ship.rot;
// Calculate the position at the tip of the ship based on the triangle geometry
// For the redesigned ship (pointing up at 0 rotation), the tip is -triangleSize units in Y
@@ -1272,11 +2663,10 @@
bullet.y = ship.y + offsetY;
// Set bullet velocity to match ship's exact facing direction
bullet.velocity.x = Math.cos(angle) * bullet.speed;
bullet.velocity.y = Math.sin(angle) * bullet.speed;
- // Add bullet to game
+ // Add bullet to active bullets array
bullets.push(bullet);
- game.addChild(bullet);
// Play shoot sound
LK.getSound('shoot').play();
}
function updateAsteroids() {
@@ -1375,21 +2765,74 @@
var bullet = bullets[i];
bullet.update();
// Remove bullets that have lived too long
if (bullet.age > bullet.lifespan) {
- game.removeChild(bullet);
+ // Return bullet to pool instead of removing
+ bullet.active = false;
+ bullet.visible = false;
bullets.splice(i, 1);
continue;
}
// Check for collisions with asteroids
var hitAsteroid = false;
for (var j = asteroids.length - 1; j >= 0; j--) {
var asteroid = asteroids[j];
+ // Skip intersection check if asteroid just wrapped (reduces false positives)
+ if (asteroid.justWrapped) {
+ continue;
+ }
if (bullet.intersects(asteroid)) {
hitAsteroid = true;
// Add score based on asteroid size
score += asteroid.getPoints();
scoreTxt.setText('SCORE: ' + score);
+ // Check if player earned an extra life
+ if (score >= nextExtraLifeScore) {
+ // Set next threshold regardless of whether we can get a life
+ nextExtraLifeScore += extraLifeInterval;
+ // Only award an extra life if below maximum
+ if (lives < maxLives) {
+ // Award an extra life
+ lives++;
+ // Update lives display
+ livesTxt.update(lives);
+ // Show extra life notification
+ var extraLifeText = new Text2('EXTRA LIFE!', {
+ size: 80,
+ fill: 0x00FF00 // Bright green
+ });
+ extraLifeText.anchor.set(0.5, 0.5);
+ extraLifeText.x = 2048 / 2;
+ extraLifeText.y = 2732 / 2 - 200;
+ extraLifeText.alpha = 0;
+ game.addChild(extraLifeText);
+ // Animate the notification
+ tween(extraLifeText, {
+ alpha: 1,
+ scaleX: 1.3,
+ scaleY: 1.3
+ }, {
+ duration: 500,
+ easing: tween.elasticOut,
+ onFinish: function onFinish() {
+ // Wait 2 seconds before fading out
+ LK.setTimeout(function () {
+ tween(extraLifeText, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ game.removeChild(extraLifeText);
+ }
+ });
+ }, 2000);
+ }
+ });
+ // Play a positive sound for extra life
+ LK.getSound('levelStart').play();
+ }
+ }
// Check if player just beat the high score and notification hasn't been shown yet
if (score > highScore && score - asteroid.getPoints() <= highScore && !newRecordShown) {
// Set flag to true so we don't show it again this game
newRecordShown = true;
@@ -1429,77 +2872,232 @@
// Update high score
highScore = score;
highScoreTxt.setText('BEST: ' + highScore);
}
- // Get the asteroid's meme type, defaulting to 0 if undefined
- var memeType = asteroid.memeType !== undefined ? asteroid.memeType : 0;
- // Construct the specific meme explosion sound ID
- var memeExplosionSoundId = 'meme' + memeType + 'Explosion';
- // Try to get the specific meme explosion sound
- var explosionSound = LK.getSound(memeExplosionSoundId);
- // Play the specific meme explosion sound or fallback to generic explosion
- if (explosionSound) {
- explosionSound.play();
+ // Special handling for Creeper asteroids in Creeper Zone
+ if (inCreeperZone && asteroid.isExplosive) {
+ // Call the explode method
+ asteroid.explode();
+ // Check if ship is within explosion radius
+ var dist = distance(asteroid.x, asteroid.y, ship.x, ship.y);
+ if (dist <= asteroid.explosionRadius && !ship.invulnerable) {
+ // Player loses a life if within explosion radius
+ lives--;
+ livesTxt.update(lives);
+ // Flash screen green
+ LK.effects.flashScreen(0x88FF88, 300);
+ // Make ship invulnerable
+ ship.makeInvulnerable(180);
+ // Game over if no lives left
+ if (lives <= 0) {
+ if (score > highScore) {
+ highScore = score;
+ storage.highScore = highScore;
+ }
+ LK.setScore(score);
+ playOutroSequence();
+ gameOver = true;
+ return;
+ }
+ }
+ // Remove the asteroid
+ game.removeChild(asteroid);
+ asteroids.splice(j, 1);
+ break;
} else {
- // Fallback to generic explosion sound
- console.log('Fallback explosion sound for memeType:', memeType);
- LK.getSound('explosion').play();
- }
- // Break large asteroids (size 2) into smaller pieces
- if (asteroid.size === 2) {
- for (var k = 0; k < 2; k++) {
- // Create medium asteroids (size 1)
- var newAsteroid = new Asteroid(1, asteroid.memeType);
- newAsteroid.x = asteroid.x;
- newAsteroid.y = asteroid.y;
- // No velocity inheritance - just use the default size-based velocity
- asteroids.push(newAsteroid);
- game.addChild(newAsteroid);
+ // Get the asteroid's meme type, defaulting to 0 if undefined
+ var memeType = asteroid.memeType !== undefined ? asteroid.memeType : 0;
+ // Construct the specific meme explosion sound ID
+ var memeExplosionSoundId = 'meme' + memeType + 'Explosion';
+ // Try to get the specific meme explosion sound
+ var explosionSound = LK.getSound(memeExplosionSoundId);
+ // Play the specific meme explosion sound or fallback to generic explosion
+ if (explosionSound) {
+ explosionSound.play();
+ } else {
+ // Fallback to generic explosion sound
+ console.log('Fallback explosion sound for memeType:', memeType);
+ LK.getSound('explosion').play();
}
+ // Break large asteroids (size 2) into smaller pieces
+ if (asteroid.size === 2) {
+ for (var k = 0; k < 2; k++) {
+ // Get medium asteroid from pool or create new one
+ var newAsteroid = null;
+ // Try to find a size 1 asteroid in the pool
+ for (var p = 0; p < asteroidPool.length; p++) {
+ if (!asteroidPool[p].active && asteroidPool[p].size === 1) {
+ newAsteroid = asteroidPool[p];
+ break;
+ }
+ }
+ // If no pooled asteroid found, create a new one
+ if (!newAsteroid) {
+ newAsteroid = new Asteroid(1, asteroid.memeType);
+ game.addChild(newAsteroid);
+ asteroidPool.push(newAsteroid);
+ } else {
+ // Reset pooled asteroid properties
+ newAsteroid.memeType = asteroid.memeType;
+ // Reset velocity
+ var speed = 0.8 * 2 + 0.5; // Speed for medium asteroid
+ var angle = Math.random() * Math.PI * 2;
+ newAsteroid.velocity = {
+ x: Math.cos(angle) * speed,
+ y: Math.sin(angle) * speed
+ };
+ newAsteroid.rotationSpeed = (Math.random() - 0.5) * 0.05;
+ }
+ newAsteroid.active = true;
+ newAsteroid.visible = true;
+ newAsteroid.justWrapped = false;
+ newAsteroid.x = asteroid.x;
+ newAsteroid.y = asteroid.y;
+ asteroids.push(newAsteroid);
+ }
+ }
+ // Return hit asteroid to pool instead of removing
+ asteroid.active = false;
+ asteroid.visible = false;
+ asteroids.splice(j, 1);
+ break;
}
- // Remove the hit asteroid (large, medium, or small)
- game.removeChild(asteroid);
- asteroids.splice(j, 1);
- break;
}
}
if (hitAsteroid) {
- // Remove the bullet
- game.removeChild(bullet);
+ // Return bullet to pool instead of removing
+ bullet.active = false;
+ bullet.visible = false;
bullets.splice(i, 1);
}
}
// Check if all asteroids are destroyed
if (asteroids.length === 0) {
- // Next level
- level++;
- levelTxt.setText('LEVEL: ' + level);
- // Play level start sound
- LK.getSound('levelStart').play();
- // Ensure gameMusic3 keeps playing - no music track changes
- if (!musicPlaying) {
- // If music isn't playing for some reason, restart gameMusic3
- LK.playMusic('gameMusic3', {
- loop: true,
- fade: {
- start: 0,
- end: 1,
- duration: 1000
- }
- });
- musicPlaying = true;
+ // Special handling for Creeper Zone
+ if (inCreeperZone) {
+ creeperWaveCount++;
+ // Check if we've completed all Creeper Zone waves
+ if (creeperWaveCount >= creeperMaxWaves) {
+ // End Creeper Zone and continue to next level
+ inCreeperZone = false;
+ // Show "Area Clear" message
+ var areaClearText = new Text2('AREA CLEAR!', {
+ size: 80,
+ fill: 0x88FF88 // Green text
+ });
+ areaClearText.anchor.set(0.5, 0.5);
+ areaClearText.x = 2048 / 2;
+ areaClearText.y = 2732 / 2;
+ areaClearText.alpha = 0;
+ game.addChild(areaClearText);
+ // Animate the message
+ tween(areaClearText, {
+ alpha: 1,
+ scaleX: 1.5,
+ scaleY: 1.5
+ }, {
+ duration: 500,
+ easing: tween.elasticOut,
+ onFinish: function onFinish() {
+ LK.setTimeout(function () {
+ tween(areaClearText, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ game.removeChild(areaClearText);
+ }
+ });
+ }, 2000);
+ }
+ });
+ // Return to normal music
+ LK.playMusic('gameMusic3', {
+ loop: true,
+ fade: {
+ start: 0,
+ end: 1,
+ duration: 1000
+ }
+ });
+ // Move to level 10
+ level = 10;
+ levelTxt.setText('LEVEL: ' + level);
+ // Play level start sound
+ LK.getSound('levelStart').play();
+ // Create new asteroids for the next level
+ createAsteroidsForLevel(level);
+ } else {
+ // Create next wave of creeper asteroids
+ var wavesLeft = creeperMaxWaves - creeperWaveCount;
+ // Display wave counter
+ var waveText = new Text2('WAVE ' + creeperWaveCount + ' COMPLETE! ' + wavesLeft + ' TO GO!', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ waveText.anchor.set(0.5, 0.5);
+ waveText.x = 2048 / 2;
+ waveText.y = 2732 / 2;
+ game.addChild(waveText);
+ // Fade out after 2 seconds
+ tween(waveText, {
+ alpha: 0
+ }, {
+ duration: 1000,
+ delay: 2000,
+ onFinish: function onFinish() {
+ game.removeChild(waveText);
+ }
+ });
+ // Create next wave - increase count with each wave
+ createCreeperAsteroids(5 + creeperWaveCount);
+ }
+ } else if (level === 9) {
+ // Start Creeper Zone after level 9
+ startCreeperZone();
+ } else {
+ // Normal level progression
+ // Next level
+ level++;
+ levelTxt.setText('LEVEL: ' + level);
+ // Play level start sound
+ LK.getSound('levelStart').play();
+ // Ensure gameMusic3 keeps playing - no music track changes
+ if (!musicPlaying) {
+ // If music isn't playing for some reason, restart gameMusic3
+ LK.playMusic('gameMusic3', {
+ loop: true,
+ fade: {
+ start: 0,
+ end: 1,
+ duration: 1000
+ }
+ });
+ musicPlaying = true;
+ }
+ // Create new asteroids for the next level
+ createAsteroidsForLevel(level);
}
- // Create new asteroids for the next level
- createAsteroidsForLevel(level);
}
}
// Main game update function
game.update = function () {
+ // Update GLAUD intro sequence if active
+ if (glaudIntroSequence) {
+ glaudIntroSequence.update();
+ return;
+ }
// Update intro sequence if active
if (introSequence) {
introSequence.update();
return;
}
+ // Update Creeper Zone intro if active
+ if (creeperIntroSequence) {
+ creeperIntroSequence.update();
+ return;
+ }
// Update outro sequence if active
if (outroSequence) {
outroSequence.update();
return;
@@ -1541,13 +3139,23 @@
var musicPlaying = false; // Track if music is currently playing
var introMusicPlayed = false; // Track if intro music has played
// Event handlers
game.down = function (x, y, obj) {
+ // Check if GLAUD intro is playing
+ if (glaudIntroSequence && !glaudIntroSequence.finished) {
+ glaudIntroSequence.finished = true;
+ return;
+ }
// Check if intro is playing
if (introSequence && !introSequence.finished) {
introSequence.skip();
return;
}
+ // Check if Creeper Zone intro is playing - but don't allow skipping
+ if (creeperIntroSequence && !creeperIntroSequence.finished) {
+ // Don't allow skipping the creeper intro
+ return;
+ }
// Check if outro is playing and can be exited
if (outroSequence) {
if (outroSequence.canExit) {
outroSequence.skip();
@@ -1573,23 +3181,31 @@
// Set current track to match
currentMusicTrack = 3;
musicPlaying = true;
}
- // Fire bullet when clicking anywhere on screen (if game started)
- if (gameStarted && ship && !gameOver) {
- createBullet();
- }
// Ensure buttons can detect touch events when pressed
var local;
var touchConsumed = false;
+ // Check if we tapped on level text for dev mode
+ if (levelTxt && levelTxt.getBounds) {
+ var levelBounds = levelTxt.getBounds();
+ // Check if tap is within level text bounds
+ if (x >= levelBounds.x && x <= levelBounds.x + levelBounds.width && y >= levelBounds.y && y <= levelBounds.y + levelBounds.height) {
+ // Let the level text handle the tap through its own down method
+ levelTxt.down(x, y, obj);
+ touchConsumed = true;
+ }
+ }
// Check left button
- local = leftButton.toLocal({
- x: x,
- y: y
- });
- if (local.x >= -leftButton.width / 2 && local.x <= leftButton.width / 2 && local.y >= -leftButton.height / 2 && local.y <= leftButton.height / 2) {
- leftButton.down(local.x, local.y, {});
- touchConsumed = true;
+ if (!touchConsumed) {
+ local = leftButton.toLocal({
+ x: x,
+ y: y
+ });
+ if (local.x >= -leftButton.width / 2 && local.x <= leftButton.width / 2 && local.y >= -leftButton.height / 2 && local.y <= leftButton.height / 2) {
+ leftButton.down(local.x, local.y, {});
+ touchConsumed = true;
+ }
}
// Check fire button only if touch not already consumed
if (!touchConsumed) {
local = fireButton.toLocal({
@@ -1630,22 +3246,118 @@
if (rightButton && typeof rightButton.up === 'function') {
rightButton.up(0, 0, {});
}
};
+// Initialize object pools with pre-populated objects
+function initObjectPools() {
+ // Clear existing pools
+ bulletPool = [];
+ asteroidPool = [];
+ thrustParticlePool = [];
+ auraParticlePool = [];
+ creeperAsteroidPool = [];
+ // Pre-populate bullet pool (30 bullets should be enough for most gameplay)
+ for (var i = 0; i < 30; i++) {
+ var bullet = new Bullet();
+ bullet.visible = false;
+ bullet.active = false;
+ bulletPool.push(bullet);
+ game.addChild(bullet);
+ }
+ // Pre-populate asteroid pool (30 for all sizes)
+ for (var i = 0; i < 10; i++) {
+ // Large asteroids (size 2)
+ var asteroid = new Asteroid(2);
+ asteroid.visible = false;
+ asteroid.active = false;
+ asteroidPool.push(asteroid);
+ game.addChild(asteroid);
+ // Medium asteroids (size 1)
+ asteroid = new Asteroid(1);
+ asteroid.visible = false;
+ asteroid.active = false;
+ asteroidPool.push(asteroid);
+ game.addChild(asteroid);
+ // Small asteroids (size 0)
+ asteroid = new Asteroid(0);
+ asteroid.visible = false;
+ asteroid.active = false;
+ asteroidPool.push(asteroid);
+ game.addChild(asteroid);
+ }
+ // Pre-populate thrust particle pool (50 particles)
+ for (var i = 0; i < 50; i++) {
+ var particle = new ThrustParticle();
+ particle.visible = false;
+ particle.active = false;
+ thrustParticlePool.push(particle);
+ game.addChild(particle);
+ }
+ // Pre-populate aura particle pool (24 particles)
+ for (var i = 0; i < 24; i++) {
+ var auraParticle = new AuraParticle();
+ auraParticle.visible = false;
+ auraParticle.active = false;
+ auraParticlePool.push(auraParticle);
+ game.addChild(auraParticle);
+ }
+ // Pre-populate creeper asteroid pool (15 creepers)
+ for (var i = 0; i < 15; i++) {
+ var creeper = new CreeperAsteroid();
+ creeper.visible = false;
+ creeper.active = false;
+ creeperAsteroidPool.push(creeper);
+ game.addChild(creeper);
+ }
+}
// Initialize the game when this script runs
initGame();
// Function to add thrust particles - called from Ship update
game.addThrustParticle = function (particle) {
- thrustParticles.push(particle);
- game.addChild(particle);
+ // If particle is from pool, just activate it
+ if (particle.active === false) {
+ particle.active = true;
+ particle.visible = true;
+ thrustParticles.push(particle);
+ // Don't need to add to game since it's already a child
+ } else {
+ // If it's a new particle, try to get one from the pool first
+ var pooledParticle = getFromPool(thrustParticlePool);
+ if (pooledParticle) {
+ // Reset particle properties
+ pooledParticle.x = particle.x;
+ pooledParticle.y = particle.y;
+ pooledParticle.velocity.x = particle.velocity.x;
+ pooledParticle.velocity.y = particle.velocity.y;
+ pooledParticle.age = 0;
+ pooledParticle.active = true;
+ pooledParticle.visible = true;
+ thrustParticles.push(pooledParticle);
+ } else {
+ // If pool is empty, use the provided particle
+ thrustParticles.push(particle);
+ game.addChild(particle);
+ }
+ }
};
+// Helper function to get object from pool
+function getFromPool(pool) {
+ for (var i = 0; i < pool.length; i++) {
+ if (!pool[i].active) {
+ return pool[i];
+ }
+ }
+ return null; // Pool is empty
+}
// Function to update thrust particles
function updateThrustParticles() {
for (var i = thrustParticles.length - 1; i >= 0; i--) {
var particle = thrustParticles[i];
// If particle update returns true, it means the particle should be removed
if (particle.update()) {
- game.removeChild(particle);
+ // Return particle to pool instead of removing
+ particle.active = false;
+ particle.visible = false;
thrustParticles.splice(i, 1);
}
}
}
@@ -1752,27 +3464,102 @@
}
}
// Function to create particle effects when aura is activated
function createAuraEffectParticles() {
- // Create a burst of particles from the ship
- for (var i = 0; i < 48; i++) {
- // Increased particle count for more impressive effect
- var angle = i * (Math.PI * 2 / 48);
- var particle = new AuraParticle(); // Use the dedicated AuraParticle class
+ // Create a burst of particles from the ship - reduced for better performance
+ for (var i = 0; i < 24; i++) {
+ // Get particle from pool or create new one
+ var particle = getFromPool(auraParticlePool);
+ if (!particle) {
+ particle = new AuraParticle();
+ game.addChild(particle);
+ auraParticlePool.push(particle);
+ }
+ // Reset particle properties
+ particle.age = 0;
+ particle.active = true;
+ particle.visible = true;
+ // Calculate angle for circular burst
+ var angle = i * (Math.PI * 2 / 24);
// Position at the ship
particle.x = ship.x;
particle.y = ship.y;
// Set velocity in all directions with varying speeds
var speed = 4 + Math.random() * 5; // Variable speeds for more organic look
particle.velocity.x = Math.cos(angle) * speed;
particle.velocity.y = Math.sin(angle) * speed;
- // Add to game
+ // Add to active particles
thrustParticles.push(particle);
- game.addChild(particle);
}
// Create a flash effect
LK.effects.flashObject(ship, 0x40E0D0, 500);
}
+// Create Creeper Zone asteroids
+function createCreeperAsteroids(count) {
+ // Create a specific number of creeper asteroids
+ count = count || 5; // Default to 5 if not specified
+ for (var i = 0; i < count; i++) {
+ // Get creeper from pool or create new one
+ var creeper = getFromPool(creeperAsteroidPool);
+ if (!creeper) {
+ creeper = new CreeperAsteroid();
+ game.addChild(creeper);
+ creeperAsteroidPool.push(creeper);
+ }
+ // Reset creeper properties
+ creeper.active = true;
+ creeper.visible = true;
+ // Reset velocity
+ var speed = 0.9;
+ var angle = Math.random() * Math.PI * 2;
+ creeper.velocity = {
+ x: Math.cos(angle) * speed,
+ y: Math.sin(angle) * speed
+ };
+ creeper.rotationSpeed = (Math.random() - 0.5) * 0.03;
+ creeper.pulseDirection = 1;
+ creeper.pulseAmount = 0;
+ // Position the creeper away from the player
+ do {
+ creeper.x = Math.random() * 2048;
+ creeper.y = Math.random() * 2732;
+ } while (distance(creeper.x, creeper.y, ship.x, ship.y) < 350);
+ asteroids.push(creeper);
+ }
+}
+// Start the special Creeper Zone level
+function startCreeperZone() {
+ // Set Creeper Zone flag
+ inCreeperZone = true;
+ creeperWaveCount = 0;
+ // Clear any existing asteroids
+ for (var i = asteroids.length - 1; i >= 0; i--) {
+ game.removeChild(asteroids[i]);
+ }
+ asteroids = [];
+ // Change music to the more tense gameMusic2
+ LK.playMusic('gameMusic2', {
+ loop: true,
+ fade: {
+ start: 0,
+ end: 1,
+ duration: 1000
+ }
+ });
+ // Create and play intro sequence
+ creeperIntroSequence = new CreeperIntroSequence();
+ game.addChild(creeperIntroSequence);
+ // Setup handler for when intro finishes
+ LK.setInterval(function () {
+ if (creeperIntroSequence && creeperIntroSequence.finished) {
+ game.removeChild(creeperIntroSequence);
+ creeperIntroSequence = null;
+ // Start the first wave of creeper asteroids
+ createCreeperAsteroids(5); // First wave has 5 creepers
+ LK.clearInterval(this);
+ }
+ }, 100);
+}
// Function to play outro sequence
function playOutroSequence() {
// Stop game music and start outro music
LK.playMusic('outroMusic', {
bulletShape. In-Game asset. 2d. High contrast. No shadows
fireButton is round and orange with a bullet pattern.. In-Game asset. 2d. High contrast. No shadows
rotateLeftButton is a square button with an arrow towards the left side.. In-Game asset. 2d. High contrast. No shadows
amungas memes no text one cracter. In-Game asset. 2d. High contrast. No shadows
trol face no text one cracter. In-Game asset. 2d. High contrast. No shadows
make those famous dog memes that are so well known.one cracter. head. In-Game asset. 2d. High contrast. No shadows
very well known pickles make Rick memes. single character. no writing.. In-Game asset. 2d. High contrast. No shadows
making very well known cat memes. single character. no writing.. In-Game asset. 2d. High contrast. No shadows
make very well known white ghost memes. single character. no writing.. In-Game asset. 2d. High contrast. No shadows
make very well known frog memes. single character. no writing. just head grinning. In-Game asset. 2d. High contrast. No shadows
make very well known duck memes. single character. no writing.. In-Game asset. 2d. High contrast. No shadows
make very well known rainbow cat memes. single character. no writing.. In-Game asset. 2d. High contrast. No shadows
very well known squid game just make cookie memes. single character. no writing.. In-Game asset. 2d. High contrast. No shadows
make very well known minecraft memes. single character. no writing.. In-Game asset. 2d. High contrast. No shadows
It's a spaceship made of orange pixels, reminiscent of arcade games.. In-Game asset. 2d. High contrast. No shadows
Not the hand with the cat.
A stylish orange letter G.. In-Game asset. 2d. High contrast. No shadows
shoot
Sound effect
meme0Explosion
Sound effect
explosion
Sound effect
meme1Explosion
Sound effect
meme2Explosion
Sound effect
meme3Explosion
Sound effect
meme4Explosion
Sound effect
meme5Explosion
Sound effect
meme6Explosion
Sound effect
meme7Explosion
Sound effect
meme8Explosion
Sound effect
meme9Explosion
Sound effect
levelStart
Sound effect
gameStart
Sound effect
beatSound
Sound effect
gameMusic1
Music
gameMusic2
Music
introMusic
Sound effect
gameMusic3
Music
meme10Explosion
Sound effect
meme11Explosion
Sound effect
meme12Explosion
Sound effect
outroMusic
Music
meme13Explosion
Sound effect
ballGrow
Sound effect
ballContract
Sound effect
letterAppear
Sound effect
colorTransform
Sound effect