/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Define a class for enemies var Enemy = Container.expand(function () { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.update = function () { self.x -= self.speed; if (self.x < -50) { self.destroy(); } }; }); // Define a class for explosion effects when enemies die var Explosion = Container.expand(function () { var self = Container.call(this); // Track explosion duration self.lifetime = 0; self.maxLifetime = 60; // reduced frames duration for shorter lasting neon glow effects // Create particles for the explosion self.createParticles = function (color) { // Use intense neon red colors exclusively for ultra-vibrant glow effect var particleColor = 0xFF0000; // Pure red base color // Ultra-vibrant neon red color palette with intense red variations for maximum glow effect var neonColors = [0xFF0000, 0xFF1100, 0xFF0500, 0xFF2200, 0xFF3333, 0xFF0033, 0xFF2211, 0xFF1111, 0xFF4444, 0xFF0022]; var colorVariations = [0xFF0000, // Pure red 0xFF2222, // Lighter red 0xFF3333, // Even brighter red 0xFFAAAA, // Intense bright red for highlights neonColors[Math.floor(Math.random() * neonColors.length)] // Add random neon red variation ]; // Create more particles for enhanced effect - reduced size for smaller explosions for (var i = 0; i < 35; i++) { // Reduced from 50 to 35 particles for smaller effect var size = Math.random() * 20 + 10; // Smaller particles for less dramatic impact // Use only circular particles with neon red glow for better visual effect var useNeon = Math.random() < 0.9; // 90% chance for neon particles var particleColor = useNeon ? neonColors[Math.floor(Math.random() * neonColors.length)] : colorVariations[Math.floor(Math.random() * colorVariations.length)]; var particle = self.attachAsset('rect', { width: size, height: size, color: particleColor, shape: 'ellipse', // Always use circles for more consistent red neon glow effect anchorX: 0.5, anchorY: 0.5, alpha: useNeon ? 0.99 : 0.9 // Even higher alpha for neon particles to enhance glow }); // Randomize particle position, direction and speed var angle = Math.random() * Math.PI * 2; var distance = Math.random() * 30; // Smaller initial spread particle.x = Math.cos(angle) * distance; particle.y = Math.sin(angle) * distance; // Store particle speed and direction with more variation particle.speedX = Math.cos(angle) * (Math.random() * 6 + 3); // Slower movement particle.speedY = Math.sin(angle) * (Math.random() * 6 + 3); particle.rotSpeed = (Math.random() - 0.5) * 0.8; // Less rotation particle.isNeon = useNeon; // Track if this is a neon particle // Set initial scale and rotation for more dynamic look particle.scaleX = 0.1; particle.scaleY = 0.1; particle.rotation = Math.random() * Math.PI * 2; // Random initial rotation // Animate particle with smaller neon red glow effects tween(particle, { scaleX: useNeon ? 2.0 : 1.0, // Smaller scale for neon particles to create less intense glow //{m} // Smaller scale for less dramatic visual scaleY: useNeon ? 2.0 : 1.0, rotation: particle.rotation + Math.PI * (Math.random() * 2), // Add rotation animation tint: useNeon ? neonColors[Math.floor(Math.random() * neonColors.length)] : particleColor // Add color transitions }, { duration: useNeon ? 800 : 300, // Shorter animation duration for neon particles for less dramatic effect //{o} // Shorter animation for smaller visual easing: tween.easeOutBack, onFinish: function (p) { return function () { // Enhanced neon particle fade-out for lingering glow effect var endScale = p.isNeon ? 0.6 : Math.random() < 0.5 ? 0 : 0.2; var endAlpha = p.isNeon ? 0.4 : endScale > 0 ? 0.1 : 0; var endRotation = p.rotation + Math.PI * (Math.random() + 1); tween(p, { scaleX: endScale, scaleY: endScale, alpha: endAlpha, rotation: endRotation }, { duration: p.isNeon ? 1400 : 600, // Much longer fade for neon particles for extended glow effect //{q} // Longer fade for better visual easing: tween.easeInOutQuad, onFinish: function onFinish() { if (p.parent) { p.parent.removeChild(p); } } }); }; }(particle) }); } // Add multiple shockwave effects (smaller) for (var i = 0; i < 2; i++) { var shockwaveColor = i === 0 ? 0xFFFFFF : neonColors[Math.floor(Math.random() * neonColors.length)]; var shockwave = self.attachAsset('rect', { width: 8, height: 8, color: shockwaveColor, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: i === 0 ? 0.7 : 0.5 }); // Animate shockwave expanding and fading with delay for sequential effect tween(shockwave, { scaleX: 8 + i * 3, // Each shockwave is smaller scaleY: 8 + i * 3, alpha: 0 }, { duration: 500 + i * 100, //{D} // Each shockwave lasts shorter delay: i * 100, // Stagger the shockwaves easing: tween.easeOutQuad }); } // Add central glow effect (smaller) var glow = self.attachAsset('rect', { width: 15, height: 15, color: neonColors[Math.floor(Math.random() * neonColors.length)], shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: 0.9 }); // Animate central glow (smaller scale) tween(glow, { scaleX: 4, scaleY: 4, alpha: 0 }, { duration: 400, easing: tween.easeOutQuad }); }; // Update explosion particles self.update = function () { self.lifetime++; // Update each particle for (var i = 0; i < self.children.length; i++) { var particle = self.children[i]; if (particle.speedX !== undefined) { particle.x += particle.speedX; particle.y += particle.speedY; // Add gravity effect to particles particle.speedY += 0.1; // Slow down particles over time with variable drag particle.speedX *= 0.94 + Math.random() * 0.02; particle.speedY *= 0.94 + Math.random() * 0.02; // Add ultra-enhanced neon glow pulse effect to neon particles if (particle.isNeon) { // Dramatically enhanced pulse effect for neon particles var pulseRate = 0.1; // Even faster pulse rate for more dynamic effect var pulseMagnitude = 0.4; // More dramatic pulse intensity var pulseOffset = self.lifetime * pulseRate + i * 0.15; var pulseValue = Math.sin(pulseOffset) * pulseMagnitude; // Apply much stronger pulse to alpha and scale for intense glow effect particle.alpha = Math.max(0.3, Math.min(0.98, 0.8 + pulseValue)); var baseScale = 1.2 + self.lifetime / self.maxLifetime * -0.3; // Even slower shrink rate for longer-lasting glow particle.scale.x = baseScale + pulseValue * 0.6; // Much stronger scale pulse particle.scale.y = baseScale + pulseValue * 0.6; // More frequent color changes for enhanced twinkling effect (neon particles only) if (Math.random() < 0.18) { // 18% chance each frame (increased from 15%) // Create ultra-intense neon red color variations for maximum sparkle effect var neonColors = [0xFF0000, 0xFF1100, 0xFF0500, 0xFF2200, 0xFF2222, 0xFF0033, 0xFF1111, 0xFF2211, 0xFF3311, 0xFF4422]; particle.tint = neonColors[Math.floor(Math.random() * neonColors.length)]; // Use tweening for smoother color transitions with enhanced glow effect tween(particle, { alpha: Math.random() * 0.3 + 0.7, // Random alpha between 0.7 and 1.0 for pulsing effect scaleX: particle.scaleX * (Math.random() * 0.4 + 0.9), // Add slight scale pulse scaleY: particle.scaleY * (Math.random() * 0.4 + 0.9) // Add slight scale pulse }, { duration: 180, easing: tween.easeOut }); } } // Rotate particles with variable speeds if (particle.rotSpeed) { particle.rotation += particle.rotSpeed; // Slightly vary rotation speed for more natural look particle.rotSpeed *= 0.97; // Slower deceleration for longer rotation } // Add more dynamic wobble to particle movement if (Math.random() < 0.15) { // Increased chance for movement variation particle.x += (Math.random() - 0.5) * 3; // Stronger wobble particle.y += (Math.random() - 0.5) * 3; } } } // Random chance to emit a secondary spark or neon trail particle (reduced size) if (self.lifetime < self.maxLifetime * 0.5 && Math.random() < 0.2) { // Reduced chance (20% down from 28%) for less frequent and shorter-lasting effects // Ultra-intense neon red colors for maximum spark brilliance and glow var neonColors = [0xFF0000, 0xFF1100, 0xFF0500, 0xFF2200, 0xFF2222, 0xFF1111, 0xFF0011, 0xFF3300, 0xFF4400]; var useNeon = Math.random() < 0.95; // 95% chance of neon particle var spark = self.attachAsset('rect', { width: useNeon ? 8 : 5, // Smaller particles for less visible glow height: useNeon ? 8 : 5, // Smaller particles for less visible glow color: useNeon ? neonColors[Math.floor(Math.random() * neonColors.length)] : 0xFF5555, // Use light red instead of white shape: 'ellipse', // Always use circles for enhanced red neon glow effect anchorX: 0.5, anchorY: 0.5, alpha: useNeon ? 0.95 : 0.8 // Slightly reduced alpha for less intense visibility }); // Position spark near an existing particle for trail effect var particleIndex = Math.floor(Math.random() * self.children.length); if (particleIndex < self.children.length && self.children[particleIndex].speedX !== undefined) { // Position near existing particle spark.x = self.children[particleIndex].x + (Math.random() - 0.5) * 10; spark.y = self.children[particleIndex].y + (Math.random() - 0.5) * 10; } else { // Fallback random position spark.x = (Math.random() - 0.5) * 40; spark.y = (Math.random() - 0.5) * 40; } // Animate spark with neon glow effect if it's a neon particle if (useNeon) { tween(spark, { alpha: 0.2, scaleX: 2, // Grow before fading scaleY: 2 }, { duration: 200, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(spark, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 400, easing: tween.easeInQuad }); } }); } else { // Regular spark animation tween(spark, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 300, easing: tween.easeOutQuad }); } } // Add less frequent energy burst effect during explosion lifetime - reduced frequency and size if (self.lifetime > 3 && self.lifetime < self.maxLifetime * 0.6 && Math.random() < 0.08) { // Reduced chance (8% down from 15%) for less frequent bursts and shorter visual duration var neonColors = [0xFF0000, 0xFF0500, 0xFF1100, 0xFF2200, 0xFF3300, 0xFF1111, 0xFF2211, 0xFF3311, 0xFF4411, 0xFF0022]; // Always use the most intense neon red colors for maximum visual impact var burstColor = neonColors[Math.floor(Math.random() * neonColors.length)]; var energyBurst = self.attachAsset('rect', { width: 15, // Smaller burst size height: 15, color: burstColor, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: 0.95 }); // Create less dramatic expanding ring effect tween(energyBurst, { scaleX: 5, //{1B} // Smaller final scale scaleY: 5, alpha: 0 }, { duration: 500, //{1F} // Shorter duration easing: tween.easeOutQuad }); // Add a second smaller, faster burst for layered effect (reduced frequency) if (Math.random() < 0.5) { // 50% chance for secondary burst (reduced from 80%) var secondaryBurst = self.attachAsset('rect', { width: 10, height: 10, color: neonColors[Math.floor(Math.random() * neonColors.length)], // Different color shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: 0.9 }); // Create faster expanding secondary ring effect (smaller) tween(secondaryBurst, { scaleX: 3, scaleY: 3, alpha: 0 }, { duration: 300, // Faster duration easing: tween.easeOutCubic // Different easing }); } } // Remove explosion when lifetime is up if (self.lifetime >= self.maxLifetime) { self.destroy(); } }; return self; }); // Define a class for flying dinosaur enemy var FlyingDino = Container.expand(function () { var self = Container.call(this); var dinoGraphics = self.attachAsset('flyingDino', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); // Customize appearance for dinosaur dinoGraphics.tint = 0xFFFFFF; // No tint needed for proper dinosaur asset self.speed = 8; // Faster than regular enemies self.verticalSpeed = 3; self.verticalDirection = 1; // 1 for down, -1 for up self.lastY = self.y; self.lastX = self.x; self.maxVerticalOffset = 150; // Reduced maximum vertical movement to keep in upper portion self.startY = 0; // Will store initial Y position self.update = function () { // Store last position for collision detection self.lastY = self.y; self.lastX = self.x; // Move horizontally self.x -= self.speed; // Move vertically in a wave pattern self.y += self.verticalSpeed * self.verticalDirection; // Change direction when reaching max offset from start position if (Math.abs(self.y - self.startY) > self.maxVerticalOffset) { self.verticalDirection *= -1; } if (self.x < -50) { self.destroy(); } }; return self; }); // Define a class for collectible hearts var Heart = Container.expand(function () { var self = Container.call(this); var heartGraphics = self.attachAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); self.speed = 5; self.lastY = self.y; self.lastX = self.x; // Add some floating animation self.floatOffset = Math.random() * Math.PI * 2; self.floatSpeed = 0.05; self.baseY = 0; self.update = function () { // Store last position for collision detection self.lastY = self.y; self.lastX = self.x; // Move the heart self.x -= self.speed; // Floating animation self.y = self.baseY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 20; // Remove if offscreen if (self.x < -50) { self.destroy(); } }; return self; }); //<Assets used in the game will automatically appear here> // Define a class for the player character var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.jumpHeight = 40; self.isJumping = false; self.canDoubleJump = false; // Track if player can perform double jump self.velocityY = 0; self.update = function () { // No need for physics-based jump calculation anymore // Animation is handled by tweens in jump method }; self.jump = function () { // First jump if (!self.isJumping) { self.isJumping = true; self.canDoubleJump = true; // Enable double jump after first jump // Use tween for smoother jump animation var initialY = self.y; var peakY = initialY - 500; // Jump peak height - increased from 300 to 500 // Store current X position to prevent horizontal movement during jump var currentX = self.x; tween(self, { y: peakY }, { duration: 500, easing: tween.easeOutQuad, onFinish: function onFinish() { // Fall back down with easing tween(self, { y: 2732 / 2 // Ground level }, { duration: 600, easing: tween.easeInQuad, onFinish: function onFinish() { self.isJumping = false; self.canDoubleJump = false; // Reset double jump ability self.velocityY = 0; } }); } }); } // Second jump (double jump) else if (self.canDoubleJump) { self.canDoubleJump = false; // Used double jump // Cancel any existing tweens on player tween.cancelAll(self); // Current Y position becomes the new starting point var currentY = self.y; var doubleJumpPeakY = currentY - 400; // Slightly lower second jump // Store current X position to prevent horizontal movement during double jump var currentX = self.x; // Perform double jump animation tween(self, { y: doubleJumpPeakY }, { duration: 400, easing: tween.easeOutQuad, onFinish: function onFinish() { // Fall back down with easing tween(self, { y: 2732 / 2 // Ground level }, { duration: 500, easing: tween.easeInQuad, onFinish: function onFinish() { self.isJumping = false; self.velocityY = 0; } }); } }); } }; }); // Define a class for collectible power-up stars var PowerStar = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); self.speed = 4; self.lastY = self.y; self.lastX = self.x; // Add floating animation self.floatOffset = Math.random() * Math.PI * 2; self.floatSpeed = 0.07; self.baseY = 0; // Add spinning animation self.rotation = 0; self.rotationSpeed = 0.05; self.update = function () { // Store last position for collision detection self.lastY = self.y; self.lastX = self.x; // Move the star self.x -= self.speed; // Floating animation self.y = self.baseY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 25; // Spinning animation self.rotation += self.rotationSpeed; // Remove if offscreen if (self.x < -50) { self.destroy(); } }; return self; }); // Define a class for rounded rectangle backgrounds var RoundedRect = Container.expand(function () { var self = Container.call(this); // Create a rounded rectangle by using multiple assets self.create = function (width, height, radius, color, alpha) { // Default values radius = radius || 15; color = color || 0x000000; alpha = alpha || 0.6; // Clear any existing children while (self.children.length > 0) { self.removeChildAt(0); } // Create center rectangle var center = self.attachAsset('rect', { width: width - radius * 2, height: height - radius * 2, color: color, shape: 'box', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); // Create horizontal rectangles (top and bottom) var top = self.attachAsset('rect', { width: width - radius * 2, height: radius, color: color, shape: 'box', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); top.y = -(height / 2 - radius / 2); var bottom = self.attachAsset('rect', { width: width - radius * 2, height: radius, color: color, shape: 'box', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); bottom.y = height / 2 - radius / 2; // Create vertical rectangles (left and right) var left = self.attachAsset('rect', { width: radius, height: height - radius * 2, color: color, shape: 'box', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); left.x = -(width / 2 - radius / 2); var right = self.attachAsset('rect', { width: radius, height: height - radius * 2, color: color, shape: 'box', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); right.x = width / 2 - radius / 2; // Create corner circles var topLeft = self.attachAsset('rect', { width: radius * 2, height: radius * 2, color: color, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); topLeft.x = -(width / 2 - radius); topLeft.y = -(height / 2 - radius); var topRight = self.attachAsset('rect', { width: radius * 2, height: radius * 2, color: color, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); topRight.x = width / 2 - radius; topRight.y = -(height / 2 - radius); var bottomLeft = self.attachAsset('rect', { width: radius * 2, height: radius * 2, color: color, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); bottomLeft.x = -(width / 2 - radius); bottomLeft.y = height / 2 - radius; var bottomRight = self.attachAsset('rect', { width: radius * 2, height: radius * 2, color: color, shape: 'ellipse', anchorX: 0.5, anchorY: 0.5, alpha: alpha }); bottomRight.x = width / 2 - radius; bottomRight.y = height / 2 - radius; return self; }; return self; }); // Define a class for star bullets var Star = Container.expand(function () { var self = Container.call(this); // Create a yellow star shape using the built-in shape feature var starGraphics = self.attachAsset('star', { width: 50, height: 50, color: 0xffff00, shape: 'box', anchorX: 0.5, anchorY: 0.5 }); // Create trailing effect container var trailContainer = new Container(); self.addChild(trailContainer); // Place it behind the star by removing and re-adding in correct order self.removeChild(trailContainer); self.removeChild(starGraphics); self.addChild(trailContainer); // Add trail container first (bottom layer) self.addChild(starGraphics); // Add star graphics last (top layer) self.speed = 10; self.speedX = 10; // Horizontal speed self.speedY = 0; // Vertical speed (default to 0 for straight shots) self.rotation = 0; // Current rotation self.rotationSpeed = 0.1; // Rotation speed for spinning stars // Store the last position for collision detection self.lastY = self.y; self.lastX = self.x; // Add a pulsing effect self.scaleDirection = 1; self.scaleSpeed = 0.02; self.minScale = 0.8; self.maxScale = 1.2; // Trail effect variables self.trailCount = 0; self.trailInterval = 2; // Create trail every n frames self.trailLifetime = 400; // Trail lifetime in ms self.maxTrails = 6; // Maximum number of trails // Function to create a gradient trail self.createTrail = function () { // Create a copy of the star graphic for the trail var trail = trailContainer.addChild(LK.getAsset('star', { width: 50, height: 50, color: 0xffff00, shape: 'box', anchorX: 0.5, anchorY: 0.5, alpha: 0.8, scaleX: starGraphics.scale.x * 0.9, scaleY: starGraphics.scale.y * 0.9, rotation: starGraphics.rotation })); // Position at current star position trail.x = 0; // Local coordinates relative to star trail.y = 0; // Animate the trail to fade out and scale down tween(trail, { alpha: 0, scaleX: trail.scale.x * 0.5, scaleY: trail.scale.y * 0.5 }, { duration: self.trailLifetime, easing: tween.easeOut, onFinish: function onFinish() { if (trail && trail.parent) { trail.parent.removeChild(trail); } } }); // Return trail for further manipulation return trail; }; // Add shooting animation self.animate = function () { // Initial rapid spinning animation tween(self, { rotation: Math.PI * 4 // Spin 720 degrees }, { duration: 800, easing: tween.easeOutQuad, onFinish: function onFinish() { // After initial spin, continue with normal rotation in update() self.rotation = Math.PI * 4 % (Math.PI * 2); // Keep within 0-360 range } }); // Flash effect with scaling tween(starGraphics, { scaleX: 1.5, scaleY: 1.5 }, { duration: 150, easing: tween.easeOutQuad, onFinish: function onFinish() { tween(starGraphics, { scaleX: 1, scaleY: 1 }, { duration: 150, easing: tween.easeInQuad }); } }); // Add color gradient animation to the star var colors = [0xffff00, 0xff9900, 0xff0000, 0xff00ff, 0x00ffff, 0xffff00]; var colorDuration = 600; function animateGradient(index) { if (index >= colors.length - 1) { return; } tween(starGraphics, { tint: colors[index + 1] }, { duration: colorDuration, easing: tween.easeInOut, onFinish: function onFinish() { animateGradient((index + 1) % (colors.length - 1)); } }); } // Start the gradient animation starGraphics.tint = colors[0]; animateGradient(0); }; // Call animation when created self.animate(); self.update = function () { // Store last position before update self.lastY = self.y; self.lastX = self.x; // Move the star according to its speed components self.x += self.speedX; self.y += self.speedY; // Rotate the star for visual effect self.rotation += self.rotationSpeed; starGraphics.rotation = self.rotation; // Pulse scaling effect if (self.scaleDirection > 0) { starGraphics.scale.x += self.scaleSpeed; starGraphics.scale.y += self.scaleSpeed; if (starGraphics.scale.x >= self.maxScale) { self.scaleDirection = -1; } } else { starGraphics.scale.x -= self.scaleSpeed; starGraphics.scale.y -= self.scaleSpeed; if (starGraphics.scale.x <= self.minScale) { self.scaleDirection = 1; } } // Create trail effect self.trailCount++; if (self.trailCount >= self.trailInterval) { self.trailCount = 0; // Only maintain a certain number of trails to avoid memory issues if (trailContainer.children.length < self.maxTrails) { self.createTrail(); } } // Remove if it goes off-screen if (self.x > 2048 + 50 || self.y < -50 || self.y > 2732 + 50) { self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue background }); /**** * Game Code ****/ // Ensure tween.cancelAll is available if (!tween.cancelAll) { tween.cancelAll = function (target) { // Find all active tweens affecting this target and cancel them var allTweens = []; // Get all tweens if (tween.getAll && typeof tween.getAll === 'function') { allTweens = tween.getAll(); } else { // Fallback implementation if getAll is not available if (tween._tweens && Array.isArray(tween._tweens)) { allTweens = tween._tweens; } } // Cancel tweens for target if (allTweens.length > 0) { allTweens.forEach(function (t) { if (t && t._target === target && typeof t.cancel === 'function') { t.cancel(); } }); } // If no direct way to access tweens, cancel by calling tween with duration 0 tween(target, {}, { duration: 0 }); }; } var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0 })); background.x = 0; background.y = 0; game.desertLevelActive = false; game.nightmareLevelActive = false; game.infernoLevelActive = false; game.levelTransition = false; // Track when we transition back to main level // Initialize explosions array var explosions = []; // Initialize player var player = game.addChild(new Player()); player.x = 2048 / 2; player.y = 2732 / 2; player.lives = 3; player.invulnerable = false; player.invulnerableTime = 0; player.hasPowerStar = false; player.powerStarTime = 0; player.originalTint = 0xFFFFFF; // Initialize enemies var enemies = []; var enemySpawnInterval = 100; var enemySpawnCounter = 0; // Initialize flying dinosaurs var flyingDinos = []; var dinoSpawnInterval = 200; var dinoSpawnCounter = 0; // Initialize collectible hearts var collectibleHearts = []; var heartSpawnInterval = 300; // Less frequent than enemies var heartSpawnCounter = 0; // Initialize power stars var powerStars = []; var powerStarSpawnInterval = 600; // Even less frequent than hearts var powerStarSpawnCounter = 0; // Create life display with proper heart images var maxHearts = 5; // Maximum number of hearts var heartsContainer = new Container(); LK.gui.top.addChild(heartsContainer); heartsContainer.x = 0; // Will be centered heartsContainer.y = 150; // Offset from top edge // Create heart icons array - we'll display both filled and empty hearts var heartIcons = []; // Function to update heart display function updateHeartDisplay() { // Remove existing hearts while (heartsContainer.children.length > 0) { heartsContainer.removeChildAt(0); } // Add heart icons for (var i = 0; i < player.lives; i++) { var heart = heartsContainer.addChild(LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 })); // Position hearts in a row, centered var heartSpacing = 130; var totalWidth = (player.lives - 1) * heartSpacing; heart.x = -totalWidth / 2 + i * heartSpacing; heartIcons[i] = heart; } } // Helper function to update text and resize its container function updateTextWithResize(textObj, container, bg, newText, minWidth) { textObj.setText(newText); if (bg && container) { // Resize background based on updated text width bg.width = Math.max(minWidth, textObj.width + 80); } } // Initialize heart display updateHeartDisplay(); // Create a container for score to control background size var scoreContainer = new Container(); LK.gui.top.addChild(scoreContainer); scoreContainer.x = 2048 / 2; scoreContainer.y = 50; // Create score background using rounded rectangle var scoreBg = new RoundedRect(); scoreContainer.addChild(scoreBg); scoreBg.create(250, 120, 20, 0x000000, 0.5); // Create a new Text2 object to display the score var scoreText = new Text2('0', { size: 100, fill: 0xFFFFFF, background: 'none' }); // Add the score text to the container scoreText.anchor.set(0.5, 0.5); scoreContainer.addChild(scoreText); // Create points container with background var pointsContainer = new Container(); LK.gui.topRight.addChild(pointsContainer); pointsContainer.x = -20; pointsContainer.y = 50; // Create points background with rounded rectangle var pointsBg = new RoundedRect(); pointsContainer.addChild(pointsBg); pointsBg.create(320, 100, 15, 0x000000, 0.5); // Adjust position since RoundedRect has center anchor pointsBg.x = -160; pointsBg.y = 0; // Create points counter and display var points = 0; var pointsText = new Text2('Points: 0', { size: 80, fill: 0xFFFFFF, background: 'none' }); pointsText.anchor.set(1, 0.5); pointsContainer.addChild(pointsText); // Create instruction text with rounded rectangle background var instructionContainer = new Container(); LK.gui.center.addChild(instructionContainer); instructionContainer.y = 300; // Create temporary text to measure its dimensions var tempText = new Text2('TAP to JUMP\nHOLD to continuously SHOOT', { size: 60, fill: 0xFFFFFF, align: 'center', background: 'none' }); // Get text width and add padding var textWidth = tempText.width + 100; var textHeight = tempText.height + 80; // Create instruction text with more detailed controls including double jump var instructionText = new Text2('TAP to JUMP\nTAP while JUMPING for DOUBLE JUMP\nHOLD to continuously SHOOT', { size: 60, fill: 0xFFFFFF, align: 'center', background: 'none', shadow: { offsetX: 5, offsetY: 5, color: 0x000000, blur: 15, alpha: 1.0 } }); instructionText.anchor.set(0.5, 0.5); instructionContainer.addChild(instructionText); // Create a timeout to hide instructions after 6 seconds (reduced by 2 seconds) LK.setTimeout(function () { // Fade out instructions var alpha = 1; var fadeInterval = LK.setInterval(function () { alpha -= 0.05; // Slower fade for better readability instructionContainer.alpha = alpha; // Fade the entire container including background if (alpha <= 0) { LK.clearInterval(fadeInterval); instructionContainer.visible = false; } }, 100); }, 6000); // Handle game updates // Array to store bullets var stars = []; var shootTimer = 0; game.update = function () { player.update(); // Handle player invulnerability from damage if (player.invulnerable && !player.hasPowerStar) { // Make player flash to indicate invulnerability player.alpha = Math.sin(Date.now() * 0.01) * 0.5 + 0.5; // End invulnerability after 2 seconds if (Date.now() - player.invulnerableTime > 2000) { player.invulnerable = false; player.alpha = 1; } } // Handle power star invincibility if (player.hasPowerStar) { // Make player glow with neon colors var elapsed = Date.now() - player.powerStarTime; var colorIndex = Math.floor(elapsed / 300 % 4); var neonColors = [0x00FFFF, 0xFF00FF, 0xFFFF00, 0x00FF00]; player.children[0].tint = neonColors[colorIndex]; // Player is invulnerable during power star effect player.invulnerable = true; player.alpha = 1; // End power star effect after 10 seconds instead of 20 if (elapsed > 10000) { player.hasPowerStar = false; player.invulnerable = false; // Restore original appearance player.children[0].tint = player.originalTint; // Show power star end indicator showActionIndicator("POWER LOST", 1000); } } // Removed auto-shooting to only allow double tap shooting // Update stars for (var i = stars.length - 1; i >= 0; i--) { stars[i].update(); // Remove stars that are destroyed if (!stars[i].parent) { stars.splice(i, 1); continue; } // Check for star-enemy collisions for (var j = enemies.length - 1; j >= 0; j--) { if (stars[i] && enemies[j] && stars[i].intersects(enemies[j])) { // Create explosion effect before destroying the enemy var explosion = new Explosion(); explosion.x = enemies[j].x; explosion.y = enemies[j].y; explosion.createParticles(enemies[j].tint || 0xFFAA00); game.addChild(explosion); explosions.push(explosion); // Remove the enemy and the star enemies[j].destroy(); enemies.splice(j, 1); stars[i].destroy(); stars.splice(i, 1); // Update score LK.setScore(points + 10); // Update score with container resize updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 10).toString(), 250); // Update points points += 10; // Update points with container resize updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320); // Nothing here - removing this redundant check as it's handled elsewhere // Check if player reached 1000 points to win if (points >= 1000) { // Ensure the score is properly set before showing win screen var finalScore = points; LK.setScore(points); scoreText.setText(points.toString()); // Resize score background based on updated text width scoreBg.width = Math.max(250, scoreText.width + 80); pointsText.setText('Points: ' + points); // Resize points background based on updated text width pointsBg.width = Math.max(320, pointsText.width + 80); LK.showYouWin(); } else if (points >= 600 && !game.levelTransition) { // Return to main level after 600 points game.levelTransition = true; game.desertLevelActive = false; game.nightmareLevelActive = false; game.infernoLevelActive = false; // No color transition when returning to main level } // Check if we just passed 150 points for the enemy increase announcement if (points - 10 < 200 && points >= 150) { showActionIndicator("MORE ENEMIES!", 2000); } // Check level changes - use a single consistent approach if (points >= 500 && !game.infernoLevelActive) { // After 500 points, transition to inferno level game.infernoLevelActive = true; game.nightmareLevelActive = false; game.desertLevelActive = false; // No color transition for inferno level } else if (points >= 300 && !game.nightmareLevelActive && !game.infernoLevelActive) { game.nightmareLevelActive = true; // No color transition for nightmare level } else if (points >= 100 && !game.desertLevelActive && !game.nightmareLevelActive && !game.infernoLevelActive) { game.desertLevelActive = true; // No color transition for desert level } break; } } } // Hearts are now managed by updateHeartDisplay function // Update collectible hearts for (var i = collectibleHearts.length - 1; i >= 0; i--) { collectibleHearts[i].update(); // Remove hearts that are destroyed if (!collectibleHearts[i].parent) { collectibleHearts.splice(i, 1); continue; } // Check for player-heart collisions if (player.intersects(collectibleHearts[i])) { // Player collected a heart collectibleHearts[i].destroy(); collectibleHearts.splice(i, 1); // Add a life if not at maximum if (player.lives < 5) { player.lives++; // Show heart collection indicator showActionIndicator("+1 LIFE!", 1000); // Update heart display updateHeartDisplay(); // Show special indicators at different milestone points if (points >= 900 && points < 1000) { showActionIndicator("ALMOST THERE! " + (1000 - points) + " TO WIN!", 1000); } else if (points >= 550 && points < 600) { showActionIndicator("LEVEL CHANGE AT 600 POINTS!", 1000); } // Get the newest heart for animation var newHeartIndex = player.lives - 1; var heart = heartIcons[newHeartIndex]; // Show animation effect tween(heart, { scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(heart, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeIn }); } }); } } } // Spawn enemies enemySpawnCounter++; if (enemySpawnCounter >= enemySpawnInterval) { var enemy = new Enemy(); enemy.x = 2048; enemy.y = 2732 / 2; // Make enemies faster and with different colors based on level if (game.infernoLevelActive) { enemy.speed = 12; // Even faster enemies in inferno level enemy.tint = 0xFF3300; // Orange-red tint for inferno enemies // Spawn duplicate enemies in inferno level - guaranteed 1-2 extras var extraCount = 1 + Math.floor(Math.random() * 2); // 1 or 2 extra enemies for (var i = 0; i < extraCount; i++) { var extraEnemy = new Enemy(); extraEnemy.x = 2048; // Position enemies at different vertical positions extraEnemy.y = 2732 / 2 + (Math.random() > 0.5 ? 150 + Math.random() * 100 : -(150 + Math.random() * 100)); // Varied positions extraEnemy.speed = 10 + Math.random() * 4; // Varied speeds extraEnemy.tint = 0xFF6600; enemies.push(extraEnemy); game.addChild(extraEnemy); } } else if (game.nightmareLevelActive) { enemy.speed = 8; // Faster enemies in nightmare level enemy.tint = 0xFF0000; // Red tint for nightmare enemies } else if (points >= 150 && game.desertLevelActive) { // More enemies after 150 points in desert level enemy.speed = 6; enemy.tint = 0xCC9900; // Darker desert tint // Occasionally spawn a second enemy after 150 points if (Math.random() < 0.3) { var extraEnemy = new Enemy(); extraEnemy.x = 2048; extraEnemy.y = 2732 / 2 + (Math.random() > 0.5 ? 150 : -150); // Above or below main path extraEnemy.speed = 5; extraEnemy.tint = 0xCCAA00; enemies.push(extraEnemy); game.addChild(extraEnemy); } } enemies.push(enemy); game.addChild(enemy); // Randomize the spawn interval for the next enemy based on level var interval, minInterval; if (game.infernoLevelActive) { interval = 80; // Even more frequent spawns in inferno minInterval = 20; } else if (game.nightmareLevelActive) { interval = 100; minInterval = 30; } else if (points >= 150) { interval = 120; // More frequent after 150 points minInterval = 40; } else { interval = 150; minInterval = 50; } enemySpawnInterval = Math.floor(Math.random() * interval) + minInterval; enemySpawnCounter = 0; } // Spawn flying dinosaurs dinoSpawnCounter++; if (dinoSpawnCounter >= dinoSpawnInterval) { var dino = new FlyingDino(); dino.x = 2048; dino.y = Math.random() * 600 + 200; // Spawn in upper portion of screen dino.startY = dino.y; // Store starting Y position // Make dinosaurs faster and more aggressive based on level if (game.infernoLevelActive) { dino.speed = 15; // Even faster in inferno level dino.verticalSpeed = 5; dino.tint = 0xFF3300; // Orange-red tint for inferno dinos } else if (game.nightmareLevelActive) { dino.speed = 12; dino.verticalSpeed = 4; dino.tint = 0xFF00FF; // Purple tint for nightmare dinos } else if (game.desertLevelActive) { dino.speed = 10; dino.verticalSpeed = 3.5; dino.tint = 0xDDA020; // Golden tint for desert dinos } flyingDinos.push(dino); game.addChild(dino); // Randomize spawn interval based on game level var interval, minInterval; if (game.infernoLevelActive) { interval = 150; minInterval = 50; } else if (game.nightmareLevelActive) { interval = 200; minInterval = 100; } else { interval = 250; minInterval = 150; } dinoSpawnInterval = Math.floor(Math.random() * interval) + minInterval; dinoSpawnCounter = 0; } // Spawn collectible hearts heartSpawnCounter++; if (heartSpawnCounter >= heartSpawnInterval) { var heart = new Heart(); heart.x = 2048; heart.y = Math.random() * 600 + 200; // Only spawn in upper third of screen heart.baseY = heart.y; collectibleHearts.push(heart); game.addChild(heart); // Randomize the spawn interval for the next heart (less frequent than enemies) heartSpawnInterval = Math.floor(Math.random() * 300) + 300; heartSpawnCounter = 0; } // Spawn power stars powerStarSpawnCounter++; if (powerStarSpawnCounter >= powerStarSpawnInterval) { var powerStar = new PowerStar(); powerStar.x = 2048; powerStar.y = Math.random() * 600 + 200; // Only spawn in upper third of screen powerStar.baseY = powerStar.y; powerStars.push(powerStar); game.addChild(powerStar); // Randomize the spawn interval for the next power star (less frequent than hearts) powerStarSpawnInterval = Math.floor(Math.random() * 400) + 600; powerStarSpawnCounter = 0; } // Update explosions for (var i = explosions.length - 1; i >= 0; i--) { explosions[i].update(); // Remove explosions that are destroyed if (!explosions[i].parent) { explosions.splice(i, 1); } } // Update power stars for (var i = powerStars.length - 1; i >= 0; i--) { powerStars[i].update(); // Remove power stars that are destroyed if (!powerStars[i].parent) { powerStars.splice(i, 1); continue; } // Check for player-power star collisions if (player.intersects(powerStars[i])) { // Player collected a power star powerStars[i].destroy(); powerStars.splice(i, 1); // Activate power star effect player.hasPowerStar = true; player.powerStarTime = Date.now(); // Apply neon glow effect to player var neonColors = [0x00FFFF, 0xFF00FF, 0xFFFF00, 0x00FF00]; player.originalTint = player.children[0].tint; tween(player.children[0], { tint: neonColors[0] }, { duration: 500 }); } } // Update flying dinosaurs for (var d = flyingDinos.length - 1; d >= 0; d--) { flyingDinos[d].update(); // Check if dinosaur intersects with player if (player.intersects(flyingDinos[d])) { if (player.hasPowerStar) { // Create explosion effect before destroying the dinosaur var explosion = new Explosion(); explosion.x = flyingDinos[d].x; explosion.y = flyingDinos[d].y; explosion.createParticles(flyingDinos[d].tint || 0xFF00FF); game.addChild(explosion); explosions.push(explosion); explosions.push(explosion); // With power star, destroy dinosaurs on contact flyingDinos[d].destroy(); flyingDinos.splice(d, 1); // Increase score - more points for dinosaurs LK.setScore(points + 30); // More points for dinosaur during power star // Update score with container resize updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 30).toString(), 250); points += 30; // Update points with container resize updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320); // Check if player reached 1000 points to win if (points >= 1000) { var finalScore = points; LK.setScore(points); scoreText.setText(points.toString()); pointsText.setText('Points: ' + points); LK.showYouWin(); } // Check level changes if (points >= 300 && game.nightmareLevelActive && !game.infernoLevelActive) { game.infernoLevelActive = true; // No color transition for inferno level showActionIndicator("INFERNO LEVEL!", 4000); } else if (points >= 200 && game.desertLevelActive && !game.nightmareLevelActive) { game.nightmareLevelActive = true; // No color transition for nightmare level showActionIndicator("NIGHTMARE LEVEL!", 4000); } else if (points >= 100 && !game.desertLevelActive) { game.desertLevelActive = true; // No color transition for desert level showActionIndicator("DESERT LEVEL!", 4000); } } else if (player.y < flyingDinos[d].y - 50) { // Player is above dinosaur - jumped on it! // Create explosion effect var explosion = new Explosion(); explosion.x = flyingDinos[d].x; explosion.y = flyingDinos[d].y; explosion.createParticles(flyingDinos[d].tint || 0xFF00FF); game.addChild(explosion); explosions.push(explosion); flyingDinos[d].destroy(); flyingDinos.splice(d, 1); // Boost player up slightly after jump (small bounce) tween.cancelAll(player); player.jump(); // Increase score LK.setScore(points + 20); // More points for jumping on dinosaur // Update score with container resize updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 20).toString(), 250); points += 20; // Update points with container resize updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320); // Show feedback showActionIndicator("DINO STOMP!", 1000); // Check win condition if (points >= 1000) { var finalScore = points; LK.setScore(points); scoreText.setText(points.toString()); pointsText.setText('Points: ' + points); LK.showYouWin(); } else if (points >= 600 && !game.levelTransition) { // After 600 points, transition back to main level game.levelTransition = true; game.desertLevelActive = false; game.nightmareLevelActive = false; game.infernoLevelActive = false; // No color transition when returning to main level } } else if (points >= 600 && !game.levelTransition) { // Return to main level after 600 points game.levelTransition = true; game.desertLevelActive = false; game.nightmareLevelActive = false; game.infernoLevelActive = false; // No color transition when returning to main level } else if (!player.invulnerable) { // Player was hit, lose a life player.lives--; // Play hit sound LK.getSound('hit').play(); // Flash screen to indicate damage LK.effects.flashScreen(0xff0000, 500); // Make player temporarily invulnerable player.invulnerable = true; player.invulnerableTime = Date.now(); // Update hearts display updateHeartDisplay(); // Check if player is out of lives if (player.lives <= 0) { LK.showGameOver(); } else { // Destroy the dinosaur that hit the player flyingDinos[d].destroy(); flyingDinos.splice(d, 1); } } } else if (player.x > flyingDinos[d].x && !flyingDinos[d].passed) { flyingDinos[d].passed = true; // Update points for passing dinosaur points += 15; // More points for passing flying dinosaur LK.setScore(points); // Update score with container resize updateTextWithResize(scoreText, scoreContainer, scoreBg, points.toString(), 250); // Update points with container resize updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320); } // Check for star-dinosaur collisions for (var s = stars.length - 1; s >= 0; s--) { if (stars[s] && flyingDinos[d] && stars[s].intersects(flyingDinos[d])) { // Create explosion effect before destroying the dinosaur var explosion = new Explosion(); explosion.x = flyingDinos[d].x; explosion.y = flyingDinos[d].y; explosion.createParticles(flyingDinos[d].tint || 0xFF00FF); game.addChild(explosion); // Remove the dinosaur and the star flyingDinos[d].destroy(); flyingDinos.splice(d, 1); stars[s].destroy(); stars.splice(s, 1); // Increase score LK.setScore(points + 15); // More points for shooting dinosaur // Update score with container resize updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 15).toString(), 250); points += 15; // Update points with container resize updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320); // Check if player reached 1000 points to win if (points >= 1000) { var finalScore = points; LK.setScore(points); scoreText.setText(points.toString()); pointsText.setText('Points: ' + points); LK.showYouWin(); } else if (points >= 600 && !game.levelTransition) { // Return to main level after 600 points game.levelTransition = true; game.desertLevelActive = false; game.nightmareLevelActive = false; game.infernoLevelActive = false; // No color transition when returning to main level } // Check level changes if (points >= 300 && game.nightmareLevelActive && !game.infernoLevelActive) { game.infernoLevelActive = true; // No color transition for inferno level } else if (points >= 200 && game.desertLevelActive && !game.nightmareLevelActive) { game.nightmareLevelActive = true; // No color transition for nightmare level } else if (points >= 100 && !game.desertLevelActive) { game.desertLevelActive = true; // No color transition for desert level } break; } } } // Update enemies for (var j = enemies.length - 1; j >= 0; j--) { enemies[j].update(); if (player.intersects(enemies[j])) { if (player.hasPowerStar) { // Create explosion effect before destroying the enemy var explosion = new Explosion(); explosion.x = enemies[j].x; explosion.y = enemies[j].y; explosion.createParticles(enemies[j].tint || 0xFFFF00); game.addChild(explosion); explosions.push(explosion); // With power star, destroy enemies on contact enemies[j].destroy(); enemies.splice(j, 1); // Increase score LK.setScore(points + 20); // Double points during power star // Update score with container resize updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 20).toString(), 250); // Increase points - doubled from 10 to 20 points += 20; // Update points with container resize updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320); // Check if player reached 1000 points to win if (points >= 1000) { // Ensure the score is properly set before showing win screen var finalScore = points; LK.setScore(points); scoreText.setText(points.toString()); pointsText.setText('Points: ' + points); LK.showYouWin(); } else if (points >= 600 && !game.levelTransition) { // Return to main level after 600 points game.levelTransition = true; game.desertLevelActive = false; game.nightmareLevelActive = false; game.infernoLevelActive = false; // No color transition when returning to main level } // Check if we need to change level // We already handled win condition above, no need for duplicate check // Check if we need to change level if (points >= 300 && game.nightmareLevelActive && !game.infernoLevelActive) { game.infernoLevelActive = true; game.nightmareLevelActive = false; game.desertLevelActive = false; // No color transition for inferno level } else if (points >= 200 && game.desertLevelActive && !game.nightmareLevelActive) { game.nightmareLevelActive = true; // No color transition for nightmare level } else if (points >= 100 && !game.desertLevelActive) { game.desertLevelActive = true; // No color transition for desert level } } else if (!player.invulnerable) { // Player was hit, lose a life player.lives--; // Play hit sound LK.getSound('hit').play(); // Flash screen to indicate damage LK.effects.flashScreen(0xff0000, 500); // Make player temporarily invulnerable player.invulnerable = true; player.invulnerableTime = Date.now(); // Update hearts display updateHeartDisplay(); // Check if player is out of lives if (player.lives <= 0) { LK.showGameOver(); } else { // Destroy the enemy that hit the player enemies[j].destroy(); enemies.splice(j, 1); } } } else if (player.x > enemies[j].x && !enemies[j].passed) { enemies[j].passed = true; // Update points for passing enemy points += 10; LK.setScore(points); // Update score with container resize updateTextWithResize(scoreText, scoreContainer, scoreBg, points.toString(), 250); // Update points with container resize updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320); } } }; // Handle player interaction var dragNode = null; var actionIndicator = null; // Create visual indicators for player actions function showActionIndicator(text, duration) { // Remove any existing indicator, except for level announcements var isLevelAnnouncement = text.includes("LEVEL"); if (actionIndicator && actionIndicator.parent && !isLevelAnnouncement) { actionIndicator.parent.removeChild(actionIndicator); } // Create new indicator actionIndicator = new Container(); game.addChild(actionIndicator); // Create temporary text to measure its dimensions var tempText = new Text2(text, { size: isLevelAnnouncement ? 100 : 50, fill: 0xFFFFFF, align: 'center', background: 'none' }); // Get text width and add padding var textWidth = tempText.width + (isLevelAnnouncement ? 120 : 80); var textHeight = tempText.height + (isLevelAnnouncement ? 50 : 30); // Set minimum width/height var textSize = isLevelAnnouncement ? 100 : 50; // Add text var txt = new Text2(text, { size: textSize, fill: isLevelAnnouncement ? 0xFFFF00 : 0xFFFFFF, align: 'center', background: 'none' }); txt.anchor.set(0.5, 0.5); actionIndicator.addChild(txt); // Position differently based on type if (isLevelAnnouncement) { // Position at center of the screen for level announcements actionIndicator.x = 2048 / 2; // Center horizontally actionIndicator.y = 2732 / 2; // Center vertically // Add pulsing animation for level announcements tween(txt.scale, { x: 1.2, y: 1.2 }, { duration: 500, repeat: -1, yoyo: true, easing: tween.easeInOut }); } else { // Position above player for regular announcements actionIndicator.x = player.x; actionIndicator.y = player.y - 150; } // Fade out after duration - shorter for level announcements var displayDuration = isLevelAnnouncement ? 2000 : duration; // Apply initial scaling effect for level announcements if (isLevelAnnouncement) { actionIndicator.scale.x = 0.1; actionIndicator.scale.y = 0.1; tween(actionIndicator.scale, { x: 1, y: 1 }, { duration: 500, easing: tween.easeOutBack }); } // Set timeout for fade-out LK.setTimeout(function () { // Cancel any existing tweens on text scaling if (isLevelAnnouncement) { tween.cancelAll(txt.scale); } // Fade out and move up tween(actionIndicator, { alpha: 0, y: isLevelAnnouncement ? actionIndicator.y - 100 : actionIndicator.y - 50 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { if (actionIndicator && actionIndicator.parent) { actionIndicator.parent.removeChild(actionIndicator); } } }); }, displayDuration - 1000); } // Function to handle movement and shooting while holding function handleMove(x, y, obj) { if (dragNode) { // Only move player horizontally when dragging // Limit movement to keep player on screen var minX = 100; // Minimum x-coordinate var maxX = 2048 - 100; // Maximum x-coordinate dragNode.x = Math.max(minX, Math.min(x, maxX)); // Keep the player at the same vertical position if not jumping // (but don't override the tween animation when jumping) if (!player.isJumping) { dragNode.y = 2732 / 2; } // If holding finger on screen, continuously shoot if (shootTimer > 0) { // Reduced fire interval based on power star status var fireInterval = player.hasPowerStar ? 5 : 10; // Only shoot every interval ticks to control fire rate if (LK.ticks % fireInterval === 0) { // Create main star var star = new Star(); star.x = player.x + 100; star.y = player.y; stars.push(star); game.addChild(star); // If player has power star, create spread shots if (player.hasPowerStar) { // Add two additional stars with slight angle variations for (var i = -1; i <= 1; i += 2) { var spreadStar = new Star(); spreadStar.x = player.x + 100; spreadStar.y = player.y; // Apply small angular deviation var angle = i * 0.2; // ~11 degrees up/down spreadStar.speedY = Math.sin(angle) * 5; spreadStar.speedX = Math.cos(angle) * spreadStar.speed; // Override default update method for angled stars spreadStar.update = function () { this.lastY = this.y; this.lastX = this.x; this.x += this.speedX; this.y += this.speedY; if (this.x > 2048 + 50) { this.destroy(); } }; stars.push(spreadStar); game.addChild(spreadStar); } } // Play shooting sound LK.getSound('shoot').play(); } } } } // shootTimer already declared earlier in the code // Start tracking player movement on touch down and shoot on hold game.down = function (x, y, obj) { // Always make the player jump on tap player.jump(); // Start shooting - fire immediately when pressing shootTimer = Date.now(); // Fire initial shot burst var initialStar = new Star(); initialStar.x = player.x + 100; initialStar.y = player.y; stars.push(initialStar); game.addChild(initialStar); LK.getSound('shoot').play(); // If player has power star, add enhanced initial burst if (player.hasPowerStar) { // Create a burst of 3 stars in a spread pattern for (var angle = -0.3; angle <= 0.3; angle += 0.3) { if (angle === 0) { continue; } // Skip center (already created) var spreadStar = new Star(); spreadStar.x = player.x + 100; spreadStar.y = player.y; spreadStar.speedY = Math.sin(angle) * 8; spreadStar.speedX = Math.cos(angle) * spreadStar.speed; spreadStar.rotationSpeed = 0.15; // Faster rotation stars.push(spreadStar); game.addChild(spreadStar); } } // Handle regular movement without repositioning the player at tap position dragNode = player; }; // Update player position during movement game.move = handleMove; // Stop tracking player movement and shooting on touch up game.up = function (x, y, obj) { dragNode = null; shootTimer = 0; // Reset shoot timer };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Define a class for enemies
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.update = function () {
self.x -= self.speed;
if (self.x < -50) {
self.destroy();
}
};
});
// Define a class for explosion effects when enemies die
var Explosion = Container.expand(function () {
var self = Container.call(this);
// Track explosion duration
self.lifetime = 0;
self.maxLifetime = 60; // reduced frames duration for shorter lasting neon glow effects
// Create particles for the explosion
self.createParticles = function (color) {
// Use intense neon red colors exclusively for ultra-vibrant glow effect
var particleColor = 0xFF0000; // Pure red base color
// Ultra-vibrant neon red color palette with intense red variations for maximum glow effect
var neonColors = [0xFF0000, 0xFF1100, 0xFF0500, 0xFF2200, 0xFF3333, 0xFF0033, 0xFF2211, 0xFF1111, 0xFF4444, 0xFF0022];
var colorVariations = [0xFF0000,
// Pure red
0xFF2222,
// Lighter red
0xFF3333,
// Even brighter red
0xFFAAAA,
// Intense bright red for highlights
neonColors[Math.floor(Math.random() * neonColors.length)] // Add random neon red variation
];
// Create more particles for enhanced effect - reduced size for smaller explosions
for (var i = 0; i < 35; i++) {
// Reduced from 50 to 35 particles for smaller effect
var size = Math.random() * 20 + 10; // Smaller particles for less dramatic impact
// Use only circular particles with neon red glow for better visual effect
var useNeon = Math.random() < 0.9; // 90% chance for neon particles
var particleColor = useNeon ? neonColors[Math.floor(Math.random() * neonColors.length)] : colorVariations[Math.floor(Math.random() * colorVariations.length)];
var particle = self.attachAsset('rect', {
width: size,
height: size,
color: particleColor,
shape: 'ellipse',
// Always use circles for more consistent red neon glow effect
anchorX: 0.5,
anchorY: 0.5,
alpha: useNeon ? 0.99 : 0.9 // Even higher alpha for neon particles to enhance glow
});
// Randomize particle position, direction and speed
var angle = Math.random() * Math.PI * 2;
var distance = Math.random() * 30; // Smaller initial spread
particle.x = Math.cos(angle) * distance;
particle.y = Math.sin(angle) * distance;
// Store particle speed and direction with more variation
particle.speedX = Math.cos(angle) * (Math.random() * 6 + 3); // Slower movement
particle.speedY = Math.sin(angle) * (Math.random() * 6 + 3);
particle.rotSpeed = (Math.random() - 0.5) * 0.8; // Less rotation
particle.isNeon = useNeon; // Track if this is a neon particle
// Set initial scale and rotation for more dynamic look
particle.scaleX = 0.1;
particle.scaleY = 0.1;
particle.rotation = Math.random() * Math.PI * 2; // Random initial rotation
// Animate particle with smaller neon red glow effects
tween(particle, {
scaleX: useNeon ? 2.0 : 1.0,
// Smaller scale for neon particles to create less intense glow
//{m} // Smaller scale for less dramatic visual
scaleY: useNeon ? 2.0 : 1.0,
rotation: particle.rotation + Math.PI * (Math.random() * 2),
// Add rotation animation
tint: useNeon ? neonColors[Math.floor(Math.random() * neonColors.length)] : particleColor // Add color transitions
}, {
duration: useNeon ? 800 : 300,
// Shorter animation duration for neon particles for less dramatic effect
//{o} // Shorter animation for smaller visual
easing: tween.easeOutBack,
onFinish: function (p) {
return function () {
// Enhanced neon particle fade-out for lingering glow effect
var endScale = p.isNeon ? 0.6 : Math.random() < 0.5 ? 0 : 0.2;
var endAlpha = p.isNeon ? 0.4 : endScale > 0 ? 0.1 : 0;
var endRotation = p.rotation + Math.PI * (Math.random() + 1);
tween(p, {
scaleX: endScale,
scaleY: endScale,
alpha: endAlpha,
rotation: endRotation
}, {
duration: p.isNeon ? 1400 : 600,
// Much longer fade for neon particles for extended glow effect
//{q} // Longer fade for better visual
easing: tween.easeInOutQuad,
onFinish: function onFinish() {
if (p.parent) {
p.parent.removeChild(p);
}
}
});
};
}(particle)
});
}
// Add multiple shockwave effects (smaller)
for (var i = 0; i < 2; i++) {
var shockwaveColor = i === 0 ? 0xFFFFFF : neonColors[Math.floor(Math.random() * neonColors.length)];
var shockwave = self.attachAsset('rect', {
width: 8,
height: 8,
color: shockwaveColor,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: i === 0 ? 0.7 : 0.5
});
// Animate shockwave expanding and fading with delay for sequential effect
tween(shockwave, {
scaleX: 8 + i * 3,
// Each shockwave is smaller
scaleY: 8 + i * 3,
alpha: 0
}, {
duration: 500 + i * 100,
//{D} // Each shockwave lasts shorter
delay: i * 100,
// Stagger the shockwaves
easing: tween.easeOutQuad
});
}
// Add central glow effect (smaller)
var glow = self.attachAsset('rect', {
width: 15,
height: 15,
color: neonColors[Math.floor(Math.random() * neonColors.length)],
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
// Animate central glow (smaller scale)
tween(glow, {
scaleX: 4,
scaleY: 4,
alpha: 0
}, {
duration: 400,
easing: tween.easeOutQuad
});
};
// Update explosion particles
self.update = function () {
self.lifetime++;
// Update each particle
for (var i = 0; i < self.children.length; i++) {
var particle = self.children[i];
if (particle.speedX !== undefined) {
particle.x += particle.speedX;
particle.y += particle.speedY;
// Add gravity effect to particles
particle.speedY += 0.1;
// Slow down particles over time with variable drag
particle.speedX *= 0.94 + Math.random() * 0.02;
particle.speedY *= 0.94 + Math.random() * 0.02;
// Add ultra-enhanced neon glow pulse effect to neon particles
if (particle.isNeon) {
// Dramatically enhanced pulse effect for neon particles
var pulseRate = 0.1; // Even faster pulse rate for more dynamic effect
var pulseMagnitude = 0.4; // More dramatic pulse intensity
var pulseOffset = self.lifetime * pulseRate + i * 0.15;
var pulseValue = Math.sin(pulseOffset) * pulseMagnitude;
// Apply much stronger pulse to alpha and scale for intense glow effect
particle.alpha = Math.max(0.3, Math.min(0.98, 0.8 + pulseValue));
var baseScale = 1.2 + self.lifetime / self.maxLifetime * -0.3; // Even slower shrink rate for longer-lasting glow
particle.scale.x = baseScale + pulseValue * 0.6; // Much stronger scale pulse
particle.scale.y = baseScale + pulseValue * 0.6;
// More frequent color changes for enhanced twinkling effect (neon particles only)
if (Math.random() < 0.18) {
// 18% chance each frame (increased from 15%)
// Create ultra-intense neon red color variations for maximum sparkle effect
var neonColors = [0xFF0000, 0xFF1100, 0xFF0500, 0xFF2200, 0xFF2222, 0xFF0033, 0xFF1111, 0xFF2211, 0xFF3311, 0xFF4422];
particle.tint = neonColors[Math.floor(Math.random() * neonColors.length)];
// Use tweening for smoother color transitions with enhanced glow effect
tween(particle, {
alpha: Math.random() * 0.3 + 0.7,
// Random alpha between 0.7 and 1.0 for pulsing effect
scaleX: particle.scaleX * (Math.random() * 0.4 + 0.9),
// Add slight scale pulse
scaleY: particle.scaleY * (Math.random() * 0.4 + 0.9) // Add slight scale pulse
}, {
duration: 180,
easing: tween.easeOut
});
}
}
// Rotate particles with variable speeds
if (particle.rotSpeed) {
particle.rotation += particle.rotSpeed;
// Slightly vary rotation speed for more natural look
particle.rotSpeed *= 0.97; // Slower deceleration for longer rotation
}
// Add more dynamic wobble to particle movement
if (Math.random() < 0.15) {
// Increased chance for movement variation
particle.x += (Math.random() - 0.5) * 3; // Stronger wobble
particle.y += (Math.random() - 0.5) * 3;
}
}
}
// Random chance to emit a secondary spark or neon trail particle (reduced size)
if (self.lifetime < self.maxLifetime * 0.5 && Math.random() < 0.2) {
// Reduced chance (20% down from 28%) for less frequent and shorter-lasting effects
// Ultra-intense neon red colors for maximum spark brilliance and glow
var neonColors = [0xFF0000, 0xFF1100, 0xFF0500, 0xFF2200, 0xFF2222, 0xFF1111, 0xFF0011, 0xFF3300, 0xFF4400];
var useNeon = Math.random() < 0.95; // 95% chance of neon particle
var spark = self.attachAsset('rect', {
width: useNeon ? 8 : 5,
// Smaller particles for less visible glow
height: useNeon ? 8 : 5,
// Smaller particles for less visible glow
color: useNeon ? neonColors[Math.floor(Math.random() * neonColors.length)] : 0xFF5555,
// Use light red instead of white
shape: 'ellipse',
// Always use circles for enhanced red neon glow effect
anchorX: 0.5,
anchorY: 0.5,
alpha: useNeon ? 0.95 : 0.8 // Slightly reduced alpha for less intense visibility
});
// Position spark near an existing particle for trail effect
var particleIndex = Math.floor(Math.random() * self.children.length);
if (particleIndex < self.children.length && self.children[particleIndex].speedX !== undefined) {
// Position near existing particle
spark.x = self.children[particleIndex].x + (Math.random() - 0.5) * 10;
spark.y = self.children[particleIndex].y + (Math.random() - 0.5) * 10;
} else {
// Fallback random position
spark.x = (Math.random() - 0.5) * 40;
spark.y = (Math.random() - 0.5) * 40;
}
// Animate spark with neon glow effect if it's a neon particle
if (useNeon) {
tween(spark, {
alpha: 0.2,
scaleX: 2,
// Grow before fading
scaleY: 2
}, {
duration: 200,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
tween(spark, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 400,
easing: tween.easeInQuad
});
}
});
} else {
// Regular spark animation
tween(spark, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 300,
easing: tween.easeOutQuad
});
}
}
// Add less frequent energy burst effect during explosion lifetime - reduced frequency and size
if (self.lifetime > 3 && self.lifetime < self.maxLifetime * 0.6 && Math.random() < 0.08) {
// Reduced chance (8% down from 15%) for less frequent bursts and shorter visual duration
var neonColors = [0xFF0000, 0xFF0500, 0xFF1100, 0xFF2200, 0xFF3300, 0xFF1111, 0xFF2211, 0xFF3311, 0xFF4411, 0xFF0022];
// Always use the most intense neon red colors for maximum visual impact
var burstColor = neonColors[Math.floor(Math.random() * neonColors.length)];
var energyBurst = self.attachAsset('rect', {
width: 15,
// Smaller burst size
height: 15,
color: burstColor,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.95
});
// Create less dramatic expanding ring effect
tween(energyBurst, {
scaleX: 5,
//{1B} // Smaller final scale
scaleY: 5,
alpha: 0
}, {
duration: 500,
//{1F} // Shorter duration
easing: tween.easeOutQuad
});
// Add a second smaller, faster burst for layered effect (reduced frequency)
if (Math.random() < 0.5) {
// 50% chance for secondary burst (reduced from 80%)
var secondaryBurst = self.attachAsset('rect', {
width: 10,
height: 10,
color: neonColors[Math.floor(Math.random() * neonColors.length)],
// Different color
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.9
});
// Create faster expanding secondary ring effect (smaller)
tween(secondaryBurst, {
scaleX: 3,
scaleY: 3,
alpha: 0
}, {
duration: 300,
// Faster duration
easing: tween.easeOutCubic // Different easing
});
}
}
// Remove explosion when lifetime is up
if (self.lifetime >= self.maxLifetime) {
self.destroy();
}
};
return self;
});
// Define a class for flying dinosaur enemy
var FlyingDino = Container.expand(function () {
var self = Container.call(this);
var dinoGraphics = self.attachAsset('flyingDino', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
// Customize appearance for dinosaur
dinoGraphics.tint = 0xFFFFFF; // No tint needed for proper dinosaur asset
self.speed = 8; // Faster than regular enemies
self.verticalSpeed = 3;
self.verticalDirection = 1; // 1 for down, -1 for up
self.lastY = self.y;
self.lastX = self.x;
self.maxVerticalOffset = 150; // Reduced maximum vertical movement to keep in upper portion
self.startY = 0; // Will store initial Y position
self.update = function () {
// Store last position for collision detection
self.lastY = self.y;
self.lastX = self.x;
// Move horizontally
self.x -= self.speed;
// Move vertically in a wave pattern
self.y += self.verticalSpeed * self.verticalDirection;
// Change direction when reaching max offset from start position
if (Math.abs(self.y - self.startY) > self.maxVerticalOffset) {
self.verticalDirection *= -1;
}
if (self.x < -50) {
self.destroy();
}
};
return self;
});
// Define a class for collectible hearts
var Heart = Container.expand(function () {
var self = Container.call(this);
var heartGraphics = self.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
self.speed = 5;
self.lastY = self.y;
self.lastX = self.x;
// Add some floating animation
self.floatOffset = Math.random() * Math.PI * 2;
self.floatSpeed = 0.05;
self.baseY = 0;
self.update = function () {
// Store last position for collision detection
self.lastY = self.y;
self.lastX = self.x;
// Move the heart
self.x -= self.speed;
// Floating animation
self.y = self.baseY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 20;
// Remove if offscreen
if (self.x < -50) {
self.destroy();
}
};
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.jumpHeight = 40;
self.isJumping = false;
self.canDoubleJump = false; // Track if player can perform double jump
self.velocityY = 0;
self.update = function () {
// No need for physics-based jump calculation anymore
// Animation is handled by tweens in jump method
};
self.jump = function () {
// First jump
if (!self.isJumping) {
self.isJumping = true;
self.canDoubleJump = true; // Enable double jump after first jump
// Use tween for smoother jump animation
var initialY = self.y;
var peakY = initialY - 500; // Jump peak height - increased from 300 to 500
// Store current X position to prevent horizontal movement during jump
var currentX = self.x;
tween(self, {
y: peakY
}, {
duration: 500,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Fall back down with easing
tween(self, {
y: 2732 / 2 // Ground level
}, {
duration: 600,
easing: tween.easeInQuad,
onFinish: function onFinish() {
self.isJumping = false;
self.canDoubleJump = false; // Reset double jump ability
self.velocityY = 0;
}
});
}
});
}
// Second jump (double jump)
else if (self.canDoubleJump) {
self.canDoubleJump = false; // Used double jump
// Cancel any existing tweens on player
tween.cancelAll(self);
// Current Y position becomes the new starting point
var currentY = self.y;
var doubleJumpPeakY = currentY - 400; // Slightly lower second jump
// Store current X position to prevent horizontal movement during double jump
var currentX = self.x;
// Perform double jump animation
tween(self, {
y: doubleJumpPeakY
}, {
duration: 400,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// Fall back down with easing
tween(self, {
y: 2732 / 2 // Ground level
}, {
duration: 500,
easing: tween.easeInQuad,
onFinish: function onFinish() {
self.isJumping = false;
self.velocityY = 0;
}
});
}
});
}
};
});
// Define a class for collectible power-up stars
var PowerStar = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
self.speed = 4;
self.lastY = self.y;
self.lastX = self.x;
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
self.floatSpeed = 0.07;
self.baseY = 0;
// Add spinning animation
self.rotation = 0;
self.rotationSpeed = 0.05;
self.update = function () {
// Store last position for collision detection
self.lastY = self.y;
self.lastX = self.x;
// Move the star
self.x -= self.speed;
// Floating animation
self.y = self.baseY + Math.sin(LK.ticks * self.floatSpeed + self.floatOffset) * 25;
// Spinning animation
self.rotation += self.rotationSpeed;
// Remove if offscreen
if (self.x < -50) {
self.destroy();
}
};
return self;
});
// Define a class for rounded rectangle backgrounds
var RoundedRect = Container.expand(function () {
var self = Container.call(this);
// Create a rounded rectangle by using multiple assets
self.create = function (width, height, radius, color, alpha) {
// Default values
radius = radius || 15;
color = color || 0x000000;
alpha = alpha || 0.6;
// Clear any existing children
while (self.children.length > 0) {
self.removeChildAt(0);
}
// Create center rectangle
var center = self.attachAsset('rect', {
width: width - radius * 2,
height: height - radius * 2,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
// Create horizontal rectangles (top and bottom)
var top = self.attachAsset('rect', {
width: width - radius * 2,
height: radius,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
top.y = -(height / 2 - radius / 2);
var bottom = self.attachAsset('rect', {
width: width - radius * 2,
height: radius,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
bottom.y = height / 2 - radius / 2;
// Create vertical rectangles (left and right)
var left = self.attachAsset('rect', {
width: radius,
height: height - radius * 2,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
left.x = -(width / 2 - radius / 2);
var right = self.attachAsset('rect', {
width: radius,
height: height - radius * 2,
color: color,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
right.x = width / 2 - radius / 2;
// Create corner circles
var topLeft = self.attachAsset('rect', {
width: radius * 2,
height: radius * 2,
color: color,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
topLeft.x = -(width / 2 - radius);
topLeft.y = -(height / 2 - radius);
var topRight = self.attachAsset('rect', {
width: radius * 2,
height: radius * 2,
color: color,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
topRight.x = width / 2 - radius;
topRight.y = -(height / 2 - radius);
var bottomLeft = self.attachAsset('rect', {
width: radius * 2,
height: radius * 2,
color: color,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
bottomLeft.x = -(width / 2 - radius);
bottomLeft.y = height / 2 - radius;
var bottomRight = self.attachAsset('rect', {
width: radius * 2,
height: radius * 2,
color: color,
shape: 'ellipse',
anchorX: 0.5,
anchorY: 0.5,
alpha: alpha
});
bottomRight.x = width / 2 - radius;
bottomRight.y = height / 2 - radius;
return self;
};
return self;
});
// Define a class for star bullets
var Star = Container.expand(function () {
var self = Container.call(this);
// Create a yellow star shape using the built-in shape feature
var starGraphics = self.attachAsset('star', {
width: 50,
height: 50,
color: 0xffff00,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5
});
// Create trailing effect container
var trailContainer = new Container();
self.addChild(trailContainer);
// Place it behind the star by removing and re-adding in correct order
self.removeChild(trailContainer);
self.removeChild(starGraphics);
self.addChild(trailContainer); // Add trail container first (bottom layer)
self.addChild(starGraphics); // Add star graphics last (top layer)
self.speed = 10;
self.speedX = 10; // Horizontal speed
self.speedY = 0; // Vertical speed (default to 0 for straight shots)
self.rotation = 0; // Current rotation
self.rotationSpeed = 0.1; // Rotation speed for spinning stars
// Store the last position for collision detection
self.lastY = self.y;
self.lastX = self.x;
// Add a pulsing effect
self.scaleDirection = 1;
self.scaleSpeed = 0.02;
self.minScale = 0.8;
self.maxScale = 1.2;
// Trail effect variables
self.trailCount = 0;
self.trailInterval = 2; // Create trail every n frames
self.trailLifetime = 400; // Trail lifetime in ms
self.maxTrails = 6; // Maximum number of trails
// Function to create a gradient trail
self.createTrail = function () {
// Create a copy of the star graphic for the trail
var trail = trailContainer.addChild(LK.getAsset('star', {
width: 50,
height: 50,
color: 0xffff00,
shape: 'box',
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8,
scaleX: starGraphics.scale.x * 0.9,
scaleY: starGraphics.scale.y * 0.9,
rotation: starGraphics.rotation
}));
// Position at current star position
trail.x = 0; // Local coordinates relative to star
trail.y = 0;
// Animate the trail to fade out and scale down
tween(trail, {
alpha: 0,
scaleX: trail.scale.x * 0.5,
scaleY: trail.scale.y * 0.5
}, {
duration: self.trailLifetime,
easing: tween.easeOut,
onFinish: function onFinish() {
if (trail && trail.parent) {
trail.parent.removeChild(trail);
}
}
});
// Return trail for further manipulation
return trail;
};
// Add shooting animation
self.animate = function () {
// Initial rapid spinning animation
tween(self, {
rotation: Math.PI * 4 // Spin 720 degrees
}, {
duration: 800,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
// After initial spin, continue with normal rotation in update()
self.rotation = Math.PI * 4 % (Math.PI * 2); // Keep within 0-360 range
}
});
// Flash effect with scaling
tween(starGraphics, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 150,
easing: tween.easeOutQuad,
onFinish: function onFinish() {
tween(starGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 150,
easing: tween.easeInQuad
});
}
});
// Add color gradient animation to the star
var colors = [0xffff00, 0xff9900, 0xff0000, 0xff00ff, 0x00ffff, 0xffff00];
var colorDuration = 600;
function animateGradient(index) {
if (index >= colors.length - 1) {
return;
}
tween(starGraphics, {
tint: colors[index + 1]
}, {
duration: colorDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
animateGradient((index + 1) % (colors.length - 1));
}
});
}
// Start the gradient animation
starGraphics.tint = colors[0];
animateGradient(0);
};
// Call animation when created
self.animate();
self.update = function () {
// Store last position before update
self.lastY = self.y;
self.lastX = self.x;
// Move the star according to its speed components
self.x += self.speedX;
self.y += self.speedY;
// Rotate the star for visual effect
self.rotation += self.rotationSpeed;
starGraphics.rotation = self.rotation;
// Pulse scaling effect
if (self.scaleDirection > 0) {
starGraphics.scale.x += self.scaleSpeed;
starGraphics.scale.y += self.scaleSpeed;
if (starGraphics.scale.x >= self.maxScale) {
self.scaleDirection = -1;
}
} else {
starGraphics.scale.x -= self.scaleSpeed;
starGraphics.scale.y -= self.scaleSpeed;
if (starGraphics.scale.x <= self.minScale) {
self.scaleDirection = 1;
}
}
// Create trail effect
self.trailCount++;
if (self.trailCount >= self.trailInterval) {
self.trailCount = 0;
// Only maintain a certain number of trails to avoid memory issues
if (trailContainer.children.length < self.maxTrails) {
self.createTrail();
}
}
// Remove if it goes off-screen
if (self.x > 2048 + 50 || self.y < -50 || self.y > 2732 + 50) {
self.destroy();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// Ensure tween.cancelAll is available
if (!tween.cancelAll) {
tween.cancelAll = function (target) {
// Find all active tweens affecting this target and cancel them
var allTweens = [];
// Get all tweens
if (tween.getAll && typeof tween.getAll === 'function') {
allTweens = tween.getAll();
} else {
// Fallback implementation if getAll is not available
if (tween._tweens && Array.isArray(tween._tweens)) {
allTweens = tween._tweens;
}
}
// Cancel tweens for target
if (allTweens.length > 0) {
allTweens.forEach(function (t) {
if (t && t._target === target && typeof t.cancel === 'function') {
t.cancel();
}
});
}
// If no direct way to access tweens, cancel by calling tween with duration 0
tween(target, {}, {
duration: 0
});
};
}
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
game.desertLevelActive = false;
game.nightmareLevelActive = false;
game.infernoLevelActive = false;
game.levelTransition = false; // Track when we transition back to main level
// Initialize explosions array
var explosions = [];
// Initialize player
var player = game.addChild(new Player());
player.x = 2048 / 2;
player.y = 2732 / 2;
player.lives = 3;
player.invulnerable = false;
player.invulnerableTime = 0;
player.hasPowerStar = false;
player.powerStarTime = 0;
player.originalTint = 0xFFFFFF;
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Initialize flying dinosaurs
var flyingDinos = [];
var dinoSpawnInterval = 200;
var dinoSpawnCounter = 0;
// Initialize collectible hearts
var collectibleHearts = [];
var heartSpawnInterval = 300; // Less frequent than enemies
var heartSpawnCounter = 0;
// Initialize power stars
var powerStars = [];
var powerStarSpawnInterval = 600; // Even less frequent than hearts
var powerStarSpawnCounter = 0;
// Create life display with proper heart images
var maxHearts = 5; // Maximum number of hearts
var heartsContainer = new Container();
LK.gui.top.addChild(heartsContainer);
heartsContainer.x = 0; // Will be centered
heartsContainer.y = 150; // Offset from top edge
// Create heart icons array - we'll display both filled and empty hearts
var heartIcons = [];
// Function to update heart display
function updateHeartDisplay() {
// Remove existing hearts
while (heartsContainer.children.length > 0) {
heartsContainer.removeChildAt(0);
}
// Add heart icons
for (var i = 0; i < player.lives; i++) {
var heart = heartsContainer.addChild(LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
}));
// Position hearts in a row, centered
var heartSpacing = 130;
var totalWidth = (player.lives - 1) * heartSpacing;
heart.x = -totalWidth / 2 + i * heartSpacing;
heartIcons[i] = heart;
}
}
// Helper function to update text and resize its container
function updateTextWithResize(textObj, container, bg, newText, minWidth) {
textObj.setText(newText);
if (bg && container) {
// Resize background based on updated text width
bg.width = Math.max(minWidth, textObj.width + 80);
}
}
// Initialize heart display
updateHeartDisplay();
// Create a container for score to control background size
var scoreContainer = new Container();
LK.gui.top.addChild(scoreContainer);
scoreContainer.x = 2048 / 2;
scoreContainer.y = 50;
// Create score background using rounded rectangle
var scoreBg = new RoundedRect();
scoreContainer.addChild(scoreBg);
scoreBg.create(250, 120, 20, 0x000000, 0.5);
// Create a new Text2 object to display the score
var scoreText = new Text2('0', {
size: 100,
fill: 0xFFFFFF,
background: 'none'
});
// Add the score text to the container
scoreText.anchor.set(0.5, 0.5);
scoreContainer.addChild(scoreText);
// Create points container with background
var pointsContainer = new Container();
LK.gui.topRight.addChild(pointsContainer);
pointsContainer.x = -20;
pointsContainer.y = 50;
// Create points background with rounded rectangle
var pointsBg = new RoundedRect();
pointsContainer.addChild(pointsBg);
pointsBg.create(320, 100, 15, 0x000000, 0.5);
// Adjust position since RoundedRect has center anchor
pointsBg.x = -160;
pointsBg.y = 0;
// Create points counter and display
var points = 0;
var pointsText = new Text2('Points: 0', {
size: 80,
fill: 0xFFFFFF,
background: 'none'
});
pointsText.anchor.set(1, 0.5);
pointsContainer.addChild(pointsText);
// Create instruction text with rounded rectangle background
var instructionContainer = new Container();
LK.gui.center.addChild(instructionContainer);
instructionContainer.y = 300;
// Create temporary text to measure its dimensions
var tempText = new Text2('TAP to JUMP\nHOLD to continuously SHOOT', {
size: 60,
fill: 0xFFFFFF,
align: 'center',
background: 'none'
});
// Get text width and add padding
var textWidth = tempText.width + 100;
var textHeight = tempText.height + 80;
// Create instruction text with more detailed controls including double jump
var instructionText = new Text2('TAP to JUMP\nTAP while JUMPING for DOUBLE JUMP\nHOLD to continuously SHOOT', {
size: 60,
fill: 0xFFFFFF,
align: 'center',
background: 'none',
shadow: {
offsetX: 5,
offsetY: 5,
color: 0x000000,
blur: 15,
alpha: 1.0
}
});
instructionText.anchor.set(0.5, 0.5);
instructionContainer.addChild(instructionText);
// Create a timeout to hide instructions after 6 seconds (reduced by 2 seconds)
LK.setTimeout(function () {
// Fade out instructions
var alpha = 1;
var fadeInterval = LK.setInterval(function () {
alpha -= 0.05; // Slower fade for better readability
instructionContainer.alpha = alpha; // Fade the entire container including background
if (alpha <= 0) {
LK.clearInterval(fadeInterval);
instructionContainer.visible = false;
}
}, 100);
}, 6000);
// Handle game updates
// Array to store bullets
var stars = [];
var shootTimer = 0;
game.update = function () {
player.update();
// Handle player invulnerability from damage
if (player.invulnerable && !player.hasPowerStar) {
// Make player flash to indicate invulnerability
player.alpha = Math.sin(Date.now() * 0.01) * 0.5 + 0.5;
// End invulnerability after 2 seconds
if (Date.now() - player.invulnerableTime > 2000) {
player.invulnerable = false;
player.alpha = 1;
}
}
// Handle power star invincibility
if (player.hasPowerStar) {
// Make player glow with neon colors
var elapsed = Date.now() - player.powerStarTime;
var colorIndex = Math.floor(elapsed / 300 % 4);
var neonColors = [0x00FFFF, 0xFF00FF, 0xFFFF00, 0x00FF00];
player.children[0].tint = neonColors[colorIndex];
// Player is invulnerable during power star effect
player.invulnerable = true;
player.alpha = 1;
// End power star effect after 10 seconds instead of 20
if (elapsed > 10000) {
player.hasPowerStar = false;
player.invulnerable = false;
// Restore original appearance
player.children[0].tint = player.originalTint;
// Show power star end indicator
showActionIndicator("POWER LOST", 1000);
}
}
// Removed auto-shooting to only allow double tap shooting
// Update stars
for (var i = stars.length - 1; i >= 0; i--) {
stars[i].update();
// Remove stars that are destroyed
if (!stars[i].parent) {
stars.splice(i, 1);
continue;
}
// Check for star-enemy collisions
for (var j = enemies.length - 1; j >= 0; j--) {
if (stars[i] && enemies[j] && stars[i].intersects(enemies[j])) {
// Create explosion effect before destroying the enemy
var explosion = new Explosion();
explosion.x = enemies[j].x;
explosion.y = enemies[j].y;
explosion.createParticles(enemies[j].tint || 0xFFAA00);
game.addChild(explosion);
explosions.push(explosion);
// Remove the enemy and the star
enemies[j].destroy();
enemies.splice(j, 1);
stars[i].destroy();
stars.splice(i, 1);
// Update score
LK.setScore(points + 10);
// Update score with container resize
updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 10).toString(), 250);
// Update points
points += 10;
// Update points with container resize
updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320);
// Nothing here - removing this redundant check as it's handled elsewhere
// Check if player reached 1000 points to win
if (points >= 1000) {
// Ensure the score is properly set before showing win screen
var finalScore = points;
LK.setScore(points);
scoreText.setText(points.toString());
// Resize score background based on updated text width
scoreBg.width = Math.max(250, scoreText.width + 80);
pointsText.setText('Points: ' + points);
// Resize points background based on updated text width
pointsBg.width = Math.max(320, pointsText.width + 80);
LK.showYouWin();
} else if (points >= 600 && !game.levelTransition) {
// Return to main level after 600 points
game.levelTransition = true;
game.desertLevelActive = false;
game.nightmareLevelActive = false;
game.infernoLevelActive = false;
// No color transition when returning to main level
}
// Check if we just passed 150 points for the enemy increase announcement
if (points - 10 < 200 && points >= 150) {
showActionIndicator("MORE ENEMIES!", 2000);
}
// Check level changes - use a single consistent approach
if (points >= 500 && !game.infernoLevelActive) {
// After 500 points, transition to inferno level
game.infernoLevelActive = true;
game.nightmareLevelActive = false;
game.desertLevelActive = false;
// No color transition for inferno level
} else if (points >= 300 && !game.nightmareLevelActive && !game.infernoLevelActive) {
game.nightmareLevelActive = true;
// No color transition for nightmare level
} else if (points >= 100 && !game.desertLevelActive && !game.nightmareLevelActive && !game.infernoLevelActive) {
game.desertLevelActive = true;
// No color transition for desert level
}
break;
}
}
}
// Hearts are now managed by updateHeartDisplay function
// Update collectible hearts
for (var i = collectibleHearts.length - 1; i >= 0; i--) {
collectibleHearts[i].update();
// Remove hearts that are destroyed
if (!collectibleHearts[i].parent) {
collectibleHearts.splice(i, 1);
continue;
}
// Check for player-heart collisions
if (player.intersects(collectibleHearts[i])) {
// Player collected a heart
collectibleHearts[i].destroy();
collectibleHearts.splice(i, 1);
// Add a life if not at maximum
if (player.lives < 5) {
player.lives++;
// Show heart collection indicator
showActionIndicator("+1 LIFE!", 1000);
// Update heart display
updateHeartDisplay();
// Show special indicators at different milestone points
if (points >= 900 && points < 1000) {
showActionIndicator("ALMOST THERE! " + (1000 - points) + " TO WIN!", 1000);
} else if (points >= 550 && points < 600) {
showActionIndicator("LEVEL CHANGE AT 600 POINTS!", 1000);
}
// Get the newest heart for animation
var newHeartIndex = player.lives - 1;
var heart = heartIcons[newHeartIndex];
// Show animation effect
tween(heart, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(heart, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
}
}
// Spawn enemies
enemySpawnCounter++;
if (enemySpawnCounter >= enemySpawnInterval) {
var enemy = new Enemy();
enemy.x = 2048;
enemy.y = 2732 / 2;
// Make enemies faster and with different colors based on level
if (game.infernoLevelActive) {
enemy.speed = 12; // Even faster enemies in inferno level
enemy.tint = 0xFF3300; // Orange-red tint for inferno enemies
// Spawn duplicate enemies in inferno level - guaranteed 1-2 extras
var extraCount = 1 + Math.floor(Math.random() * 2); // 1 or 2 extra enemies
for (var i = 0; i < extraCount; i++) {
var extraEnemy = new Enemy();
extraEnemy.x = 2048;
// Position enemies at different vertical positions
extraEnemy.y = 2732 / 2 + (Math.random() > 0.5 ? 150 + Math.random() * 100 : -(150 + Math.random() * 100)); // Varied positions
extraEnemy.speed = 10 + Math.random() * 4; // Varied speeds
extraEnemy.tint = 0xFF6600;
enemies.push(extraEnemy);
game.addChild(extraEnemy);
}
} else if (game.nightmareLevelActive) {
enemy.speed = 8; // Faster enemies in nightmare level
enemy.tint = 0xFF0000; // Red tint for nightmare enemies
} else if (points >= 150 && game.desertLevelActive) {
// More enemies after 150 points in desert level
enemy.speed = 6;
enemy.tint = 0xCC9900; // Darker desert tint
// Occasionally spawn a second enemy after 150 points
if (Math.random() < 0.3) {
var extraEnemy = new Enemy();
extraEnemy.x = 2048;
extraEnemy.y = 2732 / 2 + (Math.random() > 0.5 ? 150 : -150); // Above or below main path
extraEnemy.speed = 5;
extraEnemy.tint = 0xCCAA00;
enemies.push(extraEnemy);
game.addChild(extraEnemy);
}
}
enemies.push(enemy);
game.addChild(enemy);
// Randomize the spawn interval for the next enemy based on level
var interval, minInterval;
if (game.infernoLevelActive) {
interval = 80; // Even more frequent spawns in inferno
minInterval = 20;
} else if (game.nightmareLevelActive) {
interval = 100;
minInterval = 30;
} else if (points >= 150) {
interval = 120; // More frequent after 150 points
minInterval = 40;
} else {
interval = 150;
minInterval = 50;
}
enemySpawnInterval = Math.floor(Math.random() * interval) + minInterval;
enemySpawnCounter = 0;
}
// Spawn flying dinosaurs
dinoSpawnCounter++;
if (dinoSpawnCounter >= dinoSpawnInterval) {
var dino = new FlyingDino();
dino.x = 2048;
dino.y = Math.random() * 600 + 200; // Spawn in upper portion of screen
dino.startY = dino.y; // Store starting Y position
// Make dinosaurs faster and more aggressive based on level
if (game.infernoLevelActive) {
dino.speed = 15; // Even faster in inferno level
dino.verticalSpeed = 5;
dino.tint = 0xFF3300; // Orange-red tint for inferno dinos
} else if (game.nightmareLevelActive) {
dino.speed = 12;
dino.verticalSpeed = 4;
dino.tint = 0xFF00FF; // Purple tint for nightmare dinos
} else if (game.desertLevelActive) {
dino.speed = 10;
dino.verticalSpeed = 3.5;
dino.tint = 0xDDA020; // Golden tint for desert dinos
}
flyingDinos.push(dino);
game.addChild(dino);
// Randomize spawn interval based on game level
var interval, minInterval;
if (game.infernoLevelActive) {
interval = 150;
minInterval = 50;
} else if (game.nightmareLevelActive) {
interval = 200;
minInterval = 100;
} else {
interval = 250;
minInterval = 150;
}
dinoSpawnInterval = Math.floor(Math.random() * interval) + minInterval;
dinoSpawnCounter = 0;
}
// Spawn collectible hearts
heartSpawnCounter++;
if (heartSpawnCounter >= heartSpawnInterval) {
var heart = new Heart();
heart.x = 2048;
heart.y = Math.random() * 600 + 200; // Only spawn in upper third of screen
heart.baseY = heart.y;
collectibleHearts.push(heart);
game.addChild(heart);
// Randomize the spawn interval for the next heart (less frequent than enemies)
heartSpawnInterval = Math.floor(Math.random() * 300) + 300;
heartSpawnCounter = 0;
}
// Spawn power stars
powerStarSpawnCounter++;
if (powerStarSpawnCounter >= powerStarSpawnInterval) {
var powerStar = new PowerStar();
powerStar.x = 2048;
powerStar.y = Math.random() * 600 + 200; // Only spawn in upper third of screen
powerStar.baseY = powerStar.y;
powerStars.push(powerStar);
game.addChild(powerStar);
// Randomize the spawn interval for the next power star (less frequent than hearts)
powerStarSpawnInterval = Math.floor(Math.random() * 400) + 600;
powerStarSpawnCounter = 0;
}
// Update explosions
for (var i = explosions.length - 1; i >= 0; i--) {
explosions[i].update();
// Remove explosions that are destroyed
if (!explosions[i].parent) {
explosions.splice(i, 1);
}
}
// Update power stars
for (var i = powerStars.length - 1; i >= 0; i--) {
powerStars[i].update();
// Remove power stars that are destroyed
if (!powerStars[i].parent) {
powerStars.splice(i, 1);
continue;
}
// Check for player-power star collisions
if (player.intersects(powerStars[i])) {
// Player collected a power star
powerStars[i].destroy();
powerStars.splice(i, 1);
// Activate power star effect
player.hasPowerStar = true;
player.powerStarTime = Date.now();
// Apply neon glow effect to player
var neonColors = [0x00FFFF, 0xFF00FF, 0xFFFF00, 0x00FF00];
player.originalTint = player.children[0].tint;
tween(player.children[0], {
tint: neonColors[0]
}, {
duration: 500
});
}
}
// Update flying dinosaurs
for (var d = flyingDinos.length - 1; d >= 0; d--) {
flyingDinos[d].update();
// Check if dinosaur intersects with player
if (player.intersects(flyingDinos[d])) {
if (player.hasPowerStar) {
// Create explosion effect before destroying the dinosaur
var explosion = new Explosion();
explosion.x = flyingDinos[d].x;
explosion.y = flyingDinos[d].y;
explosion.createParticles(flyingDinos[d].tint || 0xFF00FF);
game.addChild(explosion);
explosions.push(explosion);
explosions.push(explosion);
// With power star, destroy dinosaurs on contact
flyingDinos[d].destroy();
flyingDinos.splice(d, 1);
// Increase score - more points for dinosaurs
LK.setScore(points + 30); // More points for dinosaur during power star
// Update score with container resize
updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 30).toString(), 250);
points += 30;
// Update points with container resize
updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320);
// Check if player reached 1000 points to win
if (points >= 1000) {
var finalScore = points;
LK.setScore(points);
scoreText.setText(points.toString());
pointsText.setText('Points: ' + points);
LK.showYouWin();
}
// Check level changes
if (points >= 300 && game.nightmareLevelActive && !game.infernoLevelActive) {
game.infernoLevelActive = true;
// No color transition for inferno level
showActionIndicator("INFERNO LEVEL!", 4000);
} else if (points >= 200 && game.desertLevelActive && !game.nightmareLevelActive) {
game.nightmareLevelActive = true;
// No color transition for nightmare level
showActionIndicator("NIGHTMARE LEVEL!", 4000);
} else if (points >= 100 && !game.desertLevelActive) {
game.desertLevelActive = true;
// No color transition for desert level
showActionIndicator("DESERT LEVEL!", 4000);
}
} else if (player.y < flyingDinos[d].y - 50) {
// Player is above dinosaur - jumped on it!
// Create explosion effect
var explosion = new Explosion();
explosion.x = flyingDinos[d].x;
explosion.y = flyingDinos[d].y;
explosion.createParticles(flyingDinos[d].tint || 0xFF00FF);
game.addChild(explosion);
explosions.push(explosion);
flyingDinos[d].destroy();
flyingDinos.splice(d, 1);
// Boost player up slightly after jump (small bounce)
tween.cancelAll(player);
player.jump();
// Increase score
LK.setScore(points + 20); // More points for jumping on dinosaur
// Update score with container resize
updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 20).toString(), 250);
points += 20;
// Update points with container resize
updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320);
// Show feedback
showActionIndicator("DINO STOMP!", 1000);
// Check win condition
if (points >= 1000) {
var finalScore = points;
LK.setScore(points);
scoreText.setText(points.toString());
pointsText.setText('Points: ' + points);
LK.showYouWin();
} else if (points >= 600 && !game.levelTransition) {
// After 600 points, transition back to main level
game.levelTransition = true;
game.desertLevelActive = false;
game.nightmareLevelActive = false;
game.infernoLevelActive = false;
// No color transition when returning to main level
}
} else if (points >= 600 && !game.levelTransition) {
// Return to main level after 600 points
game.levelTransition = true;
game.desertLevelActive = false;
game.nightmareLevelActive = false;
game.infernoLevelActive = false;
// No color transition when returning to main level
} else if (!player.invulnerable) {
// Player was hit, lose a life
player.lives--;
// Play hit sound
LK.getSound('hit').play();
// Flash screen to indicate damage
LK.effects.flashScreen(0xff0000, 500);
// Make player temporarily invulnerable
player.invulnerable = true;
player.invulnerableTime = Date.now();
// Update hearts display
updateHeartDisplay();
// Check if player is out of lives
if (player.lives <= 0) {
LK.showGameOver();
} else {
// Destroy the dinosaur that hit the player
flyingDinos[d].destroy();
flyingDinos.splice(d, 1);
}
}
} else if (player.x > flyingDinos[d].x && !flyingDinos[d].passed) {
flyingDinos[d].passed = true;
// Update points for passing dinosaur
points += 15; // More points for passing flying dinosaur
LK.setScore(points);
// Update score with container resize
updateTextWithResize(scoreText, scoreContainer, scoreBg, points.toString(), 250);
// Update points with container resize
updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320);
}
// Check for star-dinosaur collisions
for (var s = stars.length - 1; s >= 0; s--) {
if (stars[s] && flyingDinos[d] && stars[s].intersects(flyingDinos[d])) {
// Create explosion effect before destroying the dinosaur
var explosion = new Explosion();
explosion.x = flyingDinos[d].x;
explosion.y = flyingDinos[d].y;
explosion.createParticles(flyingDinos[d].tint || 0xFF00FF);
game.addChild(explosion);
// Remove the dinosaur and the star
flyingDinos[d].destroy();
flyingDinos.splice(d, 1);
stars[s].destroy();
stars.splice(s, 1);
// Increase score
LK.setScore(points + 15); // More points for shooting dinosaur
// Update score with container resize
updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 15).toString(), 250);
points += 15;
// Update points with container resize
updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320);
// Check if player reached 1000 points to win
if (points >= 1000) {
var finalScore = points;
LK.setScore(points);
scoreText.setText(points.toString());
pointsText.setText('Points: ' + points);
LK.showYouWin();
} else if (points >= 600 && !game.levelTransition) {
// Return to main level after 600 points
game.levelTransition = true;
game.desertLevelActive = false;
game.nightmareLevelActive = false;
game.infernoLevelActive = false;
// No color transition when returning to main level
}
// Check level changes
if (points >= 300 && game.nightmareLevelActive && !game.infernoLevelActive) {
game.infernoLevelActive = true;
// No color transition for inferno level
} else if (points >= 200 && game.desertLevelActive && !game.nightmareLevelActive) {
game.nightmareLevelActive = true;
// No color transition for nightmare level
} else if (points >= 100 && !game.desertLevelActive) {
game.desertLevelActive = true;
// No color transition for desert level
}
break;
}
}
}
// Update enemies
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
if (player.intersects(enemies[j])) {
if (player.hasPowerStar) {
// Create explosion effect before destroying the enemy
var explosion = new Explosion();
explosion.x = enemies[j].x;
explosion.y = enemies[j].y;
explosion.createParticles(enemies[j].tint || 0xFFFF00);
game.addChild(explosion);
explosions.push(explosion);
// With power star, destroy enemies on contact
enemies[j].destroy();
enemies.splice(j, 1);
// Increase score
LK.setScore(points + 20); // Double points during power star
// Update score with container resize
updateTextWithResize(scoreText, scoreContainer, scoreBg, (points + 20).toString(), 250);
// Increase points - doubled from 10 to 20
points += 20;
// Update points with container resize
updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320);
// Check if player reached 1000 points to win
if (points >= 1000) {
// Ensure the score is properly set before showing win screen
var finalScore = points;
LK.setScore(points);
scoreText.setText(points.toString());
pointsText.setText('Points: ' + points);
LK.showYouWin();
} else if (points >= 600 && !game.levelTransition) {
// Return to main level after 600 points
game.levelTransition = true;
game.desertLevelActive = false;
game.nightmareLevelActive = false;
game.infernoLevelActive = false;
// No color transition when returning to main level
}
// Check if we need to change level
// We already handled win condition above, no need for duplicate check
// Check if we need to change level
if (points >= 300 && game.nightmareLevelActive && !game.infernoLevelActive) {
game.infernoLevelActive = true;
game.nightmareLevelActive = false;
game.desertLevelActive = false;
// No color transition for inferno level
} else if (points >= 200 && game.desertLevelActive && !game.nightmareLevelActive) {
game.nightmareLevelActive = true;
// No color transition for nightmare level
} else if (points >= 100 && !game.desertLevelActive) {
game.desertLevelActive = true;
// No color transition for desert level
}
} else if (!player.invulnerable) {
// Player was hit, lose a life
player.lives--;
// Play hit sound
LK.getSound('hit').play();
// Flash screen to indicate damage
LK.effects.flashScreen(0xff0000, 500);
// Make player temporarily invulnerable
player.invulnerable = true;
player.invulnerableTime = Date.now();
// Update hearts display
updateHeartDisplay();
// Check if player is out of lives
if (player.lives <= 0) {
LK.showGameOver();
} else {
// Destroy the enemy that hit the player
enemies[j].destroy();
enemies.splice(j, 1);
}
}
} else if (player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
// Update points for passing enemy
points += 10;
LK.setScore(points);
// Update score with container resize
updateTextWithResize(scoreText, scoreContainer, scoreBg, points.toString(), 250);
// Update points with container resize
updateTextWithResize(pointsText, pointsContainer, pointsBg, 'Points: ' + points, 320);
}
}
};
// Handle player interaction
var dragNode = null;
var actionIndicator = null;
// Create visual indicators for player actions
function showActionIndicator(text, duration) {
// Remove any existing indicator, except for level announcements
var isLevelAnnouncement = text.includes("LEVEL");
if (actionIndicator && actionIndicator.parent && !isLevelAnnouncement) {
actionIndicator.parent.removeChild(actionIndicator);
}
// Create new indicator
actionIndicator = new Container();
game.addChild(actionIndicator);
// Create temporary text to measure its dimensions
var tempText = new Text2(text, {
size: isLevelAnnouncement ? 100 : 50,
fill: 0xFFFFFF,
align: 'center',
background: 'none'
});
// Get text width and add padding
var textWidth = tempText.width + (isLevelAnnouncement ? 120 : 80);
var textHeight = tempText.height + (isLevelAnnouncement ? 50 : 30);
// Set minimum width/height
var textSize = isLevelAnnouncement ? 100 : 50;
// Add text
var txt = new Text2(text, {
size: textSize,
fill: isLevelAnnouncement ? 0xFFFF00 : 0xFFFFFF,
align: 'center',
background: 'none'
});
txt.anchor.set(0.5, 0.5);
actionIndicator.addChild(txt);
// Position differently based on type
if (isLevelAnnouncement) {
// Position at center of the screen for level announcements
actionIndicator.x = 2048 / 2; // Center horizontally
actionIndicator.y = 2732 / 2; // Center vertically
// Add pulsing animation for level announcements
tween(txt.scale, {
x: 1.2,
y: 1.2
}, {
duration: 500,
repeat: -1,
yoyo: true,
easing: tween.easeInOut
});
} else {
// Position above player for regular announcements
actionIndicator.x = player.x;
actionIndicator.y = player.y - 150;
}
// Fade out after duration - shorter for level announcements
var displayDuration = isLevelAnnouncement ? 2000 : duration;
// Apply initial scaling effect for level announcements
if (isLevelAnnouncement) {
actionIndicator.scale.x = 0.1;
actionIndicator.scale.y = 0.1;
tween(actionIndicator.scale, {
x: 1,
y: 1
}, {
duration: 500,
easing: tween.easeOutBack
});
}
// Set timeout for fade-out
LK.setTimeout(function () {
// Cancel any existing tweens on text scaling
if (isLevelAnnouncement) {
tween.cancelAll(txt.scale);
}
// Fade out and move up
tween(actionIndicator, {
alpha: 0,
y: isLevelAnnouncement ? actionIndicator.y - 100 : actionIndicator.y - 50
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
if (actionIndicator && actionIndicator.parent) {
actionIndicator.parent.removeChild(actionIndicator);
}
}
});
}, displayDuration - 1000);
}
// Function to handle movement and shooting while holding
function handleMove(x, y, obj) {
if (dragNode) {
// Only move player horizontally when dragging
// Limit movement to keep player on screen
var minX = 100; // Minimum x-coordinate
var maxX = 2048 - 100; // Maximum x-coordinate
dragNode.x = Math.max(minX, Math.min(x, maxX));
// Keep the player at the same vertical position if not jumping
// (but don't override the tween animation when jumping)
if (!player.isJumping) {
dragNode.y = 2732 / 2;
}
// If holding finger on screen, continuously shoot
if (shootTimer > 0) {
// Reduced fire interval based on power star status
var fireInterval = player.hasPowerStar ? 5 : 10;
// Only shoot every interval ticks to control fire rate
if (LK.ticks % fireInterval === 0) {
// Create main star
var star = new Star();
star.x = player.x + 100;
star.y = player.y;
stars.push(star);
game.addChild(star);
// If player has power star, create spread shots
if (player.hasPowerStar) {
// Add two additional stars with slight angle variations
for (var i = -1; i <= 1; i += 2) {
var spreadStar = new Star();
spreadStar.x = player.x + 100;
spreadStar.y = player.y;
// Apply small angular deviation
var angle = i * 0.2; // ~11 degrees up/down
spreadStar.speedY = Math.sin(angle) * 5;
spreadStar.speedX = Math.cos(angle) * spreadStar.speed;
// Override default update method for angled stars
spreadStar.update = function () {
this.lastY = this.y;
this.lastX = this.x;
this.x += this.speedX;
this.y += this.speedY;
if (this.x > 2048 + 50) {
this.destroy();
}
};
stars.push(spreadStar);
game.addChild(spreadStar);
}
}
// Play shooting sound
LK.getSound('shoot').play();
}
}
}
}
// shootTimer already declared earlier in the code
// Start tracking player movement on touch down and shoot on hold
game.down = function (x, y, obj) {
// Always make the player jump on tap
player.jump();
// Start shooting - fire immediately when pressing
shootTimer = Date.now();
// Fire initial shot burst
var initialStar = new Star();
initialStar.x = player.x + 100;
initialStar.y = player.y;
stars.push(initialStar);
game.addChild(initialStar);
LK.getSound('shoot').play();
// If player has power star, add enhanced initial burst
if (player.hasPowerStar) {
// Create a burst of 3 stars in a spread pattern
for (var angle = -0.3; angle <= 0.3; angle += 0.3) {
if (angle === 0) {
continue;
} // Skip center (already created)
var spreadStar = new Star();
spreadStar.x = player.x + 100;
spreadStar.y = player.y;
spreadStar.speedY = Math.sin(angle) * 8;
spreadStar.speedX = Math.cos(angle) * spreadStar.speed;
spreadStar.rotationSpeed = 0.15; // Faster rotation
stars.push(spreadStar);
game.addChild(spreadStar);
}
}
// Handle regular movement without repositioning the player at tap position
dragNode = player;
};
// Update player position during movement
game.move = handleMove;
// Stop tracking player movement and shooting on touch up
game.up = function (x, y, obj) {
dragNode = null;
shootTimer = 0; // Reset shoot timer
};
I need an octopus that looks like bowser. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Heart illustration red shinny. Single Game Texture. 2d. Blank background. High contrast. No shadows
Star illustration shinny. Single Game Texture. Blank background. High contrast. No shadows
Make it more detailed and add flying dinosaurs
Re imagine
2d animated character Young ninja chinese qith triangular hat game. Single Game Texture. High contrast. No shadows