User prompt
If all memecoins collected the player wins the game and display Player wins animation ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
If player collected all memecoins then display a confetti rain to celebrate it ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
If player collected all memecoins then display a confetti rain to celebrate it
User prompt
If player clicks on the upper half of the screen it means Somersault jump
User prompt
Move player right by 200 units
User prompt
Refine double tapping jump sensitivity
User prompt
But now the Somersault jump not working smoot
User prompt
Avoid re double-tap while player not reach a signal
User prompt
With on double tap the player can jump only one spin and then it falling back to it original place
User prompt
Ensure Sharks hit only the lowest redsignals what they really touched
User prompt
Decrease greensignals and increase redsignals
User prompt
Player can Somersault jump with one 360 degrees spin if clicked by 2x ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Player jump rotation is only 1 spin with 360 degrees
User prompt
Player Somersault jump is only 1 spin by 360 degrees
User prompt
Reduce player jump spin in Somersault jump to only 1.
User prompt
Player can Somersault jump if clicked by 2x
User prompt
Most of signals load orientation should be in the middle of the map
User prompt
Most of signals load orientation should be in the middle of the map
User prompt
The arrangement of the signals is too uniform, arrange them more staggered
User prompt
Increase distance between greensignals load to avoid lags!
User prompt
Increase distance between greensignals to avoid lags!
User prompt
Increase distance between greensignals
User prompt
Increase distance between greensignals
User prompt
Fix it
User prompt
Turn of lightning effect to avoid lags
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BonusText = Container.expand(function (amount, type) { var self = Container.call(this); // Create bonus text display var bonusAmount = amount || 100; var bonusText = new Text2('+' + bonusAmount + ' BONUS', { size: 100, fill: 0xFFD700 // Gold color }); bonusText.anchor.set(0.5, 0.5); self.addChild(bonusText); // Start animation self.animate = function () { // Initial state self.alpha = 0; self.scale.set(0.5); // Animation sequence - fade in with scale up tween(self, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Pulse effect tween(bonusText, { tint: 0xFFFFFF // Flash to white }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { tween(bonusText, { tint: 0xFFD700 // Back to gold }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Fade out with float up tween(self, { alpha: 0, y: self.y - 100 }, { duration: 800, easing: tween.linear, onFinish: function onFinish() { self.active = false; } }); } }); } }); } }); }; self.active = true; return self; }); var CloudEffect = Container.expand(function () { var self = Container.call(this); // Create cloud parts using centerCircle assets self.cloudParts = []; var numParts = 4; // Reduced from 6 to 4 parts for better performance var cloudColors = [0xFFFFFF, 0xF0F0F0, 0xE0E0E0]; // Create main cloud parts for (var i = 0; i < numParts; i++) { var part = new Container(); var size = 120 + Math.random() * 160; // Increased size to compensate for fewer parts var cloudPart = part.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 77, // normalizing to centerCircle's 77px size scaleY: size / 77, tint: cloudColors[Math.floor(Math.random() * cloudColors.length)] }); // Position parts to form cloud shape var angle = i / numParts * Math.PI; var radius = 80; part.x = Math.cos(angle) * radius; part.y = Math.sin(angle) * radius / 2; part.baseX = part.x; part.baseY = part.y; part.offset = Math.random() * Math.PI * 2; // Random starting phase part.speed = 0.008 + Math.random() * 0.008; // Slightly reduced movement speed part.amplitude = 5 + Math.random() * 8; // Slightly reduced movement amount self.cloudParts.push(part); self.addChild(part); } // Start animation self.animate = function () { // Animate only a subset of cloud parts at a time to reduce tween overhead // This creates a more natural, less synchronous animation anyway var partCount = self.cloudParts.length; var animateCount = Math.min(3, partCount); // Only animate up to 3 parts at once var startIdx = Math.floor(Math.random() * partCount); for (var i = 0; i < animateCount; i++) { var idx = (startIdx + i) % partCount; var part = self.cloudParts[idx]; // Create gentle billowing animation for each part tween(part, { scaleX: 0.9 + Math.random() * 0.2, scaleY: 0.9 + Math.random() * 0.2, alpha: 0.8 + Math.random() * 0.2 }, { duration: 2000 + Math.random() * 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Continue animation after a short delay to stagger animations LK.setTimeout(function () { self.animate(); }, 200 + Math.random() * 300); } }); } }; // Update cloud parts movement self.update = function () { // Only update every 2nd frame to reduce computational load if (LK.ticks % 2 === 0) { for (var i = 0; i < self.cloudParts.length; i++) { var part = self.cloudParts[i]; // Gentle floating movement part.x = part.baseX + Math.sin(part.offset + LK.ticks * part.speed) * part.amplitude; part.y = part.baseY + Math.cos(part.offset + LK.ticks * part.speed) * part.amplitude / 2; } } }; return self; }); var LightningEffect = Container.expand(function (targetSignal) { var self = Container.call(this); // Lightning properties self.targetSignal = targetSignal; self.active = true; self.segments = 15; // Increased number of lightning segments for more detail self.maxOffset = 40; // Increased maximum zigzag offset for more dramatic effect self.width = 8; // Thinner lightning bolt width for more realistic appearance self.complete = false; self.lightningColor = 0xFFFF33; // Brighter neon yellow color for more realistic lightning // Create lightning segments using centerCircle assets self.lightningParts = []; // Create the lightning effect self.createLightning = function () { // Clear any existing lightning parts for (var i = 0; i < self.lightningParts.length; i++) { self.removeChild(self.lightningParts[i]); } self.lightningParts = []; // Calculate start and end positions var startX = self.x; var startY = self.y; var endX = self.targetSignal.x; var endY = self.targetSignal.y; // Create realistic lightning line with zigzag effect // Create previous point for connecting line segments var prevX = startX; var prevY = startY; // Calculate slight inclination for lightning (about 10-15 degrees) var inclineAngle = Math.random() * 0.05 + 0.15; // 0.15-0.2 radians (~8-11 degrees) var inclineOffsetX = (endY - startY) * Math.tan(inclineAngle); endX += inclineOffsetX; // Adjust end X with inclination for (var i = 0; i < self.segments; i++) { var segment = new Container(); var t = i / (self.segments - 1); // Position along the line (0 to 1) // Calculate base position along the slightly inclined line var baseX = startX + (endX - startX) * t; var baseY = startY + (endY - startY) * t; // Add random zigzag offset (except for first and last segments) var offsetX = i > 0 && i < self.segments - 1 ? (Math.random() * 2 - 1) * self.maxOffset : 0; var offsetY = i > 0 && i < self.segments - 1 ? (Math.random() * 2 - 1) * self.maxOffset : 0; // Set the segment position segment.x = baseX + offsetX; segment.y = baseY + offsetY; // Create a lightning segment using the LL asset instead of a circle if (i > 0) { // For each segment after the first, create a lightning element pointing to previous point var dx = prevX - segment.x; var dy = prevY - segment.y; var length = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); // Create lightning using LL asset var lightningPart = segment.attachAsset('ll', { anchorX: 0.5, anchorY: 0.5, scaleX: length / 100, scaleY: 1.0, rotation: Math.PI, // Rotate by 180 degrees tint: 0xFFFF33, // Brighter neon yellow for more realistic lightning x: -300 // Move lightning lines left by 300 units }); // Rotate to point toward previous segment lightningPart.rotation = angle; } else { // First segment is just a small point var lightningPart = segment.attachAsset('ll', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, tint: 0xFFFF33 // Brighter neon yellow }); } // Save current point as previous for next segment prevX = segment.x; prevY = segment.y; self.lightningParts.push(segment); self.addChild(segment); } }; // Animate the lightning self.animate = function () { // First create the lightning self.createLightning(); // Initial lightning flash bright for (var i = 0; i < self.lightningParts.length; i++) { var part = self.lightningParts[i]; part.alpha = 0.7; // Start slightly dimmer } // Bright flash effect sequence // First flash - quickly brighten LK.setTimeout(function () { for (var i = 0; i < self.lightningParts.length; i++) { tween(self.lightningParts[i], { alpha: 1.0, // Full brightness scaleX: self.lightningParts[i].scale.x * 1.2, // Expand slightly scaleY: self.lightningParts[i].scale.y * 1.2 }, { duration: 50, easing: tween.easeOut }); } // Second flash - create main strike effect LK.setTimeout(function () { // Create explosion effect at target if (self.targetSignal && self.targetSignal.active) { // Flash the target white LK.effects.flashObject(self.targetSignal, 0xFFFFFF, 200); // Make the target signal fall and destroy it self.targetSignal.falling = true; self.targetSignal.fallSpeed = 2; // Add some sideways momentum self.targetSignal.sideSpeed = Math.random() * 10 - 5; // Add to tornado signals to track it with same logic tornadoSignals.push(self.targetSignal); } // Third flash - dim slightly before final fade LK.setTimeout(function () { for (var i = 0; i < self.lightningParts.length; i++) { tween(self.lightningParts[i], { alpha: 0.8, scaleX: self.lightningParts[i].scale.x * 0.9, scaleY: self.lightningParts[i].scale.y * 0.9 }, { duration: 70, easing: tween.easeInOut }); } // Keep lightning visible without fading out LK.setTimeout(function () { // Instead of fading out, make lightning slightly brighter for (var i = 0; i < self.lightningParts.length; i++) { var part = self.lightningParts[i]; tween(part, { alpha: 0.9, scaleX: part.scale.x * 1.1, // Slightly larger to make it more visible scaleY: part.scale.y * 1.1 }, { duration: 150, easing: tween.easeOut }); } // Mark lightning as complete after a longer duration (1500ms instead of 200ms) LK.setTimeout(function () { self.complete = true; self.active = false; }, 1500); }, 80); }, 50); }, 70); }, 30); }; return self; }); var Memecoin = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'regular'; var assetId; if (self.type === 'dogmeme') { assetId = 'dogmeme'; } else if (self.type === 'shib') { assetId = 'shib'; } else if (self.type === 'pepe') { assetId = 'pepe'; } else if (self.type === 'loki') { assetId = 'loki'; } else if (self.type === 'volt') { assetId = 'volt'; } else if (self.type === 'cat') { assetId = 'cat'; } else if (self.type === 'president') { assetId = 'president'; } else { assetId = 'memecoin'; } var coinGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.speed = 6; self.active = true; self.collected = false; self.value = self.type === 'dogmeme' ? 100 : self.type === 'shib' ? 150 : self.type === 'pepe' ? 200 : self.type === 'loki' ? 250 : self.type === 'volt' ? 300 : self.type === 'cat' ? 350 : self.type === 'president' ? 400 : 50; // President worth more points than cat self.update = function () { if (!self.active) { return; } self.x -= self.speed; // Rotate coin counterclockwise (negative value for opposite direction) coinGraphics.rotation -= 0.03; // Remove when off screen if (self.x < -100) { self.active = false; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.vx = 0; self.vy = 0; self.gravity = 0.5; self.jumpPower = -25; // Increased jump power from -15 to -25 self.isJumping = false; self.isDead = false; self.lastWasIntersecting = false; self.jump = function () { // Find if player is standing on a green signal var standingOnGreenSignal = signals.some(function (s) { return player && s.type === 'green' && player.intersects(s) && Math.abs(player.y - (s.y - 275)) < 10; }); if (!self.isDead && (self.vy === 0 || standingOnGreenSignal)) { self.vy = self.jumpPower; self.isJumping = true; LK.getSound('jump').play(); } }; self.update = function () { if (self.isDead) {} self.lastY = self.y; // Apply gravity self.vy += self.gravity; // Apply movement self.y += self.vy; // Track if player is jumping or falling self.isJumping = self.vy !== 0; // Ground collision detection if (self.y > 2732 - playerGraphics.height / 2) { // Bottom of screen self.y = 2732 - playerGraphics.height / 2; self.vy = 0; self.isJumping = false; } }; return self; }); var Shark = Container.expand(function () { var self = Container.call(this); var sharkGraphics = self.attachAsset('shark', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.direction = 1; self.centerY = 2732 - sharkGraphics.height / 2 + 100; // Move sharks down by 100 units self.update = function () { // If sharks are orbiting, follow orbit pattern instead of normal movement if (orbiting) { // Keep track of which shark this is (1 or 2) var sharkIndex = this === shark2 ? 1 : 0; var offsetAngle = sharkIndex * Math.PI; // 180 degrees offset for second shark // Calculate position based on orbit angle var orbitRadius = 150; // Reduced radius of orbit from 200 to 150 for tighter movement // Increase orbit speed during tornado effect var orbitSpeed = 2.5; // Increased orbit speed factor self.x = orbitCenterX + Math.cos(orbitAngle * orbitSpeed + offsetAngle) * orbitRadius; self.y = orbitCenterY + Math.sin(orbitAngle * orbitSpeed + offsetAngle) * orbitRadius / 2; // More compressed elliptical orbit // Set shark orientation based on movement direction var movingRight = Math.cos(orbitAngle * orbitSpeed + offsetAngle + Math.PI / 2) > 0; sharkGraphics.scale.x = movingRight ? 1 : -1; return; // Skip normal movement logic when orbiting } // Normal movement when not orbiting // Keep shark at constant y position self.y = self.centerY; // Check if player exists and has fallen to shark's height level if (player && player.y > 2732 - 350) { // Shark hunts player - move toward player position self.direction = player.x > self.x ? 1 : -1; self.speed = 8; // Increase speed when hunting // Set correct shark orientation based on direction sharkGraphics.scale.x = self.direction; } else { // Normal back-and-forth movement when player isn't at shark level self.speed = 5; // Check if this is the second shark (compare to the global shark2 reference) if (this === shark2) { // Second shark moves in opposite pattern to first shark (alternating pattern) // Start swimming from right to left if (self.x <= 200 && self.direction === -1) { self.direction = 1; sharkGraphics.scale.x = 1; } else if (self.x >= 1900 && self.direction === 1) { self.direction = -1; sharkGraphics.scale.x = -1; } } else { // First shark patrols from left to right first if (self.x >= 1800 && self.direction === 1) { self.direction = -1; sharkGraphics.scale.x = -1; } else if (self.x <= 200 && self.direction === -1) { self.direction = 1; sharkGraphics.scale.x = 1; } } } // Apply movement self.x += self.speed * self.direction; }; return self; }); var Signal = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'green'; var assetId = self.type === 'green' ? 'greenSignal' : 'redSignal'; // Generate random width variation factor between 0.6 and 1.4 for more dramatic variation var widthVariation = 0.6 + Math.random() * 0.8; var signalGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, scaleX: 1 / 1.2 * widthVariation, scaleY: 1 / 1.2 }); // Rotate signal by 90 degrees to the left signalGraphics.rotation = -Math.PI / 2; // -90 degrees in radians self.speed = 3.5; self.active = true; self.shineTimer = 0; self.isShining = false; // Store the width variation for calculations self.widthVariation = widthVariation; // Create shine effect self.startShine = function () { if (!self.isShining) { self.isShining = true; var baseColor = self.type === 'green' ? 0xFFFFFF : 0xFFFFFF; // Shine effect with tween tween(signalGraphics, { alpha: 0.8 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { tween(signalGraphics, { alpha: 1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { self.isShining = false; } }); } }); } }; // Turn green signal golden when touched by player // Create shimmer animation with color tint alternation self.startShimmerAnimation = function (targetGraphics) { self.shimmerAnimationActive = true; // Golden to bright white shimmer function shimmerCycle() { if (!self.shimmerAnimationActive) { return; } // Shimmer to bright white tween(targetGraphics, { tint: 0xFFFFFF // Bright white }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (!self.shimmerAnimationActive) { return; } // Return to gold tween(targetGraphics, { tint: 0xFFD700 // Gold color }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (self.shimmerAnimationActive) { // Continue animation cycle shimmerCycle(); } } }); } }); } // Start shimmer cycle shimmerCycle(); }; self.turnGolden = function () { if (self.type === 'green' && !self.isGolden) { self.isGolden = true; var goldenSignalGraphics = LK.getAsset('goldenSignal', { anchorX: 0.5, anchorY: 0.5, scaleX: 1 / 1.2, scaleY: 1 / 1.2 }); // Match the rotation of the current signal goldenSignalGraphics.rotation = signalGraphics.rotation; // Animation sequence for golden transformation tween(signalGraphics, { alpha: 0 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { // Replace the green signal with golden one self.removeChild(signalGraphics); self.addChild(goldenSignalGraphics); // Start with small scale and expand goldenSignalGraphics.scale.set(0.5, 0.5); goldenSignalGraphics.alpha = 0.7; // Grow and fade in tween(goldenSignalGraphics, { scaleX: 1 / 1.2, scaleY: 1 / 1.2, alpha: 1 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Start shimmer animation after the initial grow animation self.startShimmerAnimation(goldenSignalGraphics); } }); } }); } }; self.falling = false; self.fallSpeed = 0; self.update = function () { if (!self.active) { // Clean up shimmer animation if it's running if (self.shimmerAnimationActive) { self.shimmerAnimationActive = false; } return; } // Handle falling behavior for red signals if (self.falling) { self.fallSpeed += 0.5; // Gravity effect self.y += self.fallSpeed; // Rotate while falling self.children[0].rotation += 0.05; // Apply red flash effect when signal starts falling (if it hasn't been flashed already) if (!self.flashedRed && self.fallSpeed < 3) { self.flashedRed = true; LK.effects.flashObject(self, 0xff4500, 500); // Flash red } // Deactivate when off bottom of screen if (self.y > 2732 + 100) { self.active = false; } return; // Skip normal behavior when falling } self.x -= self.speed; // Add random shine effect occasionally self.shineTimer++; if (self.shineTimer > 60 && Math.random() < 0.05) { self.startShine(); self.shineTimer = 0; } // Check if red signal is touching a shark if (self.type === 'red' && (shark && self.intersects(shark) || shark2 && self.intersects(shark2))) { // Save the original y position before moving var startY = self.y; // Calculate target position (random amount between 100 and 1000 upward, at half speed) var moveAmount = (100 + Math.random() * 900) / 2; // Reduced to half var targetY = startY - moveAmount; // Change color to green self.type = 'green'; // Replace the red signal graphic with a green one var oldGraphics = self.children[0]; var oldRotation = oldGraphics.rotation; var oldScaleX = oldGraphics.scale.x; var oldScaleY = oldGraphics.scale.y; self.removeChild(oldGraphics); var greenGraphics = self.attachAsset('greenSignal', { anchorX: 0.5, anchorY: 0.5, scaleX: oldScaleX, scaleY: oldScaleY }); // Apply the same rotation as the original signal greenGraphics.rotation = oldRotation; // Make the signal shine to highlight the change self.startShine(); // Use tween for smooth upward movement with longer duration for more continuous motion tween(self, { y: targetY }, { duration: 1600, // Doubled duration for slower, more continuous movement // Duration of upward movement in ms easing: tween.easeOutQuad // Smoother deceleration for continuous movement }); } // Check for signals getting too close to sharks // Shark is positioned close to bottom of screen (around 2732 - 100) var sharkYPosition = 2732 - 400; // Position above shark's height if (self.y > sharkYPosition) { // Move signal upward to avoid sharks self.y = sharkYPosition - Math.random() * 100; self.startShine(); // Visual indicator that signal moved } // Check for overlapping signals (only for active signals on screen) for (var i = 0; i < signals.length; i++) { var otherSignal = signals[i]; // Skip self and inactive signals if (otherSignal !== self && otherSignal.active) { // Check if signals are close to each other var xDistance = Math.abs(self.x - otherSignal.x); var yDistance = Math.abs(self.y - otherSignal.y); // If signals are overlapping or very close (within 20px) if (xDistance < 20 && yDistance < 20) { // Move this signal to avoid overlap if (self.type === 'green') { self.y -= 30; // Move green signals upward } else { self.y += 30; // Move red signals downward } // Trigger shine effect to show adjustment self.startShine(); } } } // Remove when off screen only if player is not jumping/falling // Make sure we keep signals visible while player is falling if (self.x < -200) { // Only deactivate if player doesn't exist or if player is not jumping/falling if (!player || !player.isJumping && player.vy <= 0) { self.active = false; } } }; return self; }); var Tornado = Container.expand(function () { var self = Container.call(this); // Create tornado graphics self.particles = []; var numParticles = 15; // Reduced from 30 to 15 particles to improve performance var colors = [0x87CEEB, 0x00BFFF, 0x1E90FF, 0x4682B4]; // Create particles that make up the tornado for (var i = 0; i < numParticles; i++) { var particle = new Container(); var size = 40 + Math.random() * 70; // Slightly larger particles to compensate for fewer of them var shape = particle.attachAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: size / 100, scaleY: size / 100, tint: colors[Math.floor(Math.random() * colors.length)] }); // Initial position particle.radius = 50 + i * 8; // Increased spacing between particles particle.angle = Math.random() * Math.PI * 2; particle.rotationSpeed = (0.02 + Math.random() * 0.03) * (Math.random() > 0.5 ? 1 : -1); // Reduced rotation speed particle.alpha = 0.5 + Math.random() * 0.5; // Set initial positions particle.x = Math.cos(particle.angle) * particle.radius; particle.y = Math.sin(particle.angle) * particle.radius; self.particles.push(particle); self.addChild(particle); } self.tornadoHeight = 0; // Current height of the tornado self.tornadoSpeed = 10; // How fast the tornado rises self.isActive = true; self.startY = 2732 - 400; // Start near the sharks self.topWidth = 50; // Width at top (narrow) self.bottomWidth = 500; // Width at bottom (wide) self.signalLimit = 3; // Fixed limit of exactly 3 signals self.signalsDestroyed = 0; // Counter for how many signals destroyed self.startTime = Date.now(); // Track when tornado started self.duration = 3000; // Duration in milliseconds (3 seconds) self.isSilent = false; // Track if tornado is in silent mode self.update = function () { if (!self.isActive) { return; } // Rise up self.tornadoHeight += self.tornadoSpeed; // Only update particles every other frame to reduce computational load if (LK.ticks % 2 === 0) { // Update all particles for (var i = 0; i < self.particles.length; i++) { var p = self.particles[i]; // Calculate relative height position (0-1) var relativeHeight = i / self.particles.length; // Width decreases as we go up var currentWidth = self.bottomWidth - (self.bottomWidth - self.topWidth) * relativeHeight; // Update angle for rotation - reduced rotation speed p.angle += p.rotationSpeed; // Position - wider at bottom, narrower at top p.x = Math.cos(p.angle) * (p.radius * (currentWidth / self.bottomWidth)); p.y = Math.sin(p.angle) * (p.radius * (currentWidth / self.bottomWidth)); // Adjust vertical position based on tornado height p.y -= self.tornadoHeight * relativeHeight; } } // Check for signal collisions - only do this every 3 frames to reduce CPU load if (tornadoActive && self.signalsDestroyed < self.signalLimit && LK.ticks % 3 === 0) { for (var i = 0; i < signals.length; i++) { var signal = signals[i]; if (signal.active && !signal.falling && !tornadoSignals.includes(signal)) { // Check if signal is near the tornado var dx = signal.x - self.x; var dy = signal.y - (self.y - self.tornadoHeight / 2); var distance = Math.sqrt(dx * dx + dy * dy); // If signal is within tornado's effect range and we haven't hit the limit if (distance < 300 && self.signalsDestroyed < self.signalLimit) { // Add to affected signals tornadoSignals.push(signal); // Make it fall signal.falling = true; signal.fallSpeed = 2; signal.startShine(); // Add some sideways momentum based on tornado rotation signal.sideSpeed = Math.random() * 10 - 5; // Increment the counter self.signalsDestroyed++; } } } } }; return self; }); /**** * Initialize Game ****/ // Golden letter shimmer animation var game = new LK.Game({ backgroundColor: 0x00142A }); /**** * Game Code ****/ // Game variables // Golden letter shimmer animation var LetterShimmerEffect = function LetterShimmerEffect(letterText) { // Create shimmer animation cycle function startShimmer() { // Shimmer to bright white tween(letterText, { tint: 0xFFFFFF // Bright white }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { // Return to gold tween(letterText, { tint: 0xFFD700 // Gold color }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { // Continue animation cycle startShimmer(); } }); } }); } // Set initial state and start shimmer letterText.tint = 0xFFD700; // Gold color startShimmer(); }; var player; var signals = []; var memecoins = []; var shark; var shark2; var isGameActive = true; var gameSpeed = 1; var lastSignalTime = 0; var lastCoinTime = 0; var score = 0; var scoreIncrement = 0.1; var distanceTraveled = 0; var memecoinLetters = []; // Reference to MEMECOIN letters var nextLetterIndex = 0; // Track which letter to illuminate next var tornadoActive = false; var tornadoSignals = []; // Signals caught in tornado var lastOrbitTime = 0; // Track when sharks last started orbiting var orbiting = false; // Track if sharks are currently orbiting var orbitAngle = 0; // Current orbit angle var orbitCenterX = 0; // X center of orbit var orbitCenterY = 0; // Y center of orbit var tornadoY = 0; // Current Y position of tornado var coinRound = { regular: false, dogmeme: false, shib: false, pepe: false, loki: false, volt: false, cat: false, president: false }; // UI elements var scoreTxt; var highScoreTxt; // Initialize UI function initUI() { // High score text var highScore = storage.highScore || 0; highScoreTxt = new Text2('HIGH SCORE: ' + Math.floor(highScore), { size: 50, fill: 0xFFD700 }); highScoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(highScoreTxt); highScoreTxt.y = 0; // Score text scoreTxt = new Text2('SCORE: 0', { size: 70, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.y = 60; // MEMECOIN letters container var memeLettersContainer = new Container(); memeLettersContainer.x = 0; memeLettersContainer.y = 130; // Match the spacing of 70px between high score and score texts LK.gui.top.addChild(memeLettersContainer); // Initialize empty letter display memecoinLetters = []; // Use global variable to store letters var memecoinWord = "MEMECOIN"; for (var i = 0; i < memecoinWord.length; i++) { var letterText = new Text2(memecoinWord[i], { size: 60, fill: 0xAAAAAA // Lighter gray (not collected) }); letterText.anchor.set(0.5, 0); letterText.x = (i - memecoinWord.length / 2 + 0.5) * 60 + 5; // Center the letters letterText.collected = false; memecoinLetters.push(letterText); memeLettersContainer.addChild(letterText); } } // Initialize game world function initGame() { isGameActive = true; score = 0; distanceTraveled = 0; gameSpeed = 1; // Reset coin round tracking coinRound = { regular: false, dogmeme: false, shib: false, pepe: false, loki: false, volt: false, cat: false, president: false }; // Reset MEMECOIN letters nextLetterIndex = 0; if (memecoinLetters && memecoinLetters.length > 0) { for (var i = 0; i < memecoinLetters.length; i++) { memecoinLetters[i].tint = 0x444444; // Reset to dark gray memecoinLetters[i].collected = false; } } // Create player at top left, ready to fall player = new Player(); player.x = 200; player.y = 100; player.vy = 5; // Start with downward velocity game.addChild(player); // Create sharks shark = new Shark(); shark.y = 2732 - 100; game.addChild(shark); // Create second shark with opposite direction compared to first shark shark2 = new Shark(); shark2.x = 1500; // Position second shark on the right side shark2.y = 2732 - 100; shark2.direction = -1; // Start with opposite direction to make sharks swim alternately // Set correct orientation based on direction shark2.children[0].scale.x = -1; game.addChild(shark2); // Clear signals and coins clearEntities(); // Create initial signals that have already reached the left side var recentHeights = []; var heightThreshold = 100; // Further increased threshold to force more staggering for (var i = 0; i < 44; i++) { var signalType = Math.random() < 0.5 ? 'green' : 'red'; // For signals that are lower than the previous, make them red // For signals that are higher than the previous, make them green var lastY = 1500; if (signals.length > 0) { lastY = signals[signals.length - 1].y; } var newY; var willBeHigher = Math.random() < 0.5; if (willBeHigher) { // More pronounced ascent with increased height differences var heightDiff = 150 + Math.random() * 180; newY = lastY - heightDiff; signalType = 'green'; } else { // More pronounced descent with increased height differences var heightDiff = 150 + Math.random() * 180; newY = lastY + heightDiff; signalType = 'red'; } // Check if this height is too similar to recent heights var isTooSimilar = recentHeights.length > 0 && recentHeights.some(function (height) { return Math.abs(newY - height) < heightThreshold; }); // If too similar, adjust the height with larger offsets for more staggering if (isTooSimilar) { if (signalType === 'green') { newY -= 120 + Math.random() * 80; // Increased offset for more staggering } else { newY += 120 + Math.random() * 80; // Increased offset for more staggering } } // Ensure signal stays within playable area and away from sharks // Keep signals at least 600px above sharks (which are at 2732 - 100) newY = Math.max(800, Math.min(2000, newY)); // Store this height for future comparison recentHeights.push(newY); if (recentHeights.length > 10) { recentHeights.shift(); // Keep only last 10 heights } var signal = new Signal(signalType); // Position signals already at the left side of the screen with horizontal variation var xOffset = Math.random() * 30 - 15; // Random horizontal offset between -15 and +15 signal.x = -150 + i * 50 + xOffset; // Distribute signals across the left edge with variations signal.y = newY; signals.push(signal); // Make sure signals are placed behind the player if (player) { var playerIndex = game.getChildIndex(player); game.addChildAt(signal, playerIndex); } else { game.addChild(signal); } } // Start game music in a loop (loop is true by default, but explicitly set for clarity) LK.playMusic('gameMusic', { loop: true }); // Add cloud to top right corner, but moved left by 100 units and up by 77 units var cloud = new CloudEffect(); cloud.x = 2048 - 250; // Position in top right, offset by 250px (moved left by 100 units) cloud.y = 123; // Position cloud 77 units higher (200 - 77 = 123) cloud.scale.set(1.2); // Slightly larger cloud game.addChild(cloud); cloud.animate(); // Start the billowing animation } // Clear all entities function clearEntities() { for (var i = signals.length - 1; i >= 0; i--) { signals[i].destroy(); signals.splice(i, 1); } for (var i = memecoins.length - 1; i >= 0; i--) { memecoins[i].destroy(); memecoins.splice(i, 1); } } // Spawn signals function spawnSignal() { var currentTime = Date.now(); // Spawn signals at intervals that maintain consistent spacing regardless of score // At higher scores (1000+), we need to keep a minimum time between signals var baseInterval = 300; var minInterval = 150; var adjustedInterval = score > 1000 ? Math.max(minInterval, baseInterval / gameSpeed) : baseInterval / gameSpeed; if (currentTime - lastSignalTime > adjustedInterval) { // This ensures spacing remains consistent even at high scores var signal; var lastSignalY = 1500; // Default Y position if no signals exist // Get the Y position of the last signal to compare with if (signals.length > 0) { var lastSignal = signals[signals.length - 1]; lastSignalY = lastSignal.y; } // Calculate a new Y position var newY; var willBeHigher = Math.random() < 0.30; // 30% chance to go higher, 70% chance for red signals // Count consecutive red signals var redSignalCount = 0; // Find the last signal of each type for height comparison var lastGreenY = null; var lastRedY = null; // Track recent signal heights to prevent repetition var recentHeights = []; var heightThreshold = 100; // Further increased threshold to force more staggering // Collect the last few signal heights for comparison for (var i = signals.length - 1; i >= Math.max(0, signals.length - 15); i--) { recentHeights.push(signals[i].y); } // Find the last green and red signal Y positions for (var i = signals.length - 1; i >= 0; i--) { if (signals[i].type === 'green' && lastGreenY === null) { lastGreenY = signals[i].y; } if (signals[i].type === 'red' && lastRedY === null) { lastRedY = signals[i].y; } if (lastGreenY !== null && lastRedY !== null) { break; } } for (var i = signals.length - 1; i >= Math.max(0, signals.length - 5); i--) { if (signals[i] && signals[i].type === 'red') { redSignalCount++; } else { break; } } // Force green signal if we've had 5 reds in a row if (redSignalCount >= 5) { // Force green signal (must be higher) but with more gradual ascent newY = lastSignalY - (60 + Math.random() * 80); signal = new Signal('green'); } else if (willBeHigher) { // This signal will be higher (ascending) // More pronounced height difference for green signals var heightDiff = 80 + Math.random() * 120; // Increased for more staggering if (lastGreenY !== null && Math.abs(lastSignalY - heightDiff - lastGreenY) > 200) { // Increased maximum height change between signals of same color heightDiff = Math.min(heightDiff, 200); } newY = lastSignalY - heightDiff; // Higher signals must be green signal = new Signal('green'); } else { // This signal will be lower (descending) // More pronounced height difference for red signals var heightDiff = 80 + Math.random() * 120; // Increased for more staggering if (lastRedY !== null && Math.abs(lastSignalY + heightDiff - lastRedY) > 200) { // Increased maximum height change between signals of same color heightDiff = Math.min(heightDiff, 200); } newY = lastSignalY + heightDiff; // Check if we would have too many consecutive red signals var wouldBeConsecutiveReds = redSignalCount + 1; if (wouldBeConsecutiveReds >= 5) { // Force this to be a green signal instead with gradual ascent newY = lastSignalY - (60 + Math.random() * 80); signal = new Signal('green'); } else { // Lower signals must be red signal = new Signal('red'); } } // Ensure signal stays within playable area and away from sharks // Sharks are positioned at bottom (2732 - 100), keep signals at least 600px above sharks newY = Math.max(800, Math.min(2000, newY)); // Lower maximum to keep away from sharks // Additional check for signals that might be about to overlap (within 25px) var potentialOverlap = signals.filter(function (s) { // Only check signals that are still on screen or about to enter return s.active && s.x > -200 && s.x < 2300 && Math.abs(s.y - newY) < 25; }); // If there's potential overlap, adjust the Y position with smaller offsets if (potentialOverlap.length > 0) { if (signal.type === 'green') { newY -= 60 + Math.random() * 30; // Move green signals higher with smaller offset } else { newY += 60 + Math.random() * 30; // Move red signals lower with smaller offset } // Re-check playable area boundaries newY = Math.max(800, Math.min(2200, newY)); } // Add height variation to signals and prevent consecutive similar heights if (signal.type === 'green') { // Green signals with cascading heights - increased variations var greenOffset = Math.random() * 80 + 30; // Increased base offset for green signals // Check if this height is too similar to recent heights var isTooSimilar = recentHeights.some(function (height) { return Math.abs(newY - greenOffset - height) < heightThreshold; }); // If too similar, use larger offset to ensure more staggering if (isTooSimilar) { greenOffset += 70 + Math.random() * 50; // Larger offset for more staggering } newY -= greenOffset; } else if (signal.type === 'red') { // Red signals with cascading heights - increased variations var redOffset = Math.random() * 80 + 30; // Increased base offset for red signals // Check if this height is too similar to recent heights var isTooSimilar = recentHeights.some(function (height) { return Math.abs(newY + redOffset - height) < heightThreshold; }); // If too similar, use larger offset to ensure more staggering if (isTooSimilar) { redOffset += 70 + Math.random() * 50; // Larger offset for more staggering } newY += redOffset; } // Add horizontal variation for more staggered appearance var horizontalVariation = Math.random() * 60 - 30; // Range of -30 to +30 signal.x = 2048 + 150 + horizontalVariation; signal.y = newY; signals.push(signal); // Add signal behind the player to ensure correct display order if (player) { var playerIndex = game.getChildIndex(player); game.addChildAt(signal, playerIndex); } else { game.addChild(signal); } lastSignalTime = currentTime; } } // Spawn memecoins function spawnMemecoin() { var currentTime = Date.now(); // Spawn coins less frequently than signals - increased interval from 6000 to 12000 if (currentTime - lastCoinTime > 12000 / gameSpeed) { // Find green signals that are still on screen (or about to enter) var greenSignals = signals.filter(function (signal) { return signal.type === 'green' && signal.x > -200 && signal.x < 2300; }); // Only spawn coin if we have green signals if (greenSignals.length > 0) { // Pick a random green signal var randomGreenSignal = greenSignals[Math.floor(Math.random() * greenSignals.length)]; // Check if we completed a round of all coin types var roundComplete = coinRound.regular && coinRound.dogmeme && coinRound.shib; if (roundComplete) { // Reset the round if all coin types have appeared coinRound = { regular: false, dogmeme: false, shib: false, pepe: false, loki: false, volt: false, cat: false, president: false }; } // Determine which coin types are still needed in current round var availableTypes = []; if (!coinRound.regular) { availableTypes.push('regular'); } if (!coinRound.dogmeme) { availableTypes.push('dogmeme'); } if (!coinRound.shib) { availableTypes.push('shib'); } if (!coinRound.pepe) { availableTypes.push('pepe'); } if (!coinRound.loki) { availableTypes.push('loki'); } if (!coinRound.volt) { availableTypes.push('volt'); } if (!coinRound.cat) { availableTypes.push('cat'); } if (!coinRound.president) { availableTypes.push('president'); } // Select a random coin type from available types var coinType = availableTypes[Math.floor(Math.random() * availableTypes.length)]; // Mark this type as spawned in current round coinRound[coinType] = true; var coin = new Memecoin(coinType); coin.x = 2048 + 150; // Position coin between MEMECOIN word (y position around 130) and center of screen (2732/2) // Calculate the available space between MEMECOIN text position and center of screen var minY = 200; // Safe position below MEMECOIN text (which is at y=130) var maxY = 2732 / 2; // Center height of the screen coin.y = minY + Math.random() * (maxY - minY); memecoins.push(coin); // Add coin behind the player to ensure correct display order if (player) { var playerIndex = game.getChildIndex(player); game.addChildAt(coin, playerIndex); } else { game.addChild(coin); } lastCoinTime = currentTime; } } } // Check collisions between player and signals/coins/shark function checkCollisions() { if (!isGameActive) { return; } // Check signal collisions // Only check signals that are near the player to reduce performance impact for (var i = 0; i < signals.length; i++) { var signal = signals[i]; // Quick proximity check before doing expensive intersection test var dx = Math.abs(signal.x - player.x); var dy = Math.abs(signal.y - player.y); // Only do intersection check if signal is within reasonable distance if (signal.active && dx < 250 && dy < 300 && player.intersects(signal)) { // Store previous intersection state var wasIntersecting = player.lastWasIntersecting; player.lastWasIntersecting = true; // Only handle collision if this is a new intersection if (!wasIntersecting) { // Handle red signal falling when touched anywhere if (signal.type === 'red' && !signal.falling) { // Make red signal fall signal.falling = true; signal.fallSpeed = 2; // Trigger laser shine effect when starting to fall signal.startShine(); continue; // Skip other collision handling for this signal } // Check if player is above the signal and falling down // The signal is rotated 90 degrees left, so we need to detect its height properly // Signal's height is now its width because of the rotation var signalHeight = 50 / 1.2; // Original height divided by scale factor var signalWidth = 500 / 1.2; // Original width divided by scale factor var playerHeight = 150; // Player height // Calculate where the top of the signal is considering rotation if (player.y < signal.y - signalWidth / 2 && player.vy > 0) { if (signal.type === 'green') { // Land on green signals but don't automatically jump // Position player properly on top of the signal player.y = signal.y - signalWidth / 2 - playerHeight / 2; player.vy = 0; player.isJumping = false; // Trigger laser shine effect on green signal when landing signal.startShine(); // Turn the green signal golden when touched signal.turnGolden(); } else if (signal.type === 'red') { // Pass through red signals, ignore collision // Don't land on red signals, continue falling player.vy += player.gravity; player.isJumping = true; // Trigger laser shine effect on red signal when passing through signal.startShine(); } } } } else if (signal.active) { // If not intersecting this signal, reset lastWasIntersecting player.lastWasIntersecting = false; } } // Check memecoin collisions for (var i = 0; i < memecoins.length; i++) { var coin = memecoins[i]; // Track previous intersection state to prevent multiple collections in one frame if (!coin.lastWasIntersecting) { coin.lastWasIntersecting = false; } // Find the highest green signal var highestGreenSignal = null; var highestY = Infinity; for (var j = 0; j < signals.length; j++) { if (signals[j].active && signals[j].type === 'green' && signals[j].y < highestY) { highestY = signals[j].y; highestGreenSignal = signals[j]; } } // Check if coin intersects with the highest green signal if (coin.active && !coin.collected && highestGreenSignal && coin.intersects(highestGreenSignal)) { // Turn the green signal red highestGreenSignal.type = 'red'; // Replace the green signal graphic with a red one var oldGraphics = highestGreenSignal.children[0]; var oldRotation = oldGraphics.rotation; var oldScaleX = oldGraphics.scale.x; var oldScaleY = oldGraphics.scale.y; highestGreenSignal.removeChild(oldGraphics); var redGraphics = highestGreenSignal.attachAsset('redSignal', { anchorX: 0.5, anchorY: 0.5, scaleX: oldScaleX, scaleY: oldScaleY }); // Apply the same rotation as the original signal redGraphics.rotation = oldRotation; // Move signal down by 100-200 units var moveDownAmount = 100 + Math.random() * 100; // Use tween for smooth downward movement tween(highestGreenSignal, { y: highestGreenSignal.y + moveDownAmount }, { duration: 800, easing: tween.easeOutQuad }); // Flash the signal to indicate it's changed LK.effects.flashObject(highestGreenSignal, 0xff4500, 500); // Flash red // Collect the coin coin.collected = true; tween(coin, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, onFinish: function onFinish() { coin.active = false; } }); } // Only collect if active, not already collected, and this is a new intersection if (coin.active && !coin.collected && player.intersects(coin) && !coin.lastWasIntersecting) { // Collect coin coin.collected = true; coin.lastWasIntersecting = true; // Add points based on coin type score += coin.value; scoreTxt.setText('SCORE: ' + Math.floor(score)); // Illuminate next MEMECOIN letter if we haven't illuminated all if (memecoinLetters && memecoinLetters.length > 0 && nextLetterIndex < memecoinLetters.length) { var letterToIlluminate = memecoinLetters[nextLetterIndex]; if (!letterToIlluminate.collected) { letterToIlluminate.collected = true; // Change the color to gold without the shimmer effect letterToIlluminate.tint = 0xFFD700; // Gold color nextLetterIndex++; // Check if all letters are now collected if (nextLetterIndex === memecoinLetters.length) { // Apply the shimmer effect to all letters now that they're all collected for (var j = 0; j < memecoinLetters.length; j++) { LetterShimmerEffect(memecoinLetters[j]); } // All letters collected - show bonus score += 500; scoreTxt.setText('SCORE: ' + Math.floor(score)); // Create bonus text for completing MEMECOIN var bonusText = new BonusText(500, 'MEMECOIN'); bonusText.x = 2048 / 2; bonusText.y = 2732 / 3; game.addChild(bonusText); bonusText.animate(); } } } // Flash coin with different colors based on type var flashColor; if (coin.type === 'dogmeme') { flashColor = 0xFFA500; // Orange } else if (coin.type === 'shib') { flashColor = 0x00FF00; // Green } else if (coin.type === 'pepe') { flashColor = 0x00FF80; // Mint Green } else if (coin.type === 'loki') { flashColor = 0x00FFFF; // Cyan } else if (coin.type === 'volt') { flashColor = 0xFFFF00; // Yellow } else if (coin.type === 'cat') { flashColor = 0xFF00FF; // Magenta } else if (coin.type === 'president') { flashColor = 0x0000FF; // Blue } else { flashColor = 0xFFFFFF; // White } LK.effects.flashObject(coin, flashColor, 300); LK.getSound('coinCollect').play(); // Show bonus text for memecoin pickup (+100 BONUS) if (coin.type === 'memecoin') { // Create bonus text in top third of screen var bonusText = new BonusText(100); bonusText.x = 2048 / 2; bonusText.y = 2732 / 3; game.addChild(bonusText); bonusText.animate(); // Add extra 100 points for memecoin score += 100; scoreTxt.setText('SCORE: ' + Math.floor(score)); } // Simplify animation logic to reduce CPU load var scaleValue, durationValue; // Compute scale based on coin type - simplified to 3 categories for better performance if (coin.type === 'president' || coin.type === 'cat') { scaleValue = 6; // High value coins durationValue = 800; } else if (coin.type === 'volt' || coin.type === 'loki' || coin.type === 'pepe') { scaleValue = 4; // Medium value coins durationValue = 600; } else { scaleValue = 2; // Regular coins durationValue = 400; } tween(coin, { alpha: 0, scaleX: scaleValue, scaleY: scaleValue }, { duration: durationValue, onFinish: function onFinish() { coin.active = false; } }); } else if (!player.intersects(coin)) { // Reset intersection state when player is no longer intersecting the coin coin.lastWasIntersecting = false; } } // Check shark collisions if (player.y > 2732 - player.height && player.intersects(shark) || player.y > 2732 - player.height && player.intersects(shark2)) { // Game over if player touches either shark gameOver(); } } // Clean up inactive entities function cleanupEntities() { // Remove inactive signals for (var i = signals.length - 1; i >= 0; i--) { // Only remove signals if player is not jumping or falling if (!signals[i].active) { // Add extra conditions to ensure we don't remove signals that player might need to land on if (!player || !player.isJumping && player.vy <= 0) { signals[i].destroy(); signals.splice(i, 1); } } } // Remove inactive coins for (var i = memecoins.length - 1; i >= 0; i--) { if (!memecoins[i].active) { memecoins[i].destroy(); memecoins.splice(i, 1); } } // Remove inactive bonus texts for (var i = game.children.length - 1; i >= 0; i--) { if (game.children[i] instanceof BonusText && !game.children[i].active) { game.children[i].destroy(); } } // Remove completed lightning effects for (var i = game.children.length - 1; i >= 0; i--) { if (game.children[i] instanceof LightningEffect && game.children[i].complete) { game.children[i].destroy(); } } } // Game over function gameOver() { isGameActive = false; player.isDead = true; // Play crash sound LK.getSound('crash').play(); // Flash screen red LK.effects.flashScreen(0xFF0000, 500); // Update high score var highScore = storage.highScore || 0; if (score > highScore) { storage.highScore = score; highScoreTxt.setText('HIGH SCORE: ' + Math.floor(score)); } // Show game over after a short delay LK.setTimeout(function () { LK.showGameOver(); }, 800); } // Input handling game.down = function (x, y, obj) { // Jump when tapping/clicking player.jump(); }; // Main game update loop game.update = function () { if (!isGameActive) { return; } // Update score based on distance distanceTraveled += gameSpeed; score += scoreIncrement * gameSpeed; // Update score display occasionally to avoid text updates every frame // Only update every 10 points now (was 5) to further reduce text rendering if (Math.floor(score) % 10 === 0 && scoreTxt.text !== 'SCORE: ' + Math.floor(score)) { scoreTxt.setText('SCORE: ' + Math.floor(score)); } // Increase game speed gradually gameSpeed = 1 + distanceTraveled / 10000; // Check if sharks should start orbiting (every 10-20 seconds) var currentTime = Date.now(); // Tornado effect is turned off // Record the time to prevent other tornado-related code from running lastOrbitTime = currentTime; // Update orbit motion if active if (orbiting) { // Increase orbit angle orbitAngle += 0.02; // After 3 complete rotations (6*PI), start tornado and stop orbiting if (orbitAngle > Math.PI * 6) { orbiting = false; } } // Check tornado duration if (tornadoActive) { var tornado = game.children.find(function (child) { return child instanceof Tornado; }); if (tornado) { var currentTime = Date.now(); var elapsedTime = currentTime - tornado.startTime; // After 3 seconds, set to silent mode but keep tornado active if (elapsedTime >= tornado.duration && !tornado.isSilent) { tornado.isSilent = true; // Make particles semi-transparent in silent mode for (var i = 0; i < tornado.particles.length; i++) { tween(tornado.particles[i], { alpha: 0.3 }, { duration: 500, easing: tween.easeInOut }); } } } } // Update all tornado affected signals for (var i = 0; i < tornadoSignals.length; i++) { var signal = tornadoSignals[i]; if (signal.active && signal.falling) { // Add side-to-side motion to falling signals if (signal.sideSpeed !== undefined) { signal.x += signal.sideSpeed; // Gradually reduce side speed signal.sideSpeed *= 0.98; } } } // Spawn entities spawnSignal(); spawnMemecoin(); // Check collisions checkCollisions(); // Remove inactive entities cleanupEntities(); // Check if player fell off screen if (player.y > 2732 + 200) { gameOver(); } // Update cloud animation for (var i = 0; i < game.children.length; i++) { if (game.children[i] instanceof CloudEffect) { game.children[i].update(); } } // Lightning strike effect is disabled to avoid lag // The code that was here created lightning strikes every 5 seconds }; // Initialize UI and game initUI(); initGame(); // Track signal status globally game.onEntityDestroyed = function (entity) { // Keep track of destroyed entities if needed if (entity instanceof Signal && player) { // Keep signals around longer if player is falling or jumping if (player.isJumping || player.vy > 0) { // Return false to prevent deletion when player might need to land return false; } } return true; };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BonusText = Container.expand(function (amount, type) {
var self = Container.call(this);
// Create bonus text display
var bonusAmount = amount || 100;
var bonusText = new Text2('+' + bonusAmount + ' BONUS', {
size: 100,
fill: 0xFFD700 // Gold color
});
bonusText.anchor.set(0.5, 0.5);
self.addChild(bonusText);
// Start animation
self.animate = function () {
// Initial state
self.alpha = 0;
self.scale.set(0.5);
// Animation sequence - fade in with scale up
tween(self, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Pulse effect
tween(bonusText, {
tint: 0xFFFFFF // Flash to white
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
tween(bonusText, {
tint: 0xFFD700 // Back to gold
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fade out with float up
tween(self, {
alpha: 0,
y: self.y - 100
}, {
duration: 800,
easing: tween.linear,
onFinish: function onFinish() {
self.active = false;
}
});
}
});
}
});
}
});
};
self.active = true;
return self;
});
var CloudEffect = Container.expand(function () {
var self = Container.call(this);
// Create cloud parts using centerCircle assets
self.cloudParts = [];
var numParts = 4; // Reduced from 6 to 4 parts for better performance
var cloudColors = [0xFFFFFF, 0xF0F0F0, 0xE0E0E0];
// Create main cloud parts
for (var i = 0; i < numParts; i++) {
var part = new Container();
var size = 120 + Math.random() * 160; // Increased size to compensate for fewer parts
var cloudPart = part.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 77,
// normalizing to centerCircle's 77px size
scaleY: size / 77,
tint: cloudColors[Math.floor(Math.random() * cloudColors.length)]
});
// Position parts to form cloud shape
var angle = i / numParts * Math.PI;
var radius = 80;
part.x = Math.cos(angle) * radius;
part.y = Math.sin(angle) * radius / 2;
part.baseX = part.x;
part.baseY = part.y;
part.offset = Math.random() * Math.PI * 2; // Random starting phase
part.speed = 0.008 + Math.random() * 0.008; // Slightly reduced movement speed
part.amplitude = 5 + Math.random() * 8; // Slightly reduced movement amount
self.cloudParts.push(part);
self.addChild(part);
}
// Start animation
self.animate = function () {
// Animate only a subset of cloud parts at a time to reduce tween overhead
// This creates a more natural, less synchronous animation anyway
var partCount = self.cloudParts.length;
var animateCount = Math.min(3, partCount); // Only animate up to 3 parts at once
var startIdx = Math.floor(Math.random() * partCount);
for (var i = 0; i < animateCount; i++) {
var idx = (startIdx + i) % partCount;
var part = self.cloudParts[idx];
// Create gentle billowing animation for each part
tween(part, {
scaleX: 0.9 + Math.random() * 0.2,
scaleY: 0.9 + Math.random() * 0.2,
alpha: 0.8 + Math.random() * 0.2
}, {
duration: 2000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue animation after a short delay to stagger animations
LK.setTimeout(function () {
self.animate();
}, 200 + Math.random() * 300);
}
});
}
};
// Update cloud parts movement
self.update = function () {
// Only update every 2nd frame to reduce computational load
if (LK.ticks % 2 === 0) {
for (var i = 0; i < self.cloudParts.length; i++) {
var part = self.cloudParts[i];
// Gentle floating movement
part.x = part.baseX + Math.sin(part.offset + LK.ticks * part.speed) * part.amplitude;
part.y = part.baseY + Math.cos(part.offset + LK.ticks * part.speed) * part.amplitude / 2;
}
}
};
return self;
});
var LightningEffect = Container.expand(function (targetSignal) {
var self = Container.call(this);
// Lightning properties
self.targetSignal = targetSignal;
self.active = true;
self.segments = 15; // Increased number of lightning segments for more detail
self.maxOffset = 40; // Increased maximum zigzag offset for more dramatic effect
self.width = 8; // Thinner lightning bolt width for more realistic appearance
self.complete = false;
self.lightningColor = 0xFFFF33; // Brighter neon yellow color for more realistic lightning
// Create lightning segments using centerCircle assets
self.lightningParts = [];
// Create the lightning effect
self.createLightning = function () {
// Clear any existing lightning parts
for (var i = 0; i < self.lightningParts.length; i++) {
self.removeChild(self.lightningParts[i]);
}
self.lightningParts = [];
// Calculate start and end positions
var startX = self.x;
var startY = self.y;
var endX = self.targetSignal.x;
var endY = self.targetSignal.y;
// Create realistic lightning line with zigzag effect
// Create previous point for connecting line segments
var prevX = startX;
var prevY = startY;
// Calculate slight inclination for lightning (about 10-15 degrees)
var inclineAngle = Math.random() * 0.05 + 0.15; // 0.15-0.2 radians (~8-11 degrees)
var inclineOffsetX = (endY - startY) * Math.tan(inclineAngle);
endX += inclineOffsetX; // Adjust end X with inclination
for (var i = 0; i < self.segments; i++) {
var segment = new Container();
var t = i / (self.segments - 1); // Position along the line (0 to 1)
// Calculate base position along the slightly inclined line
var baseX = startX + (endX - startX) * t;
var baseY = startY + (endY - startY) * t;
// Add random zigzag offset (except for first and last segments)
var offsetX = i > 0 && i < self.segments - 1 ? (Math.random() * 2 - 1) * self.maxOffset : 0;
var offsetY = i > 0 && i < self.segments - 1 ? (Math.random() * 2 - 1) * self.maxOffset : 0;
// Set the segment position
segment.x = baseX + offsetX;
segment.y = baseY + offsetY;
// Create a lightning segment using the LL asset instead of a circle
if (i > 0) {
// For each segment after the first, create a lightning element pointing to previous point
var dx = prevX - segment.x;
var dy = prevY - segment.y;
var length = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
// Create lightning using LL asset
var lightningPart = segment.attachAsset('ll', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: length / 100,
scaleY: 1.0,
rotation: Math.PI,
// Rotate by 180 degrees
tint: 0xFFFF33,
// Brighter neon yellow for more realistic lightning
x: -300 // Move lightning lines left by 300 units
});
// Rotate to point toward previous segment
lightningPart.rotation = angle;
} else {
// First segment is just a small point
var lightningPart = segment.attachAsset('ll', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
tint: 0xFFFF33 // Brighter neon yellow
});
}
// Save current point as previous for next segment
prevX = segment.x;
prevY = segment.y;
self.lightningParts.push(segment);
self.addChild(segment);
}
};
// Animate the lightning
self.animate = function () {
// First create the lightning
self.createLightning();
// Initial lightning flash bright
for (var i = 0; i < self.lightningParts.length; i++) {
var part = self.lightningParts[i];
part.alpha = 0.7; // Start slightly dimmer
}
// Bright flash effect sequence
// First flash - quickly brighten
LK.setTimeout(function () {
for (var i = 0; i < self.lightningParts.length; i++) {
tween(self.lightningParts[i], {
alpha: 1.0,
// Full brightness
scaleX: self.lightningParts[i].scale.x * 1.2,
// Expand slightly
scaleY: self.lightningParts[i].scale.y * 1.2
}, {
duration: 50,
easing: tween.easeOut
});
}
// Second flash - create main strike effect
LK.setTimeout(function () {
// Create explosion effect at target
if (self.targetSignal && self.targetSignal.active) {
// Flash the target white
LK.effects.flashObject(self.targetSignal, 0xFFFFFF, 200);
// Make the target signal fall and destroy it
self.targetSignal.falling = true;
self.targetSignal.fallSpeed = 2;
// Add some sideways momentum
self.targetSignal.sideSpeed = Math.random() * 10 - 5;
// Add to tornado signals to track it with same logic
tornadoSignals.push(self.targetSignal);
}
// Third flash - dim slightly before final fade
LK.setTimeout(function () {
for (var i = 0; i < self.lightningParts.length; i++) {
tween(self.lightningParts[i], {
alpha: 0.8,
scaleX: self.lightningParts[i].scale.x * 0.9,
scaleY: self.lightningParts[i].scale.y * 0.9
}, {
duration: 70,
easing: tween.easeInOut
});
}
// Keep lightning visible without fading out
LK.setTimeout(function () {
// Instead of fading out, make lightning slightly brighter
for (var i = 0; i < self.lightningParts.length; i++) {
var part = self.lightningParts[i];
tween(part, {
alpha: 0.9,
scaleX: part.scale.x * 1.1,
// Slightly larger to make it more visible
scaleY: part.scale.y * 1.1
}, {
duration: 150,
easing: tween.easeOut
});
}
// Mark lightning as complete after a longer duration (1500ms instead of 200ms)
LK.setTimeout(function () {
self.complete = true;
self.active = false;
}, 1500);
}, 80);
}, 50);
}, 70);
}, 30);
};
return self;
});
var Memecoin = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'regular';
var assetId;
if (self.type === 'dogmeme') {
assetId = 'dogmeme';
} else if (self.type === 'shib') {
assetId = 'shib';
} else if (self.type === 'pepe') {
assetId = 'pepe';
} else if (self.type === 'loki') {
assetId = 'loki';
} else if (self.type === 'volt') {
assetId = 'volt';
} else if (self.type === 'cat') {
assetId = 'cat';
} else if (self.type === 'president') {
assetId = 'president';
} else {
assetId = 'memecoin';
}
var coinGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.active = true;
self.collected = false;
self.value = self.type === 'dogmeme' ? 100 : self.type === 'shib' ? 150 : self.type === 'pepe' ? 200 : self.type === 'loki' ? 250 : self.type === 'volt' ? 300 : self.type === 'cat' ? 350 : self.type === 'president' ? 400 : 50; // President worth more points than cat
self.update = function () {
if (!self.active) {
return;
}
self.x -= self.speed;
// Rotate coin counterclockwise (negative value for opposite direction)
coinGraphics.rotation -= 0.03;
// Remove when off screen
if (self.x < -100) {
self.active = false;
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.vx = 0;
self.vy = 0;
self.gravity = 0.5;
self.jumpPower = -25; // Increased jump power from -15 to -25
self.isJumping = false;
self.isDead = false;
self.lastWasIntersecting = false;
self.jump = function () {
// Find if player is standing on a green signal
var standingOnGreenSignal = signals.some(function (s) {
return player && s.type === 'green' && player.intersects(s) && Math.abs(player.y - (s.y - 275)) < 10;
});
if (!self.isDead && (self.vy === 0 || standingOnGreenSignal)) {
self.vy = self.jumpPower;
self.isJumping = true;
LK.getSound('jump').play();
}
};
self.update = function () {
if (self.isDead) {}
self.lastY = self.y;
// Apply gravity
self.vy += self.gravity;
// Apply movement
self.y += self.vy;
// Track if player is jumping or falling
self.isJumping = self.vy !== 0;
// Ground collision detection
if (self.y > 2732 - playerGraphics.height / 2) {
// Bottom of screen
self.y = 2732 - playerGraphics.height / 2;
self.vy = 0;
self.isJumping = false;
}
};
return self;
});
var Shark = Container.expand(function () {
var self = Container.call(this);
var sharkGraphics = self.attachAsset('shark', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.direction = 1;
self.centerY = 2732 - sharkGraphics.height / 2 + 100; // Move sharks down by 100 units
self.update = function () {
// If sharks are orbiting, follow orbit pattern instead of normal movement
if (orbiting) {
// Keep track of which shark this is (1 or 2)
var sharkIndex = this === shark2 ? 1 : 0;
var offsetAngle = sharkIndex * Math.PI; // 180 degrees offset for second shark
// Calculate position based on orbit angle
var orbitRadius = 150; // Reduced radius of orbit from 200 to 150 for tighter movement
// Increase orbit speed during tornado effect
var orbitSpeed = 2.5; // Increased orbit speed factor
self.x = orbitCenterX + Math.cos(orbitAngle * orbitSpeed + offsetAngle) * orbitRadius;
self.y = orbitCenterY + Math.sin(orbitAngle * orbitSpeed + offsetAngle) * orbitRadius / 2; // More compressed elliptical orbit
// Set shark orientation based on movement direction
var movingRight = Math.cos(orbitAngle * orbitSpeed + offsetAngle + Math.PI / 2) > 0;
sharkGraphics.scale.x = movingRight ? 1 : -1;
return; // Skip normal movement logic when orbiting
}
// Normal movement when not orbiting
// Keep shark at constant y position
self.y = self.centerY;
// Check if player exists and has fallen to shark's height level
if (player && player.y > 2732 - 350) {
// Shark hunts player - move toward player position
self.direction = player.x > self.x ? 1 : -1;
self.speed = 8; // Increase speed when hunting
// Set correct shark orientation based on direction
sharkGraphics.scale.x = self.direction;
} else {
// Normal back-and-forth movement when player isn't at shark level
self.speed = 5;
// Check if this is the second shark (compare to the global shark2 reference)
if (this === shark2) {
// Second shark moves in opposite pattern to first shark (alternating pattern)
// Start swimming from right to left
if (self.x <= 200 && self.direction === -1) {
self.direction = 1;
sharkGraphics.scale.x = 1;
} else if (self.x >= 1900 && self.direction === 1) {
self.direction = -1;
sharkGraphics.scale.x = -1;
}
} else {
// First shark patrols from left to right first
if (self.x >= 1800 && self.direction === 1) {
self.direction = -1;
sharkGraphics.scale.x = -1;
} else if (self.x <= 200 && self.direction === -1) {
self.direction = 1;
sharkGraphics.scale.x = 1;
}
}
}
// Apply movement
self.x += self.speed * self.direction;
};
return self;
});
var Signal = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'green';
var assetId = self.type === 'green' ? 'greenSignal' : 'redSignal';
// Generate random width variation factor between 0.6 and 1.4 for more dramatic variation
var widthVariation = 0.6 + Math.random() * 0.8;
var signalGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1 / 1.2 * widthVariation,
scaleY: 1 / 1.2
});
// Rotate signal by 90 degrees to the left
signalGraphics.rotation = -Math.PI / 2; // -90 degrees in radians
self.speed = 3.5;
self.active = true;
self.shineTimer = 0;
self.isShining = false;
// Store the width variation for calculations
self.widthVariation = widthVariation;
// Create shine effect
self.startShine = function () {
if (!self.isShining) {
self.isShining = true;
var baseColor = self.type === 'green' ? 0xFFFFFF : 0xFFFFFF;
// Shine effect with tween
tween(signalGraphics, {
alpha: 0.8
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
tween(signalGraphics, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isShining = false;
}
});
}
});
}
};
// Turn green signal golden when touched by player
// Create shimmer animation with color tint alternation
self.startShimmerAnimation = function (targetGraphics) {
self.shimmerAnimationActive = true;
// Golden to bright white shimmer
function shimmerCycle() {
if (!self.shimmerAnimationActive) {
return;
}
// Shimmer to bright white
tween(targetGraphics, {
tint: 0xFFFFFF // Bright white
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.shimmerAnimationActive) {
return;
}
// Return to gold
tween(targetGraphics, {
tint: 0xFFD700 // Gold color
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (self.shimmerAnimationActive) {
// Continue animation cycle
shimmerCycle();
}
}
});
}
});
}
// Start shimmer cycle
shimmerCycle();
};
self.turnGolden = function () {
if (self.type === 'green' && !self.isGolden) {
self.isGolden = true;
var goldenSignalGraphics = LK.getAsset('goldenSignal', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1 / 1.2,
scaleY: 1 / 1.2
});
// Match the rotation of the current signal
goldenSignalGraphics.rotation = signalGraphics.rotation;
// Animation sequence for golden transformation
tween(signalGraphics, {
alpha: 0
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Replace the green signal with golden one
self.removeChild(signalGraphics);
self.addChild(goldenSignalGraphics);
// Start with small scale and expand
goldenSignalGraphics.scale.set(0.5, 0.5);
goldenSignalGraphics.alpha = 0.7;
// Grow and fade in
tween(goldenSignalGraphics, {
scaleX: 1 / 1.2,
scaleY: 1 / 1.2,
alpha: 1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Start shimmer animation after the initial grow animation
self.startShimmerAnimation(goldenSignalGraphics);
}
});
}
});
}
};
self.falling = false;
self.fallSpeed = 0;
self.update = function () {
if (!self.active) {
// Clean up shimmer animation if it's running
if (self.shimmerAnimationActive) {
self.shimmerAnimationActive = false;
}
return;
}
// Handle falling behavior for red signals
if (self.falling) {
self.fallSpeed += 0.5; // Gravity effect
self.y += self.fallSpeed;
// Rotate while falling
self.children[0].rotation += 0.05;
// Apply red flash effect when signal starts falling (if it hasn't been flashed already)
if (!self.flashedRed && self.fallSpeed < 3) {
self.flashedRed = true;
LK.effects.flashObject(self, 0xff4500, 500); // Flash red
}
// Deactivate when off bottom of screen
if (self.y > 2732 + 100) {
self.active = false;
}
return; // Skip normal behavior when falling
}
self.x -= self.speed;
// Add random shine effect occasionally
self.shineTimer++;
if (self.shineTimer > 60 && Math.random() < 0.05) {
self.startShine();
self.shineTimer = 0;
}
// Check if red signal is touching a shark
if (self.type === 'red' && (shark && self.intersects(shark) || shark2 && self.intersects(shark2))) {
// Save the original y position before moving
var startY = self.y;
// Calculate target position (random amount between 100 and 1000 upward, at half speed)
var moveAmount = (100 + Math.random() * 900) / 2; // Reduced to half
var targetY = startY - moveAmount;
// Change color to green
self.type = 'green';
// Replace the red signal graphic with a green one
var oldGraphics = self.children[0];
var oldRotation = oldGraphics.rotation;
var oldScaleX = oldGraphics.scale.x;
var oldScaleY = oldGraphics.scale.y;
self.removeChild(oldGraphics);
var greenGraphics = self.attachAsset('greenSignal', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: oldScaleX,
scaleY: oldScaleY
});
// Apply the same rotation as the original signal
greenGraphics.rotation = oldRotation;
// Make the signal shine to highlight the change
self.startShine();
// Use tween for smooth upward movement with longer duration for more continuous motion
tween(self, {
y: targetY
}, {
duration: 1600,
// Doubled duration for slower, more continuous movement
// Duration of upward movement in ms
easing: tween.easeOutQuad // Smoother deceleration for continuous movement
});
}
// Check for signals getting too close to sharks
// Shark is positioned close to bottom of screen (around 2732 - 100)
var sharkYPosition = 2732 - 400; // Position above shark's height
if (self.y > sharkYPosition) {
// Move signal upward to avoid sharks
self.y = sharkYPosition - Math.random() * 100;
self.startShine(); // Visual indicator that signal moved
}
// Check for overlapping signals (only for active signals on screen)
for (var i = 0; i < signals.length; i++) {
var otherSignal = signals[i];
// Skip self and inactive signals
if (otherSignal !== self && otherSignal.active) {
// Check if signals are close to each other
var xDistance = Math.abs(self.x - otherSignal.x);
var yDistance = Math.abs(self.y - otherSignal.y);
// If signals are overlapping or very close (within 20px)
if (xDistance < 20 && yDistance < 20) {
// Move this signal to avoid overlap
if (self.type === 'green') {
self.y -= 30; // Move green signals upward
} else {
self.y += 30; // Move red signals downward
}
// Trigger shine effect to show adjustment
self.startShine();
}
}
}
// Remove when off screen only if player is not jumping/falling
// Make sure we keep signals visible while player is falling
if (self.x < -200) {
// Only deactivate if player doesn't exist or if player is not jumping/falling
if (!player || !player.isJumping && player.vy <= 0) {
self.active = false;
}
}
};
return self;
});
var Tornado = Container.expand(function () {
var self = Container.call(this);
// Create tornado graphics
self.particles = [];
var numParticles = 15; // Reduced from 30 to 15 particles to improve performance
var colors = [0x87CEEB, 0x00BFFF, 0x1E90FF, 0x4682B4];
// Create particles that make up the tornado
for (var i = 0; i < numParticles; i++) {
var particle = new Container();
var size = 40 + Math.random() * 70; // Slightly larger particles to compensate for fewer of them
var shape = particle.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: size / 100,
scaleY: size / 100,
tint: colors[Math.floor(Math.random() * colors.length)]
});
// Initial position
particle.radius = 50 + i * 8; // Increased spacing between particles
particle.angle = Math.random() * Math.PI * 2;
particle.rotationSpeed = (0.02 + Math.random() * 0.03) * (Math.random() > 0.5 ? 1 : -1); // Reduced rotation speed
particle.alpha = 0.5 + Math.random() * 0.5;
// Set initial positions
particle.x = Math.cos(particle.angle) * particle.radius;
particle.y = Math.sin(particle.angle) * particle.radius;
self.particles.push(particle);
self.addChild(particle);
}
self.tornadoHeight = 0; // Current height of the tornado
self.tornadoSpeed = 10; // How fast the tornado rises
self.isActive = true;
self.startY = 2732 - 400; // Start near the sharks
self.topWidth = 50; // Width at top (narrow)
self.bottomWidth = 500; // Width at bottom (wide)
self.signalLimit = 3; // Fixed limit of exactly 3 signals
self.signalsDestroyed = 0; // Counter for how many signals destroyed
self.startTime = Date.now(); // Track when tornado started
self.duration = 3000; // Duration in milliseconds (3 seconds)
self.isSilent = false; // Track if tornado is in silent mode
self.update = function () {
if (!self.isActive) {
return;
}
// Rise up
self.tornadoHeight += self.tornadoSpeed;
// Only update particles every other frame to reduce computational load
if (LK.ticks % 2 === 0) {
// Update all particles
for (var i = 0; i < self.particles.length; i++) {
var p = self.particles[i];
// Calculate relative height position (0-1)
var relativeHeight = i / self.particles.length;
// Width decreases as we go up
var currentWidth = self.bottomWidth - (self.bottomWidth - self.topWidth) * relativeHeight;
// Update angle for rotation - reduced rotation speed
p.angle += p.rotationSpeed;
// Position - wider at bottom, narrower at top
p.x = Math.cos(p.angle) * (p.radius * (currentWidth / self.bottomWidth));
p.y = Math.sin(p.angle) * (p.radius * (currentWidth / self.bottomWidth));
// Adjust vertical position based on tornado height
p.y -= self.tornadoHeight * relativeHeight;
}
}
// Check for signal collisions - only do this every 3 frames to reduce CPU load
if (tornadoActive && self.signalsDestroyed < self.signalLimit && LK.ticks % 3 === 0) {
for (var i = 0; i < signals.length; i++) {
var signal = signals[i];
if (signal.active && !signal.falling && !tornadoSignals.includes(signal)) {
// Check if signal is near the tornado
var dx = signal.x - self.x;
var dy = signal.y - (self.y - self.tornadoHeight / 2);
var distance = Math.sqrt(dx * dx + dy * dy);
// If signal is within tornado's effect range and we haven't hit the limit
if (distance < 300 && self.signalsDestroyed < self.signalLimit) {
// Add to affected signals
tornadoSignals.push(signal);
// Make it fall
signal.falling = true;
signal.fallSpeed = 2;
signal.startShine();
// Add some sideways momentum based on tornado rotation
signal.sideSpeed = Math.random() * 10 - 5;
// Increment the counter
self.signalsDestroyed++;
}
}
}
}
};
return self;
});
/****
* Initialize Game
****/
// Golden letter shimmer animation
var game = new LK.Game({
backgroundColor: 0x00142A
});
/****
* Game Code
****/
// Game variables
// Golden letter shimmer animation
var LetterShimmerEffect = function LetterShimmerEffect(letterText) {
// Create shimmer animation cycle
function startShimmer() {
// Shimmer to bright white
tween(letterText, {
tint: 0xFFFFFF // Bright white
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Return to gold
tween(letterText, {
tint: 0xFFD700 // Gold color
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue animation cycle
startShimmer();
}
});
}
});
}
// Set initial state and start shimmer
letterText.tint = 0xFFD700; // Gold color
startShimmer();
};
var player;
var signals = [];
var memecoins = [];
var shark;
var shark2;
var isGameActive = true;
var gameSpeed = 1;
var lastSignalTime = 0;
var lastCoinTime = 0;
var score = 0;
var scoreIncrement = 0.1;
var distanceTraveled = 0;
var memecoinLetters = []; // Reference to MEMECOIN letters
var nextLetterIndex = 0; // Track which letter to illuminate next
var tornadoActive = false;
var tornadoSignals = []; // Signals caught in tornado
var lastOrbitTime = 0; // Track when sharks last started orbiting
var orbiting = false; // Track if sharks are currently orbiting
var orbitAngle = 0; // Current orbit angle
var orbitCenterX = 0; // X center of orbit
var orbitCenterY = 0; // Y center of orbit
var tornadoY = 0; // Current Y position of tornado
var coinRound = {
regular: false,
dogmeme: false,
shib: false,
pepe: false,
loki: false,
volt: false,
cat: false,
president: false
};
// UI elements
var scoreTxt;
var highScoreTxt;
// Initialize UI
function initUI() {
// High score text
var highScore = storage.highScore || 0;
highScoreTxt = new Text2('HIGH SCORE: ' + Math.floor(highScore), {
size: 50,
fill: 0xFFD700
});
highScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(highScoreTxt);
highScoreTxt.y = 0;
// Score text
scoreTxt = new Text2('SCORE: 0', {
size: 70,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.y = 60;
// MEMECOIN letters container
var memeLettersContainer = new Container();
memeLettersContainer.x = 0;
memeLettersContainer.y = 130; // Match the spacing of 70px between high score and score texts
LK.gui.top.addChild(memeLettersContainer);
// Initialize empty letter display
memecoinLetters = []; // Use global variable to store letters
var memecoinWord = "MEMECOIN";
for (var i = 0; i < memecoinWord.length; i++) {
var letterText = new Text2(memecoinWord[i], {
size: 60,
fill: 0xAAAAAA // Lighter gray (not collected)
});
letterText.anchor.set(0.5, 0);
letterText.x = (i - memecoinWord.length / 2 + 0.5) * 60 + 5; // Center the letters
letterText.collected = false;
memecoinLetters.push(letterText);
memeLettersContainer.addChild(letterText);
}
}
// Initialize game world
function initGame() {
isGameActive = true;
score = 0;
distanceTraveled = 0;
gameSpeed = 1;
// Reset coin round tracking
coinRound = {
regular: false,
dogmeme: false,
shib: false,
pepe: false,
loki: false,
volt: false,
cat: false,
president: false
};
// Reset MEMECOIN letters
nextLetterIndex = 0;
if (memecoinLetters && memecoinLetters.length > 0) {
for (var i = 0; i < memecoinLetters.length; i++) {
memecoinLetters[i].tint = 0x444444; // Reset to dark gray
memecoinLetters[i].collected = false;
}
}
// Create player at top left, ready to fall
player = new Player();
player.x = 200;
player.y = 100;
player.vy = 5; // Start with downward velocity
game.addChild(player);
// Create sharks
shark = new Shark();
shark.y = 2732 - 100;
game.addChild(shark);
// Create second shark with opposite direction compared to first shark
shark2 = new Shark();
shark2.x = 1500; // Position second shark on the right side
shark2.y = 2732 - 100;
shark2.direction = -1; // Start with opposite direction to make sharks swim alternately
// Set correct orientation based on direction
shark2.children[0].scale.x = -1;
game.addChild(shark2);
// Clear signals and coins
clearEntities();
// Create initial signals that have already reached the left side
var recentHeights = [];
var heightThreshold = 100; // Further increased threshold to force more staggering
for (var i = 0; i < 44; i++) {
var signalType = Math.random() < 0.5 ? 'green' : 'red';
// For signals that are lower than the previous, make them red
// For signals that are higher than the previous, make them green
var lastY = 1500;
if (signals.length > 0) {
lastY = signals[signals.length - 1].y;
}
var newY;
var willBeHigher = Math.random() < 0.5;
if (willBeHigher) {
// More pronounced ascent with increased height differences
var heightDiff = 150 + Math.random() * 180;
newY = lastY - heightDiff;
signalType = 'green';
} else {
// More pronounced descent with increased height differences
var heightDiff = 150 + Math.random() * 180;
newY = lastY + heightDiff;
signalType = 'red';
}
// Check if this height is too similar to recent heights
var isTooSimilar = recentHeights.length > 0 && recentHeights.some(function (height) {
return Math.abs(newY - height) < heightThreshold;
});
// If too similar, adjust the height with larger offsets for more staggering
if (isTooSimilar) {
if (signalType === 'green') {
newY -= 120 + Math.random() * 80; // Increased offset for more staggering
} else {
newY += 120 + Math.random() * 80; // Increased offset for more staggering
}
}
// Ensure signal stays within playable area and away from sharks
// Keep signals at least 600px above sharks (which are at 2732 - 100)
newY = Math.max(800, Math.min(2000, newY));
// Store this height for future comparison
recentHeights.push(newY);
if (recentHeights.length > 10) {
recentHeights.shift(); // Keep only last 10 heights
}
var signal = new Signal(signalType);
// Position signals already at the left side of the screen with horizontal variation
var xOffset = Math.random() * 30 - 15; // Random horizontal offset between -15 and +15
signal.x = -150 + i * 50 + xOffset; // Distribute signals across the left edge with variations
signal.y = newY;
signals.push(signal);
// Make sure signals are placed behind the player
if (player) {
var playerIndex = game.getChildIndex(player);
game.addChildAt(signal, playerIndex);
} else {
game.addChild(signal);
}
}
// Start game music in a loop (loop is true by default, but explicitly set for clarity)
LK.playMusic('gameMusic', {
loop: true
});
// Add cloud to top right corner, but moved left by 100 units and up by 77 units
var cloud = new CloudEffect();
cloud.x = 2048 - 250; // Position in top right, offset by 250px (moved left by 100 units)
cloud.y = 123; // Position cloud 77 units higher (200 - 77 = 123)
cloud.scale.set(1.2); // Slightly larger cloud
game.addChild(cloud);
cloud.animate(); // Start the billowing animation
}
// Clear all entities
function clearEntities() {
for (var i = signals.length - 1; i >= 0; i--) {
signals[i].destroy();
signals.splice(i, 1);
}
for (var i = memecoins.length - 1; i >= 0; i--) {
memecoins[i].destroy();
memecoins.splice(i, 1);
}
}
// Spawn signals
function spawnSignal() {
var currentTime = Date.now();
// Spawn signals at intervals that maintain consistent spacing regardless of score
// At higher scores (1000+), we need to keep a minimum time between signals
var baseInterval = 300;
var minInterval = 150;
var adjustedInterval = score > 1000 ? Math.max(minInterval, baseInterval / gameSpeed) : baseInterval / gameSpeed;
if (currentTime - lastSignalTime > adjustedInterval) {
// This ensures spacing remains consistent even at high scores
var signal;
var lastSignalY = 1500; // Default Y position if no signals exist
// Get the Y position of the last signal to compare with
if (signals.length > 0) {
var lastSignal = signals[signals.length - 1];
lastSignalY = lastSignal.y;
}
// Calculate a new Y position
var newY;
var willBeHigher = Math.random() < 0.30; // 30% chance to go higher, 70% chance for red signals
// Count consecutive red signals
var redSignalCount = 0;
// Find the last signal of each type for height comparison
var lastGreenY = null;
var lastRedY = null;
// Track recent signal heights to prevent repetition
var recentHeights = [];
var heightThreshold = 100; // Further increased threshold to force more staggering
// Collect the last few signal heights for comparison
for (var i = signals.length - 1; i >= Math.max(0, signals.length - 15); i--) {
recentHeights.push(signals[i].y);
}
// Find the last green and red signal Y positions
for (var i = signals.length - 1; i >= 0; i--) {
if (signals[i].type === 'green' && lastGreenY === null) {
lastGreenY = signals[i].y;
}
if (signals[i].type === 'red' && lastRedY === null) {
lastRedY = signals[i].y;
}
if (lastGreenY !== null && lastRedY !== null) {
break;
}
}
for (var i = signals.length - 1; i >= Math.max(0, signals.length - 5); i--) {
if (signals[i] && signals[i].type === 'red') {
redSignalCount++;
} else {
break;
}
}
// Force green signal if we've had 5 reds in a row
if (redSignalCount >= 5) {
// Force green signal (must be higher) but with more gradual ascent
newY = lastSignalY - (60 + Math.random() * 80);
signal = new Signal('green');
} else if (willBeHigher) {
// This signal will be higher (ascending)
// More pronounced height difference for green signals
var heightDiff = 80 + Math.random() * 120; // Increased for more staggering
if (lastGreenY !== null && Math.abs(lastSignalY - heightDiff - lastGreenY) > 200) {
// Increased maximum height change between signals of same color
heightDiff = Math.min(heightDiff, 200);
}
newY = lastSignalY - heightDiff;
// Higher signals must be green
signal = new Signal('green');
} else {
// This signal will be lower (descending)
// More pronounced height difference for red signals
var heightDiff = 80 + Math.random() * 120; // Increased for more staggering
if (lastRedY !== null && Math.abs(lastSignalY + heightDiff - lastRedY) > 200) {
// Increased maximum height change between signals of same color
heightDiff = Math.min(heightDiff, 200);
}
newY = lastSignalY + heightDiff;
// Check if we would have too many consecutive red signals
var wouldBeConsecutiveReds = redSignalCount + 1;
if (wouldBeConsecutiveReds >= 5) {
// Force this to be a green signal instead with gradual ascent
newY = lastSignalY - (60 + Math.random() * 80);
signal = new Signal('green');
} else {
// Lower signals must be red
signal = new Signal('red');
}
}
// Ensure signal stays within playable area and away from sharks
// Sharks are positioned at bottom (2732 - 100), keep signals at least 600px above sharks
newY = Math.max(800, Math.min(2000, newY)); // Lower maximum to keep away from sharks
// Additional check for signals that might be about to overlap (within 25px)
var potentialOverlap = signals.filter(function (s) {
// Only check signals that are still on screen or about to enter
return s.active && s.x > -200 && s.x < 2300 && Math.abs(s.y - newY) < 25;
});
// If there's potential overlap, adjust the Y position with smaller offsets
if (potentialOverlap.length > 0) {
if (signal.type === 'green') {
newY -= 60 + Math.random() * 30; // Move green signals higher with smaller offset
} else {
newY += 60 + Math.random() * 30; // Move red signals lower with smaller offset
}
// Re-check playable area boundaries
newY = Math.max(800, Math.min(2200, newY));
}
// Add height variation to signals and prevent consecutive similar heights
if (signal.type === 'green') {
// Green signals with cascading heights - increased variations
var greenOffset = Math.random() * 80 + 30; // Increased base offset for green signals
// Check if this height is too similar to recent heights
var isTooSimilar = recentHeights.some(function (height) {
return Math.abs(newY - greenOffset - height) < heightThreshold;
});
// If too similar, use larger offset to ensure more staggering
if (isTooSimilar) {
greenOffset += 70 + Math.random() * 50; // Larger offset for more staggering
}
newY -= greenOffset;
} else if (signal.type === 'red') {
// Red signals with cascading heights - increased variations
var redOffset = Math.random() * 80 + 30; // Increased base offset for red signals
// Check if this height is too similar to recent heights
var isTooSimilar = recentHeights.some(function (height) {
return Math.abs(newY + redOffset - height) < heightThreshold;
});
// If too similar, use larger offset to ensure more staggering
if (isTooSimilar) {
redOffset += 70 + Math.random() * 50; // Larger offset for more staggering
}
newY += redOffset;
}
// Add horizontal variation for more staggered appearance
var horizontalVariation = Math.random() * 60 - 30; // Range of -30 to +30
signal.x = 2048 + 150 + horizontalVariation;
signal.y = newY;
signals.push(signal);
// Add signal behind the player to ensure correct display order
if (player) {
var playerIndex = game.getChildIndex(player);
game.addChildAt(signal, playerIndex);
} else {
game.addChild(signal);
}
lastSignalTime = currentTime;
}
}
// Spawn memecoins
function spawnMemecoin() {
var currentTime = Date.now();
// Spawn coins less frequently than signals - increased interval from 6000 to 12000
if (currentTime - lastCoinTime > 12000 / gameSpeed) {
// Find green signals that are still on screen (or about to enter)
var greenSignals = signals.filter(function (signal) {
return signal.type === 'green' && signal.x > -200 && signal.x < 2300;
});
// Only spawn coin if we have green signals
if (greenSignals.length > 0) {
// Pick a random green signal
var randomGreenSignal = greenSignals[Math.floor(Math.random() * greenSignals.length)];
// Check if we completed a round of all coin types
var roundComplete = coinRound.regular && coinRound.dogmeme && coinRound.shib;
if (roundComplete) {
// Reset the round if all coin types have appeared
coinRound = {
regular: false,
dogmeme: false,
shib: false,
pepe: false,
loki: false,
volt: false,
cat: false,
president: false
};
}
// Determine which coin types are still needed in current round
var availableTypes = [];
if (!coinRound.regular) {
availableTypes.push('regular');
}
if (!coinRound.dogmeme) {
availableTypes.push('dogmeme');
}
if (!coinRound.shib) {
availableTypes.push('shib');
}
if (!coinRound.pepe) {
availableTypes.push('pepe');
}
if (!coinRound.loki) {
availableTypes.push('loki');
}
if (!coinRound.volt) {
availableTypes.push('volt');
}
if (!coinRound.cat) {
availableTypes.push('cat');
}
if (!coinRound.president) {
availableTypes.push('president');
}
// Select a random coin type from available types
var coinType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
// Mark this type as spawned in current round
coinRound[coinType] = true;
var coin = new Memecoin(coinType);
coin.x = 2048 + 150;
// Position coin between MEMECOIN word (y position around 130) and center of screen (2732/2)
// Calculate the available space between MEMECOIN text position and center of screen
var minY = 200; // Safe position below MEMECOIN text (which is at y=130)
var maxY = 2732 / 2; // Center height of the screen
coin.y = minY + Math.random() * (maxY - minY);
memecoins.push(coin);
// Add coin behind the player to ensure correct display order
if (player) {
var playerIndex = game.getChildIndex(player);
game.addChildAt(coin, playerIndex);
} else {
game.addChild(coin);
}
lastCoinTime = currentTime;
}
}
}
// Check collisions between player and signals/coins/shark
function checkCollisions() {
if (!isGameActive) {
return;
}
// Check signal collisions
// Only check signals that are near the player to reduce performance impact
for (var i = 0; i < signals.length; i++) {
var signal = signals[i];
// Quick proximity check before doing expensive intersection test
var dx = Math.abs(signal.x - player.x);
var dy = Math.abs(signal.y - player.y);
// Only do intersection check if signal is within reasonable distance
if (signal.active && dx < 250 && dy < 300 && player.intersects(signal)) {
// Store previous intersection state
var wasIntersecting = player.lastWasIntersecting;
player.lastWasIntersecting = true;
// Only handle collision if this is a new intersection
if (!wasIntersecting) {
// Handle red signal falling when touched anywhere
if (signal.type === 'red' && !signal.falling) {
// Make red signal fall
signal.falling = true;
signal.fallSpeed = 2;
// Trigger laser shine effect when starting to fall
signal.startShine();
continue; // Skip other collision handling for this signal
}
// Check if player is above the signal and falling down
// The signal is rotated 90 degrees left, so we need to detect its height properly
// Signal's height is now its width because of the rotation
var signalHeight = 50 / 1.2; // Original height divided by scale factor
var signalWidth = 500 / 1.2; // Original width divided by scale factor
var playerHeight = 150; // Player height
// Calculate where the top of the signal is considering rotation
if (player.y < signal.y - signalWidth / 2 && player.vy > 0) {
if (signal.type === 'green') {
// Land on green signals but don't automatically jump
// Position player properly on top of the signal
player.y = signal.y - signalWidth / 2 - playerHeight / 2;
player.vy = 0;
player.isJumping = false;
// Trigger laser shine effect on green signal when landing
signal.startShine();
// Turn the green signal golden when touched
signal.turnGolden();
} else if (signal.type === 'red') {
// Pass through red signals, ignore collision
// Don't land on red signals, continue falling
player.vy += player.gravity;
player.isJumping = true;
// Trigger laser shine effect on red signal when passing through
signal.startShine();
}
}
}
} else if (signal.active) {
// If not intersecting this signal, reset lastWasIntersecting
player.lastWasIntersecting = false;
}
}
// Check memecoin collisions
for (var i = 0; i < memecoins.length; i++) {
var coin = memecoins[i];
// Track previous intersection state to prevent multiple collections in one frame
if (!coin.lastWasIntersecting) {
coin.lastWasIntersecting = false;
}
// Find the highest green signal
var highestGreenSignal = null;
var highestY = Infinity;
for (var j = 0; j < signals.length; j++) {
if (signals[j].active && signals[j].type === 'green' && signals[j].y < highestY) {
highestY = signals[j].y;
highestGreenSignal = signals[j];
}
}
// Check if coin intersects with the highest green signal
if (coin.active && !coin.collected && highestGreenSignal && coin.intersects(highestGreenSignal)) {
// Turn the green signal red
highestGreenSignal.type = 'red';
// Replace the green signal graphic with a red one
var oldGraphics = highestGreenSignal.children[0];
var oldRotation = oldGraphics.rotation;
var oldScaleX = oldGraphics.scale.x;
var oldScaleY = oldGraphics.scale.y;
highestGreenSignal.removeChild(oldGraphics);
var redGraphics = highestGreenSignal.attachAsset('redSignal', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: oldScaleX,
scaleY: oldScaleY
});
// Apply the same rotation as the original signal
redGraphics.rotation = oldRotation;
// Move signal down by 100-200 units
var moveDownAmount = 100 + Math.random() * 100;
// Use tween for smooth downward movement
tween(highestGreenSignal, {
y: highestGreenSignal.y + moveDownAmount
}, {
duration: 800,
easing: tween.easeOutQuad
});
// Flash the signal to indicate it's changed
LK.effects.flashObject(highestGreenSignal, 0xff4500, 500); // Flash red
// Collect the coin
coin.collected = true;
tween(coin, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
coin.active = false;
}
});
}
// Only collect if active, not already collected, and this is a new intersection
if (coin.active && !coin.collected && player.intersects(coin) && !coin.lastWasIntersecting) {
// Collect coin
coin.collected = true;
coin.lastWasIntersecting = true;
// Add points based on coin type
score += coin.value;
scoreTxt.setText('SCORE: ' + Math.floor(score));
// Illuminate next MEMECOIN letter if we haven't illuminated all
if (memecoinLetters && memecoinLetters.length > 0 && nextLetterIndex < memecoinLetters.length) {
var letterToIlluminate = memecoinLetters[nextLetterIndex];
if (!letterToIlluminate.collected) {
letterToIlluminate.collected = true;
// Change the color to gold without the shimmer effect
letterToIlluminate.tint = 0xFFD700; // Gold color
nextLetterIndex++;
// Check if all letters are now collected
if (nextLetterIndex === memecoinLetters.length) {
// Apply the shimmer effect to all letters now that they're all collected
for (var j = 0; j < memecoinLetters.length; j++) {
LetterShimmerEffect(memecoinLetters[j]);
}
// All letters collected - show bonus
score += 500;
scoreTxt.setText('SCORE: ' + Math.floor(score));
// Create bonus text for completing MEMECOIN
var bonusText = new BonusText(500, 'MEMECOIN');
bonusText.x = 2048 / 2;
bonusText.y = 2732 / 3;
game.addChild(bonusText);
bonusText.animate();
}
}
}
// Flash coin with different colors based on type
var flashColor;
if (coin.type === 'dogmeme') {
flashColor = 0xFFA500; // Orange
} else if (coin.type === 'shib') {
flashColor = 0x00FF00; // Green
} else if (coin.type === 'pepe') {
flashColor = 0x00FF80; // Mint Green
} else if (coin.type === 'loki') {
flashColor = 0x00FFFF; // Cyan
} else if (coin.type === 'volt') {
flashColor = 0xFFFF00; // Yellow
} else if (coin.type === 'cat') {
flashColor = 0xFF00FF; // Magenta
} else if (coin.type === 'president') {
flashColor = 0x0000FF; // Blue
} else {
flashColor = 0xFFFFFF; // White
}
LK.effects.flashObject(coin, flashColor, 300);
LK.getSound('coinCollect').play();
// Show bonus text for memecoin pickup (+100 BONUS)
if (coin.type === 'memecoin') {
// Create bonus text in top third of screen
var bonusText = new BonusText(100);
bonusText.x = 2048 / 2;
bonusText.y = 2732 / 3;
game.addChild(bonusText);
bonusText.animate();
// Add extra 100 points for memecoin
score += 100;
scoreTxt.setText('SCORE: ' + Math.floor(score));
}
// Simplify animation logic to reduce CPU load
var scaleValue, durationValue;
// Compute scale based on coin type - simplified to 3 categories for better performance
if (coin.type === 'president' || coin.type === 'cat') {
scaleValue = 6; // High value coins
durationValue = 800;
} else if (coin.type === 'volt' || coin.type === 'loki' || coin.type === 'pepe') {
scaleValue = 4; // Medium value coins
durationValue = 600;
} else {
scaleValue = 2; // Regular coins
durationValue = 400;
}
tween(coin, {
alpha: 0,
scaleX: scaleValue,
scaleY: scaleValue
}, {
duration: durationValue,
onFinish: function onFinish() {
coin.active = false;
}
});
} else if (!player.intersects(coin)) {
// Reset intersection state when player is no longer intersecting the coin
coin.lastWasIntersecting = false;
}
}
// Check shark collisions
if (player.y > 2732 - player.height && player.intersects(shark) || player.y > 2732 - player.height && player.intersects(shark2)) {
// Game over if player touches either shark
gameOver();
}
}
// Clean up inactive entities
function cleanupEntities() {
// Remove inactive signals
for (var i = signals.length - 1; i >= 0; i--) {
// Only remove signals if player is not jumping or falling
if (!signals[i].active) {
// Add extra conditions to ensure we don't remove signals that player might need to land on
if (!player || !player.isJumping && player.vy <= 0) {
signals[i].destroy();
signals.splice(i, 1);
}
}
}
// Remove inactive coins
for (var i = memecoins.length - 1; i >= 0; i--) {
if (!memecoins[i].active) {
memecoins[i].destroy();
memecoins.splice(i, 1);
}
}
// Remove inactive bonus texts
for (var i = game.children.length - 1; i >= 0; i--) {
if (game.children[i] instanceof BonusText && !game.children[i].active) {
game.children[i].destroy();
}
}
// Remove completed lightning effects
for (var i = game.children.length - 1; i >= 0; i--) {
if (game.children[i] instanceof LightningEffect && game.children[i].complete) {
game.children[i].destroy();
}
}
}
// Game over
function gameOver() {
isGameActive = false;
player.isDead = true;
// Play crash sound
LK.getSound('crash').play();
// Flash screen red
LK.effects.flashScreen(0xFF0000, 500);
// Update high score
var highScore = storage.highScore || 0;
if (score > highScore) {
storage.highScore = score;
highScoreTxt.setText('HIGH SCORE: ' + Math.floor(score));
}
// Show game over after a short delay
LK.setTimeout(function () {
LK.showGameOver();
}, 800);
}
// Input handling
game.down = function (x, y, obj) {
// Jump when tapping/clicking
player.jump();
};
// Main game update loop
game.update = function () {
if (!isGameActive) {
return;
}
// Update score based on distance
distanceTraveled += gameSpeed;
score += scoreIncrement * gameSpeed;
// Update score display occasionally to avoid text updates every frame
// Only update every 10 points now (was 5) to further reduce text rendering
if (Math.floor(score) % 10 === 0 && scoreTxt.text !== 'SCORE: ' + Math.floor(score)) {
scoreTxt.setText('SCORE: ' + Math.floor(score));
}
// Increase game speed gradually
gameSpeed = 1 + distanceTraveled / 10000;
// Check if sharks should start orbiting (every 10-20 seconds)
var currentTime = Date.now();
// Tornado effect is turned off
// Record the time to prevent other tornado-related code from running
lastOrbitTime = currentTime;
// Update orbit motion if active
if (orbiting) {
// Increase orbit angle
orbitAngle += 0.02;
// After 3 complete rotations (6*PI), start tornado and stop orbiting
if (orbitAngle > Math.PI * 6) {
orbiting = false;
}
}
// Check tornado duration
if (tornadoActive) {
var tornado = game.children.find(function (child) {
return child instanceof Tornado;
});
if (tornado) {
var currentTime = Date.now();
var elapsedTime = currentTime - tornado.startTime;
// After 3 seconds, set to silent mode but keep tornado active
if (elapsedTime >= tornado.duration && !tornado.isSilent) {
tornado.isSilent = true;
// Make particles semi-transparent in silent mode
for (var i = 0; i < tornado.particles.length; i++) {
tween(tornado.particles[i], {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut
});
}
}
}
}
// Update all tornado affected signals
for (var i = 0; i < tornadoSignals.length; i++) {
var signal = tornadoSignals[i];
if (signal.active && signal.falling) {
// Add side-to-side motion to falling signals
if (signal.sideSpeed !== undefined) {
signal.x += signal.sideSpeed;
// Gradually reduce side speed
signal.sideSpeed *= 0.98;
}
}
}
// Spawn entities
spawnSignal();
spawnMemecoin();
// Check collisions
checkCollisions();
// Remove inactive entities
cleanupEntities();
// Check if player fell off screen
if (player.y > 2732 + 200) {
gameOver();
}
// Update cloud animation
for (var i = 0; i < game.children.length; i++) {
if (game.children[i] instanceof CloudEffect) {
game.children[i].update();
}
}
// Lightning strike effect is disabled to avoid lag
// The code that was here created lightning strikes every 5 seconds
};
// Initialize UI and game
initUI();
initGame();
// Track signal status globally
game.onEntityDestroyed = function (entity) {
// Keep track of destroyed entities if needed
if (entity instanceof Signal && player) {
// Keep signals around longer if player is falling or jumping
if (player.isJumping || player.vy > 0) {
// Return false to prevent deletion when player might need to land
return false;
}
}
return true;
};
Grey shark, sideview. In-Game asset. 2d. High contrast. No shadows
Golden Dogecoin
Golden memecoin with sunglasses
shiba inu golden memecoin
Golden memecoin with Pepe
Golden coin with Floki
Golden coin with volt
Golden coin with cute catface
Golden coin with Trump and 'WILL FIX IT' Text
Lightning line. In-Game asset. 2d. High contrast. No shadows