/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1", { coins: 0 }); var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Import tween plugin // Initialize with default coins value var Butterfly = Container.expand(function () { var self = Container.call(this); var butterflyGraphics; var selectedType; // Initialize butterfly properties self.speedX = -2; self.flitAmplitude = 20; self.flitFrequency = 0.1; self.startY = 0; self.init = function () { var butterflyTypes = ['GreenButterfly', 'PinkButterfly', 'YellowButterfly']; selectedType = butterflyTypes[Math.floor(Math.random() * butterflyTypes.length)]; if (!butterflyGraphics || butterflyGraphics.assetId !== selectedType) { if (butterflyGraphics) { self.removeChild(butterflyGraphics); } butterflyGraphics = self.attachAsset(selectedType, { anchorX: 0.5, anchorY: 0.5 }); } self.startY = Math.random() * (2732 * 0.6) + 2732 * 0.2; return self; }; self.reset = function () { var butterflyTypes = ['GreenButterfly', 'PinkButterfly', 'YellowButterfly']; var newType = butterflyTypes[Math.floor(Math.random() * butterflyTypes.length)]; if (newType !== selectedType) { selectedType = newType; if (butterflyGraphics) { self.removeChild(butterflyGraphics); } butterflyGraphics = self.attachAsset(selectedType, { anchorX: 0.5, anchorY: 0.5 }); } self.startY = Math.random() * (2732 * 0.6) + 2732 * 0.2; return self; }; self.destroy = function () { if (self.parent) { self.parent.removeChild(self); } ObjectPool.recycle('Butterfly', self); }; self.update = function () { // Move the butterfly left self.x += self.speedX * gameSpeed; // Apply flitting motion self.y = self.startY + Math.sin(self.x * self.flitFrequency) * self.flitAmplitude; // Simulate wing flapping by adjusting scale var flapScale = 0.2 * Math.sin(LK.ticks * 0.3) + 1; butterflyGraphics.scale.set(flapScale); // Destroy the butterfly if it goes off screen if (self.x < -butterflyGraphics.width / 2) { self.destroy(); } }; return self.init(); }); var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('Coin', { anchorX: 0.5, anchorY: 0.5 }); // Initialize coin properties self.rotationSpeed = 0.05; // Rotation speed for the coin self.speedX = -5; // Speed of the coin moving left self.collecting = false; // Flag to check if the coin is being collected self.targetX = 0; // Target X position for collection animation self.targetY = 0; // Target Y position for collection animation self.update = function () { // Generate GoldSparkle particles if (Math.random() < 0.0525) { var sparkle = ObjectPool.get('GoldSparkle', GoldSparkle); sparkle.x = self.x + (Math.random() * coinGraphics.width - coinGraphics.width / 2); sparkle.y = self.y + (Math.random() * coinGraphics.height - coinGraphics.height / 2); sparkle.coin = self; // Associate sparkle with the coin game.addChild(sparkle); } // Move the coin left self.x += self.speedX * gameSpeed; // Apply spin effect over Y axis coinGraphics.scale.x = Math.sin(self.x * 0.015); if (coot && coot.isMagnetActive && !self.collecting && self.x < 2048 * 0.9) { // Set initial target and mark as magnetized self.targetX = coot.x; self.targetY = coot.y; self.magnetized = true; } if (self.magnetized && !self.collecting) { // Continuously update target position to track Coot self.targetX = coot.x; self.targetY = coot.y; // Move directly toward Coot with increasing speed as it gets closer var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Calculate movement speed based on distance var speed = Math.max(5, 20 - distance / 30); // Move coin toward Coot self.x += dx / distance * speed * gameSpeed; self.y += dy / distance * speed * gameSpeed; // If very close to Coot, complete collection if (distance < 30) { self.collecting = true; // Mark as being collected to prevent double counting LK.getSound('coin').play(); coinCount++; storage.coins = coinCount; coinCounter.setText(Math.min(coinCount, 9999).toString()); coinIcon.x = -coinCounter.width - 10; // Check for life addition if (coinCount % 100 === 0 && coot && coot.lives < 3) { coot.lives++; heartIcons[coot.lives - 1].alpha = 1; } // Clean up sparkles game.children.forEach(function (child) { if (child instanceof GoldSparkle && child.coin === self) { child.destroy(); } }); // Remove from game if (self.parent) { self.parent.removeChild(self); } } } if (self.collecting && !self.tweenStarted) { // Set flag to prevent multiple tweens self.tweenStarted = true; // Create collection tween tween(self, { x: self.targetX, y: self.targetY }, { duration: 500, easing: tween.quartOut, onUpdate: function onUpdate(progress) { coinGraphics.scale.set(1 - progress * 0.9); }, onFinish: function onFinish() { if (self.targetX === coot.x && self.targetY === coot.y) { LK.getSound('coin').play(); // Play sound effect if the target is coot } // Count coin and update display coinCount++; storage.coins = coinCount; coinCounter.setText(Math.min(coinCount, 999).toString()); coinIcon.x = -coinCounter.width - 10; // Check for life addition if (coinCount % 100 === 0 && coot && coot.lives < 3) { coot.lives++; heartIcons[coot.lives - 1].alpha = 1; } // Clean up game.children.forEach(function (child) { if (child instanceof GoldSparkle && child.coin === self) { child.destroy(); } }); // Remove from game if (self.parent) { self.parent.removeChild(self); } } }); } else if (self.x < -coinGraphics.width / 2) { // Destroy the coin if it goes off screen self.destroy(); } }; }); var CollisionBlock = Container.expand(function () { var self = Container.call(this); var collisionBlockGraphics = self.attachAsset('Collisionblock', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); }); // Assets will be automatically created and loaded by the LK engine based on their usage in the code. // Example assets: 'coot', 'obstacle', 'background' // Class for the main character, the American coot var Coot = Container.expand(function () { var self = Container.call(this); var cootGraphics = self.attachAsset('coot', { anchorX: 0.5, anchorY: 0.5 }); // Add this to ensure cootGraphics is always at bottom self.ensureGraphicsOrder = function () { self.setChildIndex(cootGraphics, 0); if (cootGraphics.children.length > 0) { // Find hat if it exists for (var i = 0; i < cootGraphics.children.length; i++) { if (cootGraphics.children[i].isHat) { cootGraphics.setChildIndex(cootGraphics.children[i], cootGraphics.children.length - 1); break; } } } }; self.hasHat = false; // Flag to track if a hat is equipped // Add hat update method self.updateHat = function () { // Remove existing hat for (var i = 0; i < cootGraphics.children.length; i++) { if (cootGraphics.children[i].isHat) { cootGraphics.removeChild(cootGraphics.children[i]); break; self.ensureGraphicsOrder(); } ; } // Check if a hat is equipped var hatId = storage.equippedHat || null; if (hatId) { try { // Attach hat to cootGraphics for movement var hatId = storage.equippedHat || null; if (hatId) { try { // Find hat config var hatConfig = availableHats.find(function (hat) { return hat.id === hatId; }); if (hatConfig) { var hatGraphics = cootGraphics.attachAsset(hatId, { anchorX: 0.5, anchorY: 1.0, x: hatConfig.x, y: hatConfig.y, rotation: hatConfig.rotation }); hatGraphics.isHat = true; self.setChildIndex(cootGraphics, 0); cootGraphics.setChildIndex(hatGraphics, cootGraphics.children.length - 1); self.hasHat = true; } } catch (e) { console.error("Could not load hat asset: " + hatId); storage.equippedHat = null; self.hasHat = false; } } // Mark as hat hatGraphics.isHat = true; // Make sure cootGraphics is at the bottom of main container self.setChildIndex(cootGraphics, 0); // Set hat to top within cootGraphics cootGraphics.setChildIndex(hatGraphics, cootGraphics.children.length - 1); self.hasHat = true; } catch (e) { console.error("Could not load hat asset: " + hatId); storage.equippedHat = null; self.hasHat = false; } } else { self.hasHat = false; } }; self.originalX = self.x; // Store original X position self.speed = 6.5; self.dashSpeed = 15; self.lives = 3; // Initialize Coot with three lives self.dashDelay = 30; self.isFalling = false; self.isDashing = false; self.fallRotation = 0; // Initialize fall rotation self.dashTimer = 0; self.jumpVelocity = 40; // Increased initial upward velocity for jump self.gravity = 1.0; // Gravity to apply during jump self.jumpHeight = 105; self.isJumping = false; self.isReturning = false; // New state to handle returning to top after diving self.isInvincible = false; // Initialize invincible state self.isShielded = false; // Initialize shielded state self.isMagnetActive = false; // Initialize magnet state self.isWarningActive = false; // Initialize warning state self.isPowerDashActive = false; // Initialize PowerDash state self.hasPhoenixFeather = false; // Initialize Phoenix Feather state self.powerUpTimers = { 'CoinMagnet': { endTime: 0 }, 'PowerDash': { endTime: 0 }, 'Warning': { endTime: 0 } }; self.update = function () { if (self.isFalling) { handleFalling(); } else if (self.isDiving) { handleDiving(); } else if (self.returnDelay > 0) { handleReturnDelay(); } else if (self.isReturning) { handleReturning(); } // Define state key mapping for power-ups var stateKeyMap = { 'CoinMagnet': 'isMagnetActive', 'PowerDash': 'isPowerDashActive', 'Warning': 'isWarningActive' }; // Check power-up timers for (var powerUpType in self.powerUpTimers) { if (self.powerUpTimers.hasOwnProperty(powerUpType)) { var timer = self.powerUpTimers[powerUpType]; if (Date.now() >= timer.endTime) { var stateKey = stateKeyMap[powerUpType]; if (self[stateKey]) { self[stateKey] = false; // Deactivate the power-up } } } } function handleFalling() { if (self.lives <= 0) { self.y -= self.fallSpeed; self.fallRotation += 0.05; cootGraphics.rotation = -self.fallRotation; if (self.y <= self.fallStartY - 50) { self.fallSpeed = -10; } if (self.y > 2732) { LK.showGameOver(); } } else { self.y += self.gravity * 10; // Increase gravity effect during fall if (self.y >= self.originalY) { self.y = self.originalY; self.isFalling = false; self.jumpVelocity = 40; self.isJumping = false; } cootGraphics.rotation = 0; } } function handleDiving() { self.y += self.speed * 1.5; cootGraphics.alpha = Math.max(0.5, cootGraphics.alpha - 0.05); cootGraphics.tint = 0x0000ff; if (self.y >= 2732 * 0.90) { self.y = 2732 * 0.90; self.isDiving = false; self.returnDelay = Date.now() + 1000; } } function handleReturnDelay() { if (Date.now() >= self.returnDelay) { self.returnDelay = 0; if (self.y >= 2732 * 0.90) { self.isReturning = true; } } } function handleReturning() { self.y -= self.speed * 1.5; var transitionFactor = 1 - (self.y - self.originalY) / (2732 * 0.90 - self.originalY); cootGraphics.alpha = 0.5 + 0.5 * transitionFactor; var startTint = self.underwaterTint; var endTint = 0xffffff; var r = (startTint >> 16 & 0xff) + ((endTint >> 16 & 0xff) - (startTint >> 16 & 0xff)) * transitionFactor; var g = (startTint >> 8 & 0xff) + ((endTint >> 8 & 0xff) - (startTint >> 8 & 0xff)) * transitionFactor; var b = (startTint & 0xff) + ((endTint & 0xff) - (startTint & 0xff)) * transitionFactor; cootGraphics.tint = (r << 16) + (g << 8) + b; if (self.y <= self.originalY) { self.y = self.originalY; self.isReturning = false; cootGraphics.alpha = 1.0; cootGraphics.tint = 0xffffff; } if (self.isReturning) { self.x += (self.originalX - self.x) * 0.1; } } // Add wobble effect to the coot while running if (!self.isJumping && !self.isDiving && !self.isReturning && !self.isFalling) { cootGraphics.rotation = Math.sin(LK.ticks / 5) * 0.1; // Increase wobble speed by reducing the divisor // Generate 1-2 water splash particles every 60 ticks (1 second) only when not jumping, diving, or falling if (!self.isJumping && !self.isDiving && !self.isFalling && LK.ticks % 60 == 0) { var numParticles = Math.floor(Math.random() * 2) + 1; for (var i = 0; i < numParticles; i++) { var splashLeft = ObjectPool.get('Splash', Splash); splashLeft.x = self.x - cootGraphics.width / 2; splashLeft.y = self.y + cootGraphics.height / 2; game.addChildAt(splashLeft, game.getChildIndex(foreground1) - 1); var splashRight = ObjectPool.get('Splash', Splash); splashRight.x = self.x + cootGraphics.width / 2; splashRight.y = self.y + cootGraphics.height / 2; game.addChildAt(splashRight, game.getChildIndex(foreground1) - 1); } } } // Handle dash state if (self.isDashing) { if (self.dashTimer === 0) { LK.getSound('dashsound').play(); // Play dashsound when dash starts } if (self.dashTimer < self.dashDelay) { // Dash forward if (self.x < 2048 * 0.75) { // Stop at 3/4 of screen width self.x += self.dashSpeed * 1.1; } // Create echo trail effect only if PowerDash is active if (self.isPowerDashActive) { var echo = ObjectPool.get('EchoTrail', EchoTrail); echo.x = self.x; echo.y = self.y; game.addChild(echo); } self.dashTimer++; } else { var returnSpeed = 10; // Fixed return speed self.x += Math.sign(self.originalX - self.x) * Math.min(returnSpeed, Math.abs(self.originalX - self.x)); if (Math.abs(self.x - self.originalX) < returnSpeed) { self.x = self.originalX; tween(self, { x: self.originalX }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { self.isDashing = false; self.dashTimer = 0; } }); } } } // Ensure smooth return to original position if falling and returning from a dash if (self.isFalling && self.isReturning) { var returnSpeed = 10; // Fixed return speed self.x += Math.sign(self.originalX - self.x) * Math.min(returnSpeed, Math.abs(self.originalX - self.x)); if (Math.abs(self.x - self.originalX) < returnSpeed) { self.x = self.originalX; self.isReturning = false; } } self.ensureGraphicsOrder(); }; self.isDiving = false; self.isDashing = false; self.dashSpeed = 15; self.dashDelay = 60; self.dashTimer = 0; self.originalX = self.x; self.jump = function () { if (canPerformAction()) { // Cancel any existing tweens on this object tween.stop(self); self.isJumping = true; LK.getSound('jumpsound').play(); self.originalX = self.x; self.isReturning = false; self.originalY = self.y; // Fixed jump height - much higher than before var jumpHeight = 805; // Try with a specific large value // Jump tween with steep curve tween(self, { y: self.y - jumpHeight }, { duration: 450, // Longer duration easing: tween.backOut, onFinish: function onFinish() { // Fall tween back to original position tween(self, { y: self.originalY }, { duration: 600, // Longer duration down too easing: tween.backIn, onFinish: function onFinish() { self.isJumping = false; } }); } }); } }; self.dash = function () { if (canPerformAction()) { self.isDashing = true; self.originalX = self.x; self.dashTimer = 0; } }; self.dive = function () { if (canPerformAction()) { LK.getSound('cootdive').play(); // Play cootdive sound effect when the coot starts to dive self.isDiving = true; self.returnDelay = Date.now() + 5000; self.isReturning = false; self.underwaterTint = 0x4444ff; self.originalX = self.x; self.originalY = self.y; self.y += self.speed * 2; generateSplashParticles(2, 4); } }; function canPerformAction() { return !self.isJumping && !self.isDiving && !self.isReturning && !self.isDashing; } function generateSplashParticles(min, max) { var numParticles = Math.floor(Math.random() * (max - min + 1)) + min; for (var i = 0; i < numParticles; i++) { var splash = ObjectPool.get('Splash', Splash); splash.x = self.x; splash.y = self.y + cootGraphics.height / 2; game.addChildAt(splash, game.getChildIndex(foreground1) - 1); } } }); var DayCountDisplay = Container.expand(function () { var self = Container.call(this); var dayText = new Text2('Day ' + DayCount, { size: 100, fill: 0xFFFFFF }); dayText.anchor.set(0.5, 0.5); dayText.alpha = 0; // Set alpha to 0 by default self.addChild(dayText); self.show = function () { dayText.setText('Day ' + DayCount); dayText.alpha = 1; LK.setTimeout(function () { dayText.alpha = 0; // Set alpha back to 0 instead of destroying }, 1500); // Set a timer to make the day count display visible for 3 seconds every time the day count increases LK.setTimeout(function () { dayText.alpha = 1; LK.setTimeout(function () { dayText.alpha = 0; }, 1500); }, 1500); }; }); var DuckWarningIcon = Container.expand(function () { var self = Container.call(this); var iconGraphics = self.attachAsset('DuckIcon', { anchorX: 0.5, anchorY: 0.5, alpha: 0.75 }); self.flashInterval = 0; self.update = function () { self.flashInterval++; if (coot.isWarningActive) { if (self.flashInterval % 30 < 15) { iconGraphics.alpha = 0.75; } else { iconGraphics.alpha = 0.5; } } else { iconGraphics.alpha = 0; } }; }); var EagleWarningIcon = Container.expand(function () { var self = Container.call(this); var iconGraphics = self.attachAsset('EagleIcon', { anchorX: 0.5, anchorY: 0.5, alpha: 0.75 }); self.flashInterval = 0; self.update = function () { self.flashInterval++; if (coot.isWarningActive) { if (self.flashInterval % 30 < 15) { iconGraphics.alpha = 0.75; } else { iconGraphics.alpha = 0.5; } } else { iconGraphics.alpha = 0; } }; }); var EchoTrail = Container.expand(function () { var self = Container.call(this); var echoGraphics; self.fadeRate = 0.05; self.init = function () { if (!echoGraphics) { echoGraphics = self.attachAsset('coot', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); } else { echoGraphics.alpha = 0.5; } return self; }; self.reset = function () { if (echoGraphics) { echoGraphics.alpha = 0.5; } return self; }; self.destroy = function () { if (self.parent) { self.parent.removeChild(self); } ObjectPool.recycle('EchoTrail', self); }; self.update = function () { echoGraphics.alpha -= self.fadeRate; if (echoGraphics.alpha <= 0) { self.destroy(); } }; return self.init(); }); var Firefly = Container.expand(function () { var self = Container.call(this); var fireflyGraphics = self.attachAsset('Firefly', { anchorX: 0.5, anchorY: 0.5 }); // Initialize firefly properties self.speedX = Math.random() * 8 - 4; // Further increased random horizontal speed self.speedY = Math.random() * 8 - 4; // Further increased random vertical speed self.fadeRate = 0.005; // Much slower fade rate for slow pulsing self.flickerTimer = Math.random() * 240 + 120; // Random flicker timer between 2 to 4 seconds self.alphaDirection = 1; // Direction of alpha change (1 for fade in, -1 for fade out) self.lifetime = Math.random() * 180 + 180; // Random lifetime between 3 to 5 seconds self.update = function () { // Move the firefly self.x += self.speedX; self.y += self.speedY; // Create trail effect var trail = ObjectPool.get('FireflyTrail', FireflyTrail); trail.x = self.x; trail.y = self.y; game.addChild(trail); // Loop around the screen if (self.x < 0) { self.x = 2048; } if (self.x > 2048) { self.x = 0; } if (self.y < 0) { self.y = 2732; } if (self.y > 2732) { self.y = 0; } // Flicker effect self.flickerTimer--; if (self.flickerTimer <= 0) { fireflyGraphics.alpha = Math.random(); // Random alpha for flicker self.flickerTimer = Math.random() * 60 + 30; // Reset flicker timer } // Fade in and out fireflyGraphics.alpha += self.fadeRate * self.alphaDirection; if (fireflyGraphics.alpha <= 0 || fireflyGraphics.alpha >= 1) { self.alphaDirection *= -1; // Reverse fade direction } // Destroy firefly after its lifetime self.lifetime--; if (self.lifetime <= 0) { self.destroy(); } }; }); var FireflyTrail = Container.expand(function () { var self = Container.call(this); var trailGraphics; self.fadeRate = 0.02; self.init = function () { if (!trailGraphics) { trailGraphics = self.attachAsset('FireflyTrail', { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); } else { trailGraphics.alpha = 1; } return self; }; self.reset = function () { if (trailGraphics) { trailGraphics.alpha = 1; } return self; }; self.destroy = function () { if (self.parent) { self.parent.removeChild(self); } ObjectPool.recycle('FireflyTrail', self); }; self.update = function () { trailGraphics.alpha -= self.fadeRate; if (trailGraphics.alpha <= 0) { self.destroy(); } }; return self.init(); }); var FishWarningIcon = Container.expand(function () { var self = Container.call(this); var iconGraphics = self.attachAsset('FishIcon', { anchorX: 0.5, anchorY: 0.5, alpha: 0.75 }); self.flashInterval = 0; self.update = function () { self.flashInterval++; if (coot.isWarningActive) { if (self.flashInterval % 30 < 15) { iconGraphics.alpha = 0.75; } else { iconGraphics.alpha = 0.5; } } else { iconGraphics.alpha = 0; } }; }); // Define a single obstacleTypes array as a constant // Class for the foreground var Foreground = Container.expand(function () { var self = Container.call(this); var foregroundGraphics = self.attachAsset('Foreground', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.isFirst = false; // Will be set during initialization self.update = function () { // Only move the first piece if (self.isFirst) { self.x -= self.speed * gameSpeed; // Apply gameSpeed to movement // Move second piece to maintain fixed distance self.other.x = self.x + self.width; // Reset both pieces when first goes off screen if (self.x < -self.width / 1.5) { // Reset earlier - when piece is mostly off screen self.x += self.width; self.other.x = self.x + self.width; } } }; }); var GoldSparkle = Container.expand(function () { var self = Container.call(this); var sparkleGraphics; // Initialize variables but don't create graphics yet self.speedX = 0; self.speedY = 0; self.fadeRate = 0.02; self.coin = null; // Reference to associated coin self.init = function () { // Only create graphics if they don't exist if (!sparkleGraphics) { sparkleGraphics = self.attachAsset('GoldSparkle', { anchorX: 0.5, anchorY: 0.5 }); } // Set initial properties self.speedX = (Math.random() * 2 - 1) * 0.75; self.speedY = (Math.random() * 2 - 1) * 0.75; sparkleGraphics.alpha = 1; return self; }; self.reset = function () { // Reset properties without recreating the graphics self.speedX = (Math.random() * 2 - 1) * 0.75; self.speedY = (Math.random() * 2 - 1) * 0.75; self.coin = null; if (sparkleGraphics) { sparkleGraphics.alpha = 1; } return self; }; self.cleanup = function () { // Clear any references to other objects self.coin = null; }; self.destroy = function () { // Instead of destroying, return to pool if (self.parent) { self.parent.removeChild(self); } ObjectPool.recycle('GoldSparkle', self); }; self.update = function () { self.x += self.speedX; self.y += self.speedY; sparkleGraphics.alpha -= self.fadeRate; if (sparkleGraphics.alpha <= 0) { self.destroy(); // This now recycles instead of destroying } }; return self.init(); // Initialize on creation }); var HatShopButton = Container.expand(function () { var self = Container.call(this); var hatShopButtonGraphics = self.attachAsset('HatShopButton', { anchorX: 0.5, anchorY: 0.5 }); self.down = function () { gameLogo.visible = false; if (gameLogo.playButton) { gameLogo.playButton.visible = false; } if (gameLogo.instructionsButton) { gameLogo.instructionsButton.visible = false; } if (gameLogo.hatShopButton) { gameLogo.hatShopButton.visible = false; } showHatShop(); }; }); var HatShopScreen = Container.expand(function () { var self = Container.call(this); // Title var shopTitle = new Text2('Hat Shop', { size: 100, fill: 0xFFFFFF, fontWeight: "bold" }); shopTitle.anchor.set(0.5, 0); shopTitle.x = 2048 / 2; shopTitle.y = 2732 * 0.15; self.addChild(shopTitle); // Initialize purchased hats array if not present if (!storage.purchasedHats) { storage.purchasedHats = []; } // Hat grid var gridItems = []; var itemsPerRow = 3; var itemWidth = 400; var itemHeight = 400; var padding = 50; var startX = 2048 / 2 - (itemWidth + padding) * (itemsPerRow / 2) + itemWidth / 2 + 2048 * 0.01; var startY = 2732 * 0.27; availableHats.forEach(function (hat, index) { var row = Math.floor(index / itemsPerRow); var col = index % itemsPerRow; var itemContainer = new Container(); itemContainer.x = startX + col * (itemWidth + padding); itemContainer.y = startY + row * (itemHeight + padding); // Background var bg = LK.getAsset('Collisionblock', { anchorX: 0.5, anchorY: 0.5, width: itemWidth, height: itemHeight, alpha: 0.5 }); itemContainer.addChild(bg); // Hat image var hatImg = LK.getAsset(hat.id, { anchorX: 0.5, anchorY: 0.5, y: -50, scaleX: 1.35, // Increase width by 25% scaleY: 1.35 // Increase height by 25% }); itemContainer.addChild(hatImg); // Hat name var nameText = new Text2(hat.name, { size: 50, // Increased by 15% fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0); nameText.y = 50; itemContainer.addChild(nameText); // Price or status var isPurchased = storage.purchasedHats && storage.purchasedHats.indexOf(hat.id) !== -1; var isEquipped = storage.equippedHat === hat.id; var statusText; if (isEquipped) { statusText = new Text2('EQUIPPED', { size: 40, fill: 0x00FF00 }); } else if (isPurchased) { statusText = new Text2('OWNED', { size: 40, fill: 0xAAAAAA }); } else { statusText = new Text2(hat.price + ' coins', { size: 40, // Increased by 15% fill: 0xFFD700 }); } statusText.anchor.set(0.5, 0); statusText.y = 100; itemContainer.addChild(statusText); // Add interaction itemContainer.down = function () { handleHatSelection(hat, statusText); }; self.addChild(itemContainer); gridItems.push(itemContainer); }); // Back button var backButton = new Text2('Back', { size: 100, fill: 0xFFFFFF, fontWeight: "bold" }); backButton.anchor.set(0.5, 0.5); backButton.x = 2048 / 2; backButton.y = 2732 * 0.875; self.addChild(backButton); backButton.down = function () { self.menuBackground.destroy(); self.destroy(); isTitleScreen = true; gameLogo.visible = true; if (gameLogo.playButton) { gameLogo.playButton.visible = true; } if (gameLogo.instructionsButton) { gameLogo.instructionsButton.visible = true; } if (gameLogo.hatShopButton) { gameLogo.hatShopButton.visible = true; } }; function handleHatSelection(hat, statusText) { var isPurchased = storage.purchasedHats && storage.purchasedHats.indexOf(hat.id) !== -1; var isEquipped = storage.equippedHat === hat.id; if (isEquipped) { // Unequip hat storage.equippedHat = null; // Update UI self.menuBackground.destroy(); self.destroy(); showHatShop(); } else if (isPurchased) { // Equip hat storage.equippedHat = hat.id; // Update UI self.menuBackground.destroy(); self.destroy(); showHatShop(); } else if (storage.coins >= hat.price) { // Purchase hat storage.coins -= hat.price; // Initialize array if needed if (!storage.purchasedHats) { storage.purchasedHats = []; } // Get current purchased hats as array var currentHats = getPurchasedHats(); // Add new hat currentHats.push(hat.id); // Save back to storage savePurchasedHats(currentHats); storage.equippedHat = hat.id; // Update UI if (typeof coinDisplay !== 'undefined') { coinDisplay.setText('Coins: ' + storage.coins); } // Update coin counter in game coinCount = storage.coins; coinCounter.setText(Math.min(coinCount, 9999).toString()); // Refresh shop self.menuBackground.destroy(); self.destroy(); showHatShop(); // Show purchase confirmation var confirmText = new Text2('Purchased ' + hat.name + '!', { size: 100, fill: 0x00FF00 }); confirmText.anchor.set(0.5, 0.5); confirmText.x = 2048 / 2; confirmText.y = 2732 / 2; game.addChild(confirmText); LK.setTimeout(function () { confirmText.destroy(); }, 1500); // Play purchase sound LK.getSound('coin').play(); } else { // Not enough coins var errorText = new Text2('Not enough coins!', { size: 100, fill: 0xFF0000 }); errorText.anchor.set(0.5, 0.5); errorText.x = 2048 / 2; errorText.y = 2732 / 2; game.addChild(errorText); LK.setTimeout(function () { errorText.destroy(); }, 1500); } } }); var InstructionsButton = Container.expand(function () { var self = Container.call(this); var instructionsButtonGraphics = self.attachAsset('InstructionsButton', { anchorX: 0.5, anchorY: 0.5 }); self.down = function () { gameLogo.visible = false; if (gameLogo.playButton) { gameLogo.playButton.visible = false; } if (gameLogo.instructionsButton) { gameLogo.instructionsButton.visible = false; } showInstructions(); }; }); var InstructionsScreen = Container.expand(function () { var self = Container.call(this); var instructionsText = new Text2('Swipe up to jump,\ndown to dive,\nand right to dash!', { size: 80, fill: 0xFFFFFF, align: "center" }); instructionsText.anchor.set(0.5, 0); instructionsText.x = 2048 / 2; instructionsText.y = 2732 * 0.15; self.addChild(instructionsText); // Add power-up icons and explanations var powerUpsInfo = [{ type: 'CoinMagnet', text: 'Coin Magnet: Attracts coins' }, { type: 'Shield', text: 'Shield: Protects from one hit' }, { type: 'PhoenixFeather', text: 'Phoenix Feather: Revive once' }, { type: 'PowerDash', text: 'Power Dash: Dash while jumping or diving' }, { type: 'Warning', text: 'Warning: Alerts of upcoming obstacles' }]; powerUpsInfo.forEach(function (info, index) { var icon = LK.getAsset(info.type, { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 300 - 2048 * 0.10, // Move icons further left y: 2732 * 0.30 + index * 200 }); self.addChild(icon); var text = new Text2(info.text, { size: 60, fill: 0xFFFFFF, align: "left" }); text.anchor.set(0, 0.5); text.x = 2048 / 2 - 300 + 2048 * 0.035 - 2048 * 0.07; text.y = icon.y + 20; self.addChild(text); }); var instructionDetails = new Text2('Jump over the Rock,\nDive away from the Owl,\nDash away from the Fish.\n100 Coins = 1 heart.', { size: 70, fill: 0xFFFFFF, align: "center" }); instructionDetails.anchor.set(0.5, 0); instructionDetails.x = 2048 / 2; instructionDetails.y = 2732 * 0.66; // Move up by 5% from the original position self.addChild(instructionDetails); var backButton = new Text2('Back', { size: 100, fill: 0xFFFFFF, fontWeight: "bold" }); backButton.anchor.set(0.5, 0.5); backButton.x = 2048 / 2; backButton.y = 2732 * 0.88; // Move back button down by 1.5% self.addChild(backButton); backButton.down = function () { self.menuBackground.destroy(); // Remove the MenuBackground self.destroy(); isTitleScreen = true; gameLogo.visible = true; if (gameLogo.playButton) { gameLogo.playButton.visible = true; } if (gameLogo.instructionsButton) { gameLogo.instructionsButton.visible = true; } }; }); // Class for the midgroundtrees var Midgroundtrees = Container.expand(function () { var self = Container.call(this); var midgroundtreesGraphics = self.attachAsset('Midgroundtrees', { anchorX: 0.5, anchorY: 0.7 }); self.speed = 3; self.update = function () { self.x -= self.speed * gameSpeed; // self.y = coot.y; if (self.x <= -self.width / 2) { self.x += self.width * 2; } }; }); var Moon = Container.expand(function () { var self = Container.call(this); var moonGraphics = self.attachAsset('Moon', { anchorX: 0.5, anchorY: 0.5 }); // Initialize moon properties self.startX = -moonGraphics.width / 2; // Start off-screen to the left self.startY = 2732 * 0.25; // Match the Sun's position (25% from the top) self.endX = 2048 + moonGraphics.width / 2; // End off-screen to the right self.arcHeight = 2732 * 0.1; // Match the Sun's arc height self.duration = 2 * 60 * 60; // 2 minutes in ticks (assuming 60 FPS) self.ticks = 0; // For the Moon class, replace the update function with: self.update = function () { if (!self.tweenStarted) { // Set initial position self.x = self.startX; self.y = self.startY; self.tweenStarted = true; // First tween: Move up in an arc tween(self, { x: self.startX + (self.endX - self.startX) * 0.5, y: self.startY - self.arcHeight }, { duration: 60 * 1000, // 1 minute in milliseconds easing: tween.sineInOut, onFinish: function onFinish() { // Second tween: Move down in an arc to end position tween(self, { x: self.endX, y: self.startY }, { duration: 60 * 1000, // 1 minute in milliseconds easing: tween.sineInOut, onFinish: function onFinish() { self.destroy(); // Switch to day state isDay = true; isNight = false; DayCount += 1; dayCountDisplay.show(); // Stop spawning fireflies if (fireflySpawnInterval) { LK.clearInterval(fireflySpawnInterval); fireflySpawnInterval = null; } var newSun = new Sun(); newSun.x = newSun.startX; // Use the sun's defined starting X position newSun.y = newSun.startY; // Set the proper starting Y position game.addChild(newSun); } }); } }); } }; }); // Moved Owl logic into Obstacle class var Obstacle = Container.expand(function (type) { var self = Container.call(this); var graphics; var collisionBlock = self.addChild(new CollisionBlock()); collisionBlock.x = 0; collisionBlock.y = 0; graphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); // Add a Ripple instance to the Duck obstacle if (type === 'Duck') { self.lastRippleTime = Date.now(); var ripple = ObjectPool.get('Ripple', Ripple); ripple.x = 0; // Center the ripple on the Duck ripple.y = graphics.height / 2; // Position the ripple at the bottom of the Duck self.addChildAt(ripple, 0); // Add ripple below the Duck but above the water layer } self.speedX = -6; if (type === 'Fish') { self.isJumping = false; self.pauseTime = 0; graphics.tint = 0x4444ff; graphics.alpha = 0.7; } if (type === 'Fish') { self.speedX = -6; // Back to original fast swimming speed self.jumpSpeed = 0.05; // Slow jump speed control self.isJumping = false; self.isPausing = false; self.pauseTime = 0; self.lastSplashTime = Date.now(); // Add splash timer graphics.tint = 0x4444ff; // Set underwater blue tint initially graphics.alpha = 0.7; // Set underwater opacity initially self.isFalling = false; self.jumpVelocity = 0; self.jumpStartY = self.y; // Store original position self.jumpTime = 0; // Initialize jump time counter self.y = 2732 * 0.90; // Set Fish Y position to the bottom of the Coot's dive } if (type === 'Owl') { self.speedX = -6; // Speed of the owl moving left self.speedY = 0; // Initial vertical speed self.x = 2048 + graphics.width / 2; // Spawn at the top right of the screen self.isDiving = false; // Initialize isDiving state self.hasDived = false; // Initialize hasDived state to false by default if (!self.soundPlayed && self.x >= 2048 - 50) { LK.getSound('owl').play(); self.soundPlayed = true; // Set flag to prevent repeating } } self.update = function () { self.x += self.speedX * gameSpeed; if (type === 'Fish') { // Move horizontally self.x += self.speedX; // Generate underwater splash effects if (!self.isJumping && Date.now() - self.lastSplashTime >= 200) { // Generate every 200ms var numSplashes = Math.floor(Math.random() * 2) + 1; // 1-2 splashes for (var i = 0; i < numSplashes; i++) { var splash = ObjectPool.get('Splash', Splash); splash.x = self.x + (Math.random() * graphics.width - graphics.width / 2); // Random position along fish body splash.y = self.y; splash.tint = 0x4444ff; // Match fish's underwater tint game.addChildAt(splash, game.getChildIndex(self) - 1); } self.lastSplashTime = Date.now(); } // Check if crossing under Coot if (self.x <= coot.x && !self.isJumping) { self.speedX = 0; graphics.alpha = 0; self.pauseTime += 0.02; // Add this check for when to reappear if (self.pauseTime >= 1 && !self.isJumping) { LK.getSound('fishsplash').play(); graphics.alpha = 1; graphics.rotation = Math.PI / 2; self.isJumping = true; self.jumpStartY = self.y; // Create splash burst when starting jump var burstSplashes = Math.floor(Math.random() * 4) + 6; for (var i = 0; i < burstSplashes; i++) { var splash = ObjectPool.get('Splash', Splash); splash.x = self.x + (Math.random() * graphics.width - graphics.width / 2); splash.y = self.jumpStartY - Math.random() * 200; game.addChildAt(splash, game.getChildIndex(self) - 1); } // Create jump tween // Change the tint directly at specific points instead of in onUpdate // Create jump tween with faster duration tween(self, { y: self.jumpStartY - 864 // Jump height }, { duration: 350, // 0.4 seconds up easing: tween.sineOut, onFinish: function onFinish() { // Create fall tween with faster duration tween(self, { y: self.jumpStartY + 50 // Go slightly below starting point }, { duration: 300, // 0.4 seconds down easing: tween.sineIn, onFinish: function onFinish() { Obstacle.lastDestroyTime = Date.now(); currentObstacle = null; self.destroy(); } }); } }); } } if (self.isJumping) { // Hardcoded tint change based on absolute position var jumpProgress = Math.abs(self.y - self.jumpStartY) / 864; if (self.y < self.jumpStartY - 100) { graphics.tint = 0xffffff; graphics.alpha = 1.0; } else { graphics.tint = 0x4444ff; graphics.alpha = 0.7; } } } else if (type === 'Duck') { // Add sinusoidal wave pattern travel for Duck if (type === 'Duck') { self.y += Math.sin(self.x * 0.00625) * 2.7; // Adjusted sinusoidal wave pattern with further reduced frequency and increased amplitude } if (type === 'Duck') { // Play duck sound every 2 seconds if (LK.ticks % 100 == 0) { // 120 frames = 2 seconds at 60 FPS LK.getSound('duck').play(); } } // Ripple spawn logic if (Date.now() - self.lastRippleTime >= 500) { var ripple = ObjectPool.get('Ripple', Ripple); ripple.x = self.x; ripple.y = self.y + graphics.height / 2; game.addChildAt(ripple, game.getChildIndex(self) - 1); self.lastRippleTime = Date.now(); } // Existing destroy logic if (self.x <= -self.width / 2) { self.destroy(); Obstacle.lastDestroyTime = Date.now(); console.log("Obstacle destroyed at " + Obstacle.lastDestroyTime); } } else if (type === 'Owl') { if (coot) { if (!self.isDiving && self.x > coot.x) { self.x += self.speedX * gameSpeed; // Move Owl left until it reaches Coot's X position } else { if (!self.hasDived) { self.isDiving = true; // Set isDiving state if (coot.intersects(collisionBlock)) { self.hasDived = true; // Set hasDived state only on intersection with Coot } self.speedX = 0; // Stop left movement if (!coot.isDiving) { self.speedY = (coot.y - self.y) / 40; // Adjust speed to track Coot's Y position at half speed self.y += self.speedY * gameSpeed; // Move Owl towards Coot self.x += (coot.x - self.x) / 20 * gameSpeed; // Move Owl towards Coot's X position } if (self.y >= coot.originalY) { self.isDiving = false; // Remove isDiving state self.speedX = -6; // Set speed to fly away to the left self.speedY = -3; // Add upward movement } else if (self.isDiving) { var shadowClone = ObjectPool.get('ShadowClone', ShadowClone); shadowClone.x = self.x; shadowClone.y = self.y; game.addChild(shadowClone); } } } // Check if Coot's state requires Owl to fly off if (self.isDiving && (coot.isDiving || coot.isReturning || coot.returnDelay > 0 || coot.intersects(collisionBlock))) { self.isDiving = false; // Remove isDiving state self.speedX = -6; // Set speed to fly away to the left self.speedY = -3; // Add upward movement self.x += self.speedX * gameSpeed; self.y += self.speedY * gameSpeed; // Move Owl upwards as it flies away if (self.x < -self.width / 2) { self.destroy(); // Remove Owl from the game when off-screen } } } } collisionBlock.x = 0; collisionBlock.y = 0; }; }); var OwlWarningIcon = Container.expand(function () { var self = Container.call(this); var iconGraphics = self.attachAsset('OwlIcon', { anchorX: 0.5, anchorY: 0.5, alpha: 0.75 }); self.flashInterval = 0; self.update = function () { self.flashInterval++; if (coot.isWarningActive) { if (self.flashInterval % 30 < 15) { iconGraphics.alpha = 0.75; } else { iconGraphics.alpha = 0.5; } } else { iconGraphics.alpha = 0; } }; }); var PhoenixFeatherIcon = Container.expand(function () { var self = Container.call(this); var iconGraphics = self.attachAsset('PhoenixFeatherIcon', { anchorX: 0.5, anchorY: 0.5, alpha: 0.2 // Start dim, not opaque }); self.update = function () { iconGraphics.alpha = coot && coot.hasPhoenixFeather ? 1 : 0.2; // Set alpha based on possession }; }); var PlayButton = Container.expand(function () { var self = Container.call(this); var playButtonGraphics = self.attachAsset('PlayButton', { anchorX: 0.5, anchorY: 0.5 }); self.down = function () { // Exit title screen LK.getSound('powerup').play(); // Play coothurt sound LK.effects.flashScreen(0xFFFFFF, 300); // Flash screen red for 100ms LK.setTimeout(function () { // Slide out all elements isTitleScreen = false; var slideOutInterval = LK.setInterval(function () { gameLogo.x += 50; self.x += 50; if (gameLogo.instructionsButton) { gameLogo.instructionsButton.x += 50; } // Make sure to include the hat shop button here if (gameLogo.hatShopButton) { gameLogo.hatShopButton.x += 50; } if (gameLogo.x > 2048 + gameLogo.width / 2) { LK.clearInterval(slideOutInterval); self.destroy(); if (gameLogo.instructionsButton) { gameLogo.instructionsButton.destroy(); } if (gameLogo.hatShopButton) { gameLogo.hatShopButton.destroy(); } // Start game after animations startGame(); } }, 16); }, 500); }; }); var PowerUp = Container.expand(function (type) { var self = Container.call(this); var powerUpGraphics = self.attachAsset(type, { anchorX: 0.5, anchorY: 0.5 }); // Initialize power-up properties self.speedX = -5; // Speed of the power-up moving left self.collected = false; // Flag to check if the power-up is collected self.update = function () { // Add pulsating effect var pulseScale = 0.05 * Math.sin(LK.ticks * 0.1) + 1; powerUpGraphics.scale.set(pulseScale); // Add bobbing effect self.y += Math.sin(LK.ticks * 0.05) * 0.5; // Move the power-up left self.x += self.speedX * gameSpeed; // Check for collection by the Coot if (coot && !self.collected && coot.intersects(self)) { if (type === 'PhoenixFeather' && coot.hasPhoenixFeather || type === 'Shield' && coot.isShielded) { return; // Do not collect if already active } self.collected = true; activatePowerUp(type); self.destroy(); } // Destroy the power-up if it goes off screen if (self.x < -powerUpGraphics.width / 2) { self.destroy(); } }; function activatePowerUp(type) { LK.getSound('powerup').play(); // Play powerup sound effect when a power-up is collected var powerUpConfig = { 'CoinMagnet': { duration: 15000, state: 'isMagnetActive' }, 'PowerDash': { duration: 30000, state: 'isPowerDashActive' }, 'Warning': { duration: 30000, state: 'isWarningActive' }, 'Shield': { duration: 0, // Shield is instant and doesn't have a duration state: 'isShielded' }, 'PhoenixFeather': { duration: 0, // Phoenix Feather is instant and doesn't have a duration state: 'hasPhoenixFeather' } }; var config = powerUpConfig[type]; var startTime = Date.now(); if (config) { // Removed interval-based state management if (type === 'Shield') { coot.isShielded = true; coot.shieldGraphics = coot.addChild(LK.getAsset('Shield', { anchorX: 0.5, anchorY: 0.5, scaleX: coot.width / 200, // Scale to cover Coot scaleY: coot.height / 204.2, // Scale to cover Coot alpha: 0.5 // Set opacity to 50% })); return; // Exit early as Shield doesn't need a timer } if (type === 'PhoenixFeather') { coot.hasPhoenixFeather = true; return; // Exit early as Phoenix Feather doesn't need a timer } coot[config.state] = true; // Ensure state is set to true coot.powerUpTimers[type].endTime = Date.now() + config.duration; // Set power-up end time var existingPowerUp = powerUpDisplay.activePowerUps.find(function (p) { return p.type === type; }); if (existingPowerUp) { // Calculate remaining time in milliseconds var timeRemaining = existingPowerUp.startTime + existingPowerUp.duration - Date.now(); if (timeRemaining > 0) { config.duration += timeRemaining; // Add remaining time to new duration startTime = Date.now(); // Reset start time to now for accurate duration } LK.clearInterval(existingPowerUp.interval); existingPowerUp.startTime = Date.now(); // Update startTime to reflect new total duration existingPowerUp.duration = config.duration; // Update duration existingPowerUp.interval = createInterval(config, existingPowerUp.startTime); existingPowerUp.timer = config.duration / 1000; // Update visual timer return; // Exit early as we have updated the existing power-up } powerUpDisplay.addPowerUp(type, config.duration / 1000, startTime); // Include startTime for accurate remaining time } } }); var PowerUpDisplay = Container.expand(function () { var self = Container.call(this); self.x = 2048 / 2; // Center horizontally self.y = 2732 * 0.9; // Position at bottom center self.activePowerUps = []; self.update = function () { // Update each power-up display self.activePowerUps.forEach(function (powerUp, index) { powerUp.timer -= (Date.now() - powerUp.lastUpdateTime) / 1000; // Decrease timer based on real time powerUp.lastUpdateTime = Date.now(); // Update the last update time powerUp.text.setText(Math.max(0, Math.ceil(powerUp.timer)) + 's'); // Ensure timer doesn't show negative values // Remove power-up if timer reaches 0 if (powerUp.timer <= 0) { powerUp.icon.destroy(); powerUp.text.destroy(); self.activePowerUps.splice(index, 1); self.repositionPowerUps(); } }); }; self.addPowerUp = function (type, duration) { var existingPowerUp = self.activePowerUps.find(function (p) { return p.type === type; }); if (existingPowerUp) { existingPowerUp.timer += duration; existingPowerUp.lastUpdateTime = Date.now(); existingPowerUp.text.setText(Math.max(0, Math.ceil(existingPowerUp.timer)) + 's'); // Update visual timer return; } var iconAssetMap = { 'CoinMagnet': 'CoinMagnet', 'PowerDash': 'PowerDash', 'Warning': 'Warning', 'Shield': 'Shield', 'PhoenixFeather': 'PhoenixFeather' }; if (iconAssetMap[type]) { var icon = LK.getAsset(iconAssetMap[type], { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0 }); } else { console.error("Power-up type not found in iconAssetMap: ", type); return; } var text = new Text2(duration + 's', { size: 100, fill: 0xFFFFFF }); text.anchor.set(0.5, 0.5); self.addChild(icon); self.addChild(text); self.activePowerUps.push({ icon: icon, text: text, timer: duration, type: type, lastUpdateTime: Date.now() }); self.repositionPowerUps(); }; self.repositionPowerUps = function () { var totalWidth = self.activePowerUps.reduce(function (acc, powerUp) { return acc + powerUp.icon.width + powerUp.text.width + 40; // 40 is the new spacing }, 0); var startX = -totalWidth / 2; self.activePowerUps.forEach(function (powerUp) { powerUp.icon.x = startX + powerUp.icon.width / 2; powerUp.text.x = powerUp.icon.x + powerUp.icon.width / 2 + 40; // 40 is the new spacing between icon and text startX += powerUp.icon.width + powerUp.text.width + 20; }); }; }); var Ripple = Container.expand(function () { var self = Container.call(this); var rippleGraphics; self.initialScale = 0.05; self.growthRate = 0.02; self.fadeRate = 0.01; self.maxScale = 2.0; self.init = function () { if (!rippleGraphics) { rippleGraphics = self.attachAsset('Ripple', { anchorX: 0.5, anchorY: 0.5 }); } rippleGraphics.scaleX = self.initialScale; rippleGraphics.scaleY = self.initialScale; rippleGraphics.alpha = 1; return self; }; self.reset = function () { rippleGraphics.scaleX = self.initialScale; rippleGraphics.scaleY = self.initialScale; rippleGraphics.alpha = 1; return self; }; self.destroy = function () { if (self.parent) { self.parent.removeChild(self); } ObjectPool.recycle('Ripple', self); }; self.update = function () { rippleGraphics.scaleX += self.growthRate; rippleGraphics.scaleY += self.growthRate; rippleGraphics.alpha -= self.fadeRate; if (rippleGraphics.scaleX >= self.maxScale || rippleGraphics.alpha <= 0) { self.destroy(); } }; return self.init(); }); var ShadowClone = Container.expand(function () { var self = Container.call(this); var cloneGraphics; self.fadeRate = 0.05; self.init = function () { if (!cloneGraphics) { cloneGraphics = self.attachAsset('Owl', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); } else { cloneGraphics.alpha = 0.5; } return self; }; self.reset = function () { if (cloneGraphics) { cloneGraphics.alpha = 0.5; } return self; }; self.destroy = function () { if (self.parent) { self.parent.removeChild(self); } ObjectPool.recycle('ShadowClone', self); }; self.update = function () { cloneGraphics.alpha -= self.fadeRate; if (cloneGraphics.alpha <= 0) { self.destroy(); } }; return self.init(); }); // Static respawn delay property // Class for the water splash particles var Splash = Container.expand(function () { var self = Container.call(this); var splashGraphics; var scale; // Initialize variables but don't create graphics self.speedX = 0; self.speedY = 0; self.gravity = 0.1; self.init = function () { // Only create graphics if they don't exist if (!splashGraphics) { scale = Math.random() * 0.75 + 0.25; splashGraphics = self.attachAsset('Watersplash', { anchorX: 0.5, anchorY: 0.5, scaleX: scale, scaleY: scale }); } // Set initial properties self.speedX = Math.random() * 2 - 1; self.speedY = -Math.random() * 5; splashGraphics.alpha = 1; return self; }; self.reset = function () { // Reset properties without recreating the graphics self.speedX = Math.random() * 2 - 1; self.speedY = -Math.random() * 5; if (splashGraphics) { splashGraphics.alpha = 1; splashGraphics.rotation = 0; } return self; }; self.cleanup = function () { // Clear any references to other objects self.coin = null; }; self.destroy = function () { // Instead of destroying, return to pool self.parent.removeChild(self); ObjectPool.recycle('Splash', self); }; self.update = function () { self.x += self.speedX * gameSpeed; self.y += self.speedY * gameSpeed; self.speedY += self.gravity; // Add rotation based on the speed of the particle self.rotation += self.speedX / 10; if (self.y > 2732) { // If the particle is below the screen self.destroy(); // This now recycles instead of destroying } }; return self.init(); // Initialize on creation }); var Sun = Container.expand(function () { var self = Container.call(this); var sunGraphics = self.attachAsset('Sun', { anchorX: 0.5, anchorY: 0.5 }); // Initialize sun properties self.startX = -sunGraphics.width / 2; // Start off-screen to the left self.startY = 2732 * 0.25; // 25% from the top self.endX = 2048 + sunGraphics.width / 2; // End off-screen to the right self.arcHeight = 2732 * 0.1; // Arc height self.duration = 2 * 60 * 60; // 2 minutes in ticks (assuming 60 FPS) self.ticks = 0; // For the Sun class, replace the update function with: self.update = function () { if (!self.tweenStarted) { self.tweenStarted = true; // Create path tween for the arc motion tween(self, { x: self.endX, y: self.startY - self.arcHeight }, { duration: 2 * 60 * 1000, // 2 minutes in milliseconds easing: tween.sineInOut, onFinish: function onFinish() { self.destroy(); isDay = false; isNight = true; // Start spawning fireflies fireflySpawnInterval = LK.setInterval(function () { var newFirefly = new Firefly(); newFirefly.x = Math.random() * 2048; newFirefly.y = Math.random() * 2732; game.addChild(newFirefly); }, 1000); game.addChild(new Moon()); } }); } }; }); // Class for the water var Water = Container.expand(function () { var self = Container.call(this); var waterGraphics = self.attachAsset('Water', { anchorX: 0.5, anchorY: 0.7 }); self.speed = 2; self.update = function () { self.x -= self.speed * gameSpeed; // self.y = midgroundtrees1.y + midgroundtrees1.height / 2; if (self.x <= -self.width / 2) { self.x += self.width * 2; } }; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ function savePurchasedHats(hatsArray) { if (Array.isArray(hatsArray)) { storage.purchasedHats = hatsArray.join(","); } } function getPurchasedHats() { if (!storage.purchasedHats) { return []; } return storage.purchasedHats.split ? storage.purchasedHats.split(",") : storage.purchasedHats; } var cootGraphics = { width: 550, height: 423.5 }; // Define cootGraphics with default dimensions var availableHats = [{ id: 'TopHat', name: 'Top Hat', price: 100, x: cootGraphics.width / 2.65, y: -cootGraphics.height / 3 + cootGraphics.height / 2.7, rotation: -0.35 }, { id: 'Cowboy', name: 'Cowboy Hat', price: 200, x: cootGraphics.width / 2.4, y: -cootGraphics.height / 3 + cootGraphics.height / 2.6, rotation: -0.35 }, { id: 'Crown', name: 'Royal Crown', price: 800, x: cootGraphics.width / 2.65, y: -cootGraphics.height / 3 + cootGraphics.height / 2.7, rotation: -0.35 }, { id: 'Beanie', name: 'Toque', price: 200, x: cootGraphics.width / 2.65, y: -cootGraphics.height / 3 + cootGraphics.height / 2.7, rotation: -0.35 }, { id: 'Captain', name: 'Captain Hat', price: 500, x: cootGraphics.width / 2.65, y: -cootGraphics.height / 3 + cootGraphics.height / 2.7, rotation: -0.35 }, { id: 'Sockhat', name: 'Cap of Legend', price: 700, x: cootGraphics.width / 3, y: -cootGraphics.height / 3 + cootGraphics.height / 2.4, rotation: 0.25 }, { id: 'Propeller', name: 'Propeller Cap', price: 1000, x: cootGraphics.width / 2.65, y: -cootGraphics.height / 3 + cootGraphics.height / 2.7, rotation: -0.35 }, { id: 'Sombrero', name: 'Sombrero', price: 300, x: cootGraphics.width / 2.65, y: -cootGraphics.height / 3 + cootGraphics.height / 2.7, rotation: -0.35 }, { id: 'Knight', name: 'Knight Helmet', price: 500, x: cootGraphics.width / 2.8, y: -cootGraphics.height / 3 + cootGraphics.height / 1.95, rotation: 0.27 }, { id: 'Viking', name: 'Viking Helmet', price: 600, x: cootGraphics.width / 2.7, y: -cootGraphics.height / 3 + cootGraphics.height / 2.6, rotation: -0.35 }, { id: 'Astronaut', name: 'Astronaut Helmet', price: 600, x: cootGraphics.width / 2.95, y: -cootGraphics.height / 3 + cootGraphics.height / 2, rotation: 0.27 }, { id: 'Fedora', name: 'Fedora', price: 100, x: cootGraphics.width / 2.6, y: -cootGraphics.height / 3 + cootGraphics.height / 2.7, rotation: -0.15 }]; availableHats.sort(function (a, b) { return a.price - b.price; }); function showHatShop() { var menuBackground = LK.getAsset('MenuBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(menuBackground); var hatShopScreen = new HatShopScreen(); game.addChild(hatShopScreen); hatShopScreen.menuBackground = menuBackground; } function updateBackgroundFade() { // Check for day to night transition (Sun) if (isDay && !isNight) { game.children.forEach(function (child) { if (child instanceof Sun) { // Use x position to determine fade progress // When sun is halfway across screen, start fading if (child.x > 2048 * 0.85) { var fadeProgress = (child.x - 2048 * 0.85) / (2048 * 0.25); nightBackground.alpha = Math.min(fadeProgress, 1); } } }); } // Check for night to day transition (Moon) if (isNight && !isDay) { game.children.forEach(function (child) { if (child instanceof Moon) { // Use x position to determine fade progress // When moon is halfway across screen, start fading out if (child.x > 2048 * 0.85) { var fadeProgress = (child.x - 2048 * 0.85) / (2048 * 0.25); nightBackground.alpha = Math.max(1 - fadeProgress, 0); } } }); } } // Define createInterval function to fix ReferenceError var ObjectPool = { pools: {}, getPool: function getPool(type) { if (!this.pools[type]) { this.pools[type] = []; } return this.pools[type]; }, get: function get(type, Constructor, initParams) { var pool = this.getPool(type); var obj; if (pool.length > 0) { obj = pool.pop(); if (typeof obj.reset === 'function') { obj.reset(initParams); } return obj; } else { return new Constructor(initParams); } }, recycle: function recycle(type, object) { // Don't store null or undefined objects if (!object) { return; } var pool = this.getPool(type); // Limit pool size to prevent memory issues if (pool.length < 50) { // Clear any references that might cause memory leaks if (typeof object.cleanup === 'function') { object.cleanup(); } pool.push(object); } } }; function createInterval(config, startTime) { return LK.setInterval(function () { var timeRemaining = config.duration - (Date.now() - startTime); if (timeRemaining <= 0) { LK.clearInterval(this); } }, 1000); } function showInstructions() { var menuBackground = LK.getAsset('MenuBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(menuBackground); var instructionsScreen = new InstructionsScreen(); game.addChild(instructionsScreen); instructionsScreen.menuBackground = menuBackground; // Store reference for later removal } function startGame() { // Fade in background music LK.playMusic('backgroundmusic', { fade: { start: 0, end: 0.5, duration: 1500 } }); gameActive = true; Obstacle.lastDestroyTime = Date.now(); // Create coot but start off-screen coot = new Coot(); coot.x = -400; // Start off-screen left coot.y = 2732 * 0.75 - 25; game.addChild(coot); // Add coot normally coot.updateHat(); game.setChildIndex(foreground1, game.children.length - 1); game.setChildIndex(foreground2, game.children.length - 1); // Initialize other game elements if (powerUpDisplay && powerUpDisplay.parent) { game.setChildIndex(powerUpDisplay, game.children.length - 1); } coot.phoenixFeatherIcon = phoenixFeatherIcon; heartIcons.forEach(function (icon, index) { icon.alpha = index < coot.lives ? 1 : 0.2; }); dayCountDisplay = new DayCountDisplay(); LK.gui.top.addChild(dayCountDisplay); dayCountDisplay.y = 2732 * 0.25; sun = game.addChild(new Sun()); sun.x = sun.startX; sun.y = sun.startY; // Play whistle and start game sequence LK.getSound('whistle').play(); LK.setTimeout(function () { gameSpeed = 1.5; // Animate coot running in tween(coot, { x: 2048 * 0.20 }, { duration: 1000, easing: tween.quadOut, onFinish: function onFinish() { // Store final positions after coot reaches destination coot.originalX = coot.x; coot.originalY = coot.y; coot.y = coot.y || 0; // Initialize coot.y if undefined coot.originalY = coot.originalY || 0; // Initialize coot.originalY if undefined } }); }, 1000); // Wait for whistle sound to finish } function handleShieldHit() { // Remove shield asset if (coot.shieldGraphics) { coot.shieldGraphics.destroy(); coot.shieldGraphics = null; } coot.isShielded = false; coot.isInvincible = true; // Set Coot to invincible state LK.getSound('coothurt').play(); // Play coothurt sound effect when the shield is removed // Flash effect for invincibility var flashInterval = LK.setInterval(function () { coot.children[0].alpha = coot.children[0].alpha === 1 ? 0.5 : 1; }, 100); LK.setTimeout(function () { LK.clearInterval(flashInterval); coot.children[0].alpha = 1; coot.isInvincible = false; }, 1500); } // Define a single obstacleTypes array as a constant var obstacleTypes = ['Duck', 'Fish', 'Owl']; var obstacleSpawnChances = { Duck: 1, Fish: 1, Owl: 1 }; Obstacle.getRandomDelay = function (min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; }; Obstacle.spawnDelay = 6000; // Further reduce default spawn delay to 6 seconds Obstacle.respawnDelay = { Duck: Obstacle.getRandomDelay(2250, 3750), // Reduce Duck respawn delay by 25% Fish: Obstacle.getRandomDelay(2250, 3750), // Reduce Fish respawn delay by 25% Owl: Obstacle.getRandomDelay(2250, 3750) // Add Owl respawn delay }; Obstacle.lastDestroyTime = Date.now() - 5000; // Initialize to ensure first spawn check passes var background = game.addChild(LK.getAsset('Background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 2048 / 2500, scaleY: 2732 / 2500 })); var nightBackground = game.addChild(LK.getAsset('NightBackground', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 2048 / 2500, scaleY: 2732 / 2500, alpha: 0 })); var DayCount = 1; // Global variable to keep track of the number of days, initialized to 1 at game start // Initialize the sun after title screen var sun; var isDay = true; // Initialize day state var isNight = false; // Initialize night state // Initialize the midgroundtrees var midgroundtrees1 = game.addChild(new Midgroundtrees()); midgroundtrees1.x = 2048 / 2; midgroundtrees1.y = 2732 * 0.7; var midgroundtrees2 = game.addChild(new Midgroundtrees()); midgroundtrees2.x = 2048 / 2 + midgroundtrees2.width; midgroundtrees2.y = 2732 * 0.7; // Initialize the water var water1 = game.addChild(new Water()); water1.x = 2048 / 2; water1.y = midgroundtrees1.y + midgroundtrees1.height / 2; var water2 = game.addChild(new Water()); water2.x = 2048 / 2 + water2.width; water2.y = midgroundtrees2.y + midgroundtrees2.height / 2; // Initialize game logo var gameLogo = LK.getAsset('GameIcon', { anchorX: 0.5, anchorY: 0.5, x: -100, // Start off-screen to the left y: 2732 * 0.4 // Move up by 10% of the screen height }); game.addChild(gameLogo); // Initialize game variables var coot; var dragStartY = null; // Initialize dragStartY to null var dragStartX = null; // Initialize dragStartX to null var gameSpeed = 0; // Global variable for game speed, set to 0 during title screen var isTitleScreen = true; // Flag to indicate if the game is in the title screen state var gameActive = false; // Flag to indicate if the game is actively being played var speedIncreaseInterval = 30000; // 30 seconds in milliseconds var maxGameSpeed = 5.0; // Maximum game speed var lastSpeedIncreaseTime = Date.now(); // Track the last time speed was increased var score = 0; var coinCount = storage.coins || 0; // Initialize coinCount from storage var coinCounter = new Text2(Math.min(coinCount, 999).toString(), { size: 150, fill: 0xFFFFFF }); coinCounter.anchor.set(1, 0); // Anchor to the top right corner var currentObstacle = null; var warningIcon = null; // Initialize the foreground var foreground1 = new Foreground(); var foreground2 = new Foreground(); // Removed duplicate addChild calls for foregrounds game.addChild(foreground1); game.addChild(foreground2); foreground1.x = 2048 / 2; foreground1.y = 2732 * 0.9; foreground2.x = foreground1.x + foreground1.width; foreground2.y = 2732 * 0.9; foreground1.isFirst = true; foreground1.other = foreground2; foreground2.other = foreground1; // Validate positioning if (Math.abs(foreground2.x - (foreground1.x + foreground1.width)) > 1) { console.error("Foreground pieces are not correctly positioned!"); } // Add coin counter display var coinCounter = new Text2('0', { size: 150, // Increased size for better visibility fill: 0xFFFFFF }); coinCounter.anchor.set(1, 0); // Anchor to the top right corner // Add coin asset beside the coin counter var coinIcon = LK.getAsset('Coin', { anchorX: 1, anchorY: 0, x: -coinCounter.width - 10, // Position to the left of the counter with some padding y: 0 }); LK.gui.topRight.addChild(coinIcon); LK.gui.topRight.addChild(coinCounter); // Add heart icons to represent Coot's lives var heartIcons = []; for (var i = 0; i < 3; i++) { // Always create 3 heart icons var heartIcon = LK.getAsset('HeartLife', { anchorX: 0, anchorY: 0, x: -coinCounter.width - 10 - i * 110, y: coinIcon.height + 10 }); LK.gui.topRight.addChild(heartIcon); heartIcons.push(heartIcon); } // Update heart icons based on remaining lives heartIcons.forEach(function (icon, index) { icon.alpha = coot && index < coot.lives ? 1 : 0.2; // Dim the heart if life is lost }); // Initialize PowerUpDisplay after foregrounds are set up var powerUpDisplay = new PowerUpDisplay(); game.addChild(powerUpDisplay); // Initialize Phoenix Feather icon at the start of the game var phoenixFeatherIcon = new PhoenixFeatherIcon(); // coot.phoenixFeatherIcon will be set after coot initialization phoenixFeatherIcon.x = heartIcons[0].x + heartIcons[0].width / 2 - phoenixFeatherIcon.width / 2; // Center below the leftmost heart icon phoenixFeatherIcon.y = heartIcons[0].y + heartIcons[0].height + 70; // Increase padding below the heart icon LK.gui.topRight.addChild(phoenixFeatherIcon); var dayCountDisplay; game.update = function () { if (DayCount > 1 && !dayCountDisplay.visible) { dayCountDisplay.show(); } updateBackgroundFade(); if (isTitleScreen) { // Update coinCounter text on the title screen coinCounter.setText(Math.min(coinCount, 9999).toString()); coinIcon.x = -coinCounter.width - 10; // Update coin icon position to align with the coin counter // Only update background, midground trees, water, and foreground during title screen [midgroundtrees1, midgroundtrees2, water1, water2, foreground1, foreground2].forEach(function (element) { element.update(); }); // Animate game logo sliding in from the left if (gameLogo.x < 2048 / 2) { // Move logo gameLogo.x += 30; // Slide in quickly // Move buttons at the same time if (gameLogo.playButton) { // Calculate how far along the animation we are (0-1) var progress = (gameLogo.x + 100) / (2048 / 2 + 100); // Position playButton in the center gameLogo.playButton.x = -100 + progress * (2048 / 2 + 100); // Position other buttons relative to playButton gameLogo.instructionsButton.x = gameLogo.playButton.x - 320; gameLogo.hatShopButton.x = gameLogo.playButton.x + 350; } } else { // Ensure everything is at final position gameLogo.x = 2048 / 2; if (gameLogo.playButton) { gameLogo.playButton.x = 2048 / 2; gameLogo.instructionsButton.x = 2048 / 2 - 320; gameLogo.hatShopButton.x = 2048 / 2 + 350; } } if (!gameLogo.playButton && !gameLogo.instructionsButton && !gameLogo.hatShopButton) { var playButton = new PlayButton(); playButton.x = -100; // Start off-screen to the left, like the logo playButton.y = gameLogo.y + gameLogo.height / 2 + 2732 * 0.06; game.addChild(playButton); gameLogo.playButton = playButton; var instructionsButton = new InstructionsButton(); instructionsButton.x = -100; // Start off-screen to the left instructionsButton.y = playButton.y + playButton.height; game.addChild(instructionsButton); gameLogo.instructionsButton = instructionsButton; var hatShopButton = new HatShopButton(); hatShopButton.x = -100; // Start off-screen to the left hatShopButton.y = playButton.y + playButton.height; game.addChild(hatShopButton); gameLogo.hatShopButton = hatShopButton; } return; // Exit update function to prevent further updates } if (coot) { coot.update(); } phoenixFeatherIcon.update(); // Increase game speed every 20 seconds if (!isTitleScreen && Date.now() - lastSpeedIncreaseTime >= speedIncreaseInterval) { if (gameSpeed < maxGameSpeed) { gameSpeed = Math.min(gameSpeed + 0.1, maxGameSpeed); // Decrease respawn delay for all obstacles Obstacle.respawnDelay.Eagle = Math.max(Obstacle.respawnDelay.Eagle - 500, 1000); Obstacle.respawnDelay.Duck = Math.max(Obstacle.respawnDelay.Duck - 500, 1000); Obstacle.respawnDelay.Fish = Math.max(Obstacle.respawnDelay.Fish - 500, 1000); Obstacle.respawnDelay.Owl = Math.max(Obstacle.respawnDelay.Owl - 500, 1000); } lastSpeedIncreaseTime = Date.now(); } // Define createInterval function to fix ReferenceError // Removed the timer counter increment and display logic // Check for collision between the Coot and the Collision Block of the current obstacle if (currentObstacle && coot.intersects(currentObstacle.children[0])) { if (coot.isShielded) { handleShieldHit(); } else if (coot.isInvincible) { // Do nothing, invincible state } else { coot.lives -= 1; LK.getSound('coothurt').play(); // Play coothurt sound when Coot gets hit if (coot.lives <= 0 && coot.hasPhoenixFeather) { LK.getSound('phoenix').play(); // Play phoenix sound effect when lives are refilled coot.hasPhoenixFeather = false; // Remove Phoenix Feather phoenixFeatherIcon.update(); // Update Phoenix Feather icon coot.lives = 3; // Refill all lives heartIcons.forEach(function (icon) { icon.alpha = 1; // Restore all heart icons }); // Add Phoenix effect coot.isInvincible = true; // Set Coot to invincible state LK.effects.flashScreen(0xff0000, 1000); // Flash screen red for 1 second var phoenix = LK.getAsset('Phoenix', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 // Set initial opacity to 50% }); phoenix.x = 2048 / 2; phoenix.y = 2732 / 2; game.addChild(phoenix); LK.setTimeout(function () { phoenix.destroy(); // Destroy the Phoenix asset coot.isInvincible = false; // Remove invincibility after effect }, 1500); // Wait for 1.5 seconds } else if (coot.lives <= 0) { coot.isFalling = true; coot.fallStartY = coot.y; coot.fallRotation = 0; coot.fallSpeed = 5; heartIcons[0].alpha = 0.2; // Dim the last heart icon to indicate all lives lost } else { coot.isInvincible = true; heartIcons[coot.lives].alpha = 0.2; // Dim the heart icon to indicate a lost life // Flash effect for invincibility LK.effects.flashScreen(0xff0000, 100); // Flash screen red for 100ms var flashInterval = LK.setInterval(function () { coot.children[0].alpha = coot.children[0].alpha === 1 ? 0.5 : 1; }, 100); LK.setTimeout(function () { LK.clearInterval(flashInterval); coot.children[0].alpha = 1; coot.isInvincible = false; }, 1500); } } } // Update PowerUpDisplay powerUpDisplay.update(); // Update the midgroundtrees midgroundtrees1.update(); midgroundtrees2.update(); // Update the water water1.update(); water2.update(); // Update the current obstacle // Check if the current obstacle needs destruction if (currentObstacle && currentObstacle.x < -currentObstacle.width / 2) { currentObstacle.destroy(); Obstacle.lastDestroyTime = Date.now(); currentObstacle = null; } // Check if it's time to spawn a new obstacle if (gameActive && !currentObstacle) { var timeSinceLastDestroy = Date.now() - Obstacle.lastDestroyTime; if (timeSinceLastDestroy >= Obstacle.spawnDelay - 1000 && !warningIcon) { if (coot.isWarningActive) { LK.getSound('alert').play(); // Play alert sound when warning icon is displayed and player has the warning power up } var totalChance = obstacleSpawnChances.Duck + obstacleSpawnChances.Fish; if (isNight) { totalChance += obstacleSpawnChances.Owl; } var randomChance = Math.random() * totalChance; var cumulativeChance = 0; for (var i = 0; i < obstacleTypes.length; i++) { if (obstacleTypes[i] === 'Eagle' && isDay || obstacleTypes[i] === 'Owl' && isNight || obstacleTypes[i] !== 'Eagle' && obstacleTypes[i] !== 'Owl') { cumulativeChance += obstacleSpawnChances[obstacleTypes[i]]; if (randomChance < cumulativeChance) { currentObstacleType = obstacleTypes[i]; obstacleSpawnChances[currentObstacleType] = 1; // Reset chance on spawn break; } } } var warningIconClassMap = { 'Duck': DuckWarningIcon, 'Eagle': EagleWarningIcon, 'Fish': FishWarningIcon, 'Owl': OwlWarningIcon // Add OwlIcon to warning icons }; warningIcon = game.addChild(new warningIconClassMap[currentObstacleType]()); warningIcon.x = 2048 / 2; warningIcon.y = 2732 / 2; } if (timeSinceLastDestroy >= Obstacle.spawnDelay) { if (warningIcon) { warningIcon.destroy(); warningIcon = null; } currentObstacle = game.addChild(new Obstacle(currentObstacleType)); currentObstacle.x = 2048 + currentObstacle.width / 2; // Ensure it starts off-screen currentObstacle.y = currentObstacleType === 'Duck' ? 2732 * 0.80 : currentObstacleType === 'Eagle' ? 2732 * 0.25 : currentObstacleType === 'Fish' ? 2732 * 0.85 : 2732 * 0.10; // Owl positioned at 10% from the top // Increase spawn chance for obstacles not selected obstacleTypes.forEach(function (type) { if (type !== currentObstacleType) { obstacleSpawnChances[type] += 0.5; // Increase chance by 0.5 for each non-selected obstacle } }); } } if (currentObstacle) { currentObstacle.update(); } // Check for collision between the Coot and Coins game.children.forEach(function (child) { if (child instanceof Coin && coot && coot.intersects(child) && !child.collecting) { LK.getSound('coin').play(); // Play coin sound effect when coot touches the coin child.collecting = true; child.targetX = 2048 + child.children[0].width; // Target off-screen to the right child.targetY = -child.children[0].height; // Target off-screen to the top } }); // Update the foreground foreground1.update(); foreground2.update(); // Spawn butterflies randomly if (isDay && LK.ticks % 720 == 0) { // Every 3 seconds at 60 FPS var newButterfly = ObjectPool.get('Butterfly', Butterfly); newButterfly.x = 2048 + newButterfly.width / 2; // Start off-screen to the right newButterfly.y = Math.random() * (2732 * 0.6) + 2732 * 0.2; // Random Y position within midground tree layer game.addChild(newButterfly); } // Calculate spawn interval based on coin magnet state var baseSpawnRate = 120; // Normal 2-second spawn rate var magnetSpawnRate = 30; // 25% faster (120 * 0.75 = 90) var currentSpawnRate = coot && coot.hasCoinMagnet ? magnetSpawnRate : baseSpawnRate; if (LK.ticks % currentSpawnRate == 0) { var newCoin = new Coin(); newCoin.x = 2048 + newCoin.width / 2; newCoin.y = Math.random() * (2732 * 0.45) + 2732 * 0.35; game.addChild(newCoin); } // Spawn power ups randomly if (LK.ticks % 1080 == 0) { // Every 3 seconds at 60 FPS var powerUpTypes = [{ type: 'Shield', weight: 20 }, { type: 'PowerDash', weight: 25 }, { type: 'CoinMagnet', weight: 25 }, { type: 'Warning', weight: 20 }, { type: 'PhoenixFeather', weight: 10 }]; var totalWeight = powerUpTypes.reduce(function (acc, powerUp) { return acc + powerUp.weight; }, 0); var randomWeight = Math.random() * totalWeight; var cumulativeWeight = 0; var selectedPowerUpType; for (var i = 0; i < powerUpTypes.length; i++) { cumulativeWeight += powerUpTypes[i].weight; if (randomWeight < cumulativeWeight) { selectedPowerUpType = powerUpTypes[i].type; break; } } // Check if Coot already has a shield or Phoenix feather if (selectedPowerUpType === 'Shield' && coot.isShielded || selectedPowerUpType === 'PhoenixFeather' && coot.hasPhoenixFeather) { // Skip spawning this power-up return; } var newPowerup = new PowerUp(selectedPowerUpType); newPowerup.x = 2048 + newPowerup.width / 2; // Start off-screen to the right newPowerup.y = Math.random() * (2732 * 0.45) + 2732 * 0.35; // Random Y position between 35% and 80% of the screen height game.addChild(newPowerup); } }; // Handle touch events for jumping and diving game.down = function (x, y, obj) { dragStartY = y; // Record the initial y position when touch starts dragStartX = x; // Record the initial x position when touch starts }; game.move = function (x, y, obj) { if (!isTitleScreen && dragStartY !== null && dragStartX !== null) { if (x - dragStartX > 50) { // Only allow dash during these states if PowerDash is active if (coot.isJumping || coot.isFalling || coot.isDiving || coot.isReturning || coot.returnDelay > 0) { // Check for PowerDash before allowing if (coot.isPowerDashActive) { coot.isDashing = true; } } else { // Normal dash on ground, no PowerDash needed coot.isDashing = true; } dragStartX = null; // Reset dragStartX to prevent repeated dashes } if (y - dragStartY > 50 && coot && coot.originalY !== undefined && coot.y !== undefined && coot.y === coot.originalY) { // Check if the drag is downward and significant coot.dive(); dragStartY = null; // Reset dragStartY to prevent repeated dives } else if (dragStartY - y > 50 && coot.y === coot.originalY) { // Check if the drag is upward and significant coot.jump(); dragStartY = null; // Reset dragStartY to prevent repeated jumps } } }; game.up = function (x, y, obj) { dragStartY = null; // Reset dragStartY when touch ends dragStartX = null; // Reset dragStartX when touch ends };
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1", {
coins: 0
});
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Import tween plugin
// Initialize with default coins value
var Butterfly = Container.expand(function () {
var self = Container.call(this);
var butterflyGraphics;
var selectedType;
// Initialize butterfly properties
self.speedX = -2;
self.flitAmplitude = 20;
self.flitFrequency = 0.1;
self.startY = 0;
self.init = function () {
var butterflyTypes = ['GreenButterfly', 'PinkButterfly', 'YellowButterfly'];
selectedType = butterflyTypes[Math.floor(Math.random() * butterflyTypes.length)];
if (!butterflyGraphics || butterflyGraphics.assetId !== selectedType) {
if (butterflyGraphics) {
self.removeChild(butterflyGraphics);
}
butterflyGraphics = self.attachAsset(selectedType, {
anchorX: 0.5,
anchorY: 0.5
});
}
self.startY = Math.random() * (2732 * 0.6) + 2732 * 0.2;
return self;
};
self.reset = function () {
var butterflyTypes = ['GreenButterfly', 'PinkButterfly', 'YellowButterfly'];
var newType = butterflyTypes[Math.floor(Math.random() * butterflyTypes.length)];
if (newType !== selectedType) {
selectedType = newType;
if (butterflyGraphics) {
self.removeChild(butterflyGraphics);
}
butterflyGraphics = self.attachAsset(selectedType, {
anchorX: 0.5,
anchorY: 0.5
});
}
self.startY = Math.random() * (2732 * 0.6) + 2732 * 0.2;
return self;
};
self.destroy = function () {
if (self.parent) {
self.parent.removeChild(self);
}
ObjectPool.recycle('Butterfly', self);
};
self.update = function () {
// Move the butterfly left
self.x += self.speedX * gameSpeed;
// Apply flitting motion
self.y = self.startY + Math.sin(self.x * self.flitFrequency) * self.flitAmplitude;
// Simulate wing flapping by adjusting scale
var flapScale = 0.2 * Math.sin(LK.ticks * 0.3) + 1;
butterflyGraphics.scale.set(flapScale);
// Destroy the butterfly if it goes off screen
if (self.x < -butterflyGraphics.width / 2) {
self.destroy();
}
};
return self.init();
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('Coin', {
anchorX: 0.5,
anchorY: 0.5
});
// Initialize coin properties
self.rotationSpeed = 0.05; // Rotation speed for the coin
self.speedX = -5; // Speed of the coin moving left
self.collecting = false; // Flag to check if the coin is being collected
self.targetX = 0; // Target X position for collection animation
self.targetY = 0; // Target Y position for collection animation
self.update = function () {
// Generate GoldSparkle particles
if (Math.random() < 0.0525) {
var sparkle = ObjectPool.get('GoldSparkle', GoldSparkle);
sparkle.x = self.x + (Math.random() * coinGraphics.width - coinGraphics.width / 2);
sparkle.y = self.y + (Math.random() * coinGraphics.height - coinGraphics.height / 2);
sparkle.coin = self; // Associate sparkle with the coin
game.addChild(sparkle);
}
// Move the coin left
self.x += self.speedX * gameSpeed;
// Apply spin effect over Y axis
coinGraphics.scale.x = Math.sin(self.x * 0.015);
if (coot && coot.isMagnetActive && !self.collecting && self.x < 2048 * 0.9) {
// Set initial target and mark as magnetized
self.targetX = coot.x;
self.targetY = coot.y;
self.magnetized = true;
}
if (self.magnetized && !self.collecting) {
// Continuously update target position to track Coot
self.targetX = coot.x;
self.targetY = coot.y;
// Move directly toward Coot with increasing speed as it gets closer
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Calculate movement speed based on distance
var speed = Math.max(5, 20 - distance / 30);
// Move coin toward Coot
self.x += dx / distance * speed * gameSpeed;
self.y += dy / distance * speed * gameSpeed;
// If very close to Coot, complete collection
if (distance < 30) {
self.collecting = true; // Mark as being collected to prevent double counting
LK.getSound('coin').play();
coinCount++;
storage.coins = coinCount;
coinCounter.setText(Math.min(coinCount, 9999).toString());
coinIcon.x = -coinCounter.width - 10;
// Check for life addition
if (coinCount % 100 === 0 && coot && coot.lives < 3) {
coot.lives++;
heartIcons[coot.lives - 1].alpha = 1;
}
// Clean up sparkles
game.children.forEach(function (child) {
if (child instanceof GoldSparkle && child.coin === self) {
child.destroy();
}
});
// Remove from game
if (self.parent) {
self.parent.removeChild(self);
}
}
}
if (self.collecting && !self.tweenStarted) {
// Set flag to prevent multiple tweens
self.tweenStarted = true;
// Create collection tween
tween(self, {
x: self.targetX,
y: self.targetY
}, {
duration: 500,
easing: tween.quartOut,
onUpdate: function onUpdate(progress) {
coinGraphics.scale.set(1 - progress * 0.9);
},
onFinish: function onFinish() {
if (self.targetX === coot.x && self.targetY === coot.y) {
LK.getSound('coin').play(); // Play sound effect if the target is coot
}
// Count coin and update display
coinCount++;
storage.coins = coinCount;
coinCounter.setText(Math.min(coinCount, 999).toString());
coinIcon.x = -coinCounter.width - 10;
// Check for life addition
if (coinCount % 100 === 0 && coot && coot.lives < 3) {
coot.lives++;
heartIcons[coot.lives - 1].alpha = 1;
}
// Clean up
game.children.forEach(function (child) {
if (child instanceof GoldSparkle && child.coin === self) {
child.destroy();
}
});
// Remove from game
if (self.parent) {
self.parent.removeChild(self);
}
}
});
} else if (self.x < -coinGraphics.width / 2) {
// Destroy the coin if it goes off screen
self.destroy();
}
};
});
var CollisionBlock = Container.expand(function () {
var self = Container.call(this);
var collisionBlockGraphics = self.attachAsset('Collisionblock', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
});
// Assets will be automatically created and loaded by the LK engine based on their usage in the code.
// Example assets: 'coot', 'obstacle', 'background'
// Class for the main character, the American coot
var Coot = Container.expand(function () {
var self = Container.call(this);
var cootGraphics = self.attachAsset('coot', {
anchorX: 0.5,
anchorY: 0.5
});
// Add this to ensure cootGraphics is always at bottom
self.ensureGraphicsOrder = function () {
self.setChildIndex(cootGraphics, 0);
if (cootGraphics.children.length > 0) {
// Find hat if it exists
for (var i = 0; i < cootGraphics.children.length; i++) {
if (cootGraphics.children[i].isHat) {
cootGraphics.setChildIndex(cootGraphics.children[i], cootGraphics.children.length - 1);
break;
}
}
}
};
self.hasHat = false; // Flag to track if a hat is equipped
// Add hat update method
self.updateHat = function () {
// Remove existing hat
for (var i = 0; i < cootGraphics.children.length; i++) {
if (cootGraphics.children[i].isHat) {
cootGraphics.removeChild(cootGraphics.children[i]);
break;
self.ensureGraphicsOrder();
}
;
}
// Check if a hat is equipped
var hatId = storage.equippedHat || null;
if (hatId) {
try {
// Attach hat to cootGraphics for movement
var hatId = storage.equippedHat || null;
if (hatId) {
try {
// Find hat config
var hatConfig = availableHats.find(function (hat) {
return hat.id === hatId;
});
if (hatConfig) {
var hatGraphics = cootGraphics.attachAsset(hatId, {
anchorX: 0.5,
anchorY: 1.0,
x: hatConfig.x,
y: hatConfig.y,
rotation: hatConfig.rotation
});
hatGraphics.isHat = true;
self.setChildIndex(cootGraphics, 0);
cootGraphics.setChildIndex(hatGraphics, cootGraphics.children.length - 1);
self.hasHat = true;
}
} catch (e) {
console.error("Could not load hat asset: " + hatId);
storage.equippedHat = null;
self.hasHat = false;
}
}
// Mark as hat
hatGraphics.isHat = true;
// Make sure cootGraphics is at the bottom of main container
self.setChildIndex(cootGraphics, 0);
// Set hat to top within cootGraphics
cootGraphics.setChildIndex(hatGraphics, cootGraphics.children.length - 1);
self.hasHat = true;
} catch (e) {
console.error("Could not load hat asset: " + hatId);
storage.equippedHat = null;
self.hasHat = false;
}
} else {
self.hasHat = false;
}
};
self.originalX = self.x; // Store original X position
self.speed = 6.5;
self.dashSpeed = 15;
self.lives = 3; // Initialize Coot with three lives
self.dashDelay = 30;
self.isFalling = false;
self.isDashing = false;
self.fallRotation = 0; // Initialize fall rotation
self.dashTimer = 0;
self.jumpVelocity = 40; // Increased initial upward velocity for jump
self.gravity = 1.0; // Gravity to apply during jump
self.jumpHeight = 105;
self.isJumping = false;
self.isReturning = false; // New state to handle returning to top after diving
self.isInvincible = false; // Initialize invincible state
self.isShielded = false; // Initialize shielded state
self.isMagnetActive = false; // Initialize magnet state
self.isWarningActive = false; // Initialize warning state
self.isPowerDashActive = false; // Initialize PowerDash state
self.hasPhoenixFeather = false; // Initialize Phoenix Feather state
self.powerUpTimers = {
'CoinMagnet': {
endTime: 0
},
'PowerDash': {
endTime: 0
},
'Warning': {
endTime: 0
}
};
self.update = function () {
if (self.isFalling) {
handleFalling();
} else if (self.isDiving) {
handleDiving();
} else if (self.returnDelay > 0) {
handleReturnDelay();
} else if (self.isReturning) {
handleReturning();
}
// Define state key mapping for power-ups
var stateKeyMap = {
'CoinMagnet': 'isMagnetActive',
'PowerDash': 'isPowerDashActive',
'Warning': 'isWarningActive'
};
// Check power-up timers
for (var powerUpType in self.powerUpTimers) {
if (self.powerUpTimers.hasOwnProperty(powerUpType)) {
var timer = self.powerUpTimers[powerUpType];
if (Date.now() >= timer.endTime) {
var stateKey = stateKeyMap[powerUpType];
if (self[stateKey]) {
self[stateKey] = false; // Deactivate the power-up
}
}
}
}
function handleFalling() {
if (self.lives <= 0) {
self.y -= self.fallSpeed;
self.fallRotation += 0.05;
cootGraphics.rotation = -self.fallRotation;
if (self.y <= self.fallStartY - 50) {
self.fallSpeed = -10;
}
if (self.y > 2732) {
LK.showGameOver();
}
} else {
self.y += self.gravity * 10; // Increase gravity effect during fall
if (self.y >= self.originalY) {
self.y = self.originalY;
self.isFalling = false;
self.jumpVelocity = 40;
self.isJumping = false;
}
cootGraphics.rotation = 0;
}
}
function handleDiving() {
self.y += self.speed * 1.5;
cootGraphics.alpha = Math.max(0.5, cootGraphics.alpha - 0.05);
cootGraphics.tint = 0x0000ff;
if (self.y >= 2732 * 0.90) {
self.y = 2732 * 0.90;
self.isDiving = false;
self.returnDelay = Date.now() + 1000;
}
}
function handleReturnDelay() {
if (Date.now() >= self.returnDelay) {
self.returnDelay = 0;
if (self.y >= 2732 * 0.90) {
self.isReturning = true;
}
}
}
function handleReturning() {
self.y -= self.speed * 1.5;
var transitionFactor = 1 - (self.y - self.originalY) / (2732 * 0.90 - self.originalY);
cootGraphics.alpha = 0.5 + 0.5 * transitionFactor;
var startTint = self.underwaterTint;
var endTint = 0xffffff;
var r = (startTint >> 16 & 0xff) + ((endTint >> 16 & 0xff) - (startTint >> 16 & 0xff)) * transitionFactor;
var g = (startTint >> 8 & 0xff) + ((endTint >> 8 & 0xff) - (startTint >> 8 & 0xff)) * transitionFactor;
var b = (startTint & 0xff) + ((endTint & 0xff) - (startTint & 0xff)) * transitionFactor;
cootGraphics.tint = (r << 16) + (g << 8) + b;
if (self.y <= self.originalY) {
self.y = self.originalY;
self.isReturning = false;
cootGraphics.alpha = 1.0;
cootGraphics.tint = 0xffffff;
}
if (self.isReturning) {
self.x += (self.originalX - self.x) * 0.1;
}
}
// Add wobble effect to the coot while running
if (!self.isJumping && !self.isDiving && !self.isReturning && !self.isFalling) {
cootGraphics.rotation = Math.sin(LK.ticks / 5) * 0.1; // Increase wobble speed by reducing the divisor
// Generate 1-2 water splash particles every 60 ticks (1 second) only when not jumping, diving, or falling
if (!self.isJumping && !self.isDiving && !self.isFalling && LK.ticks % 60 == 0) {
var numParticles = Math.floor(Math.random() * 2) + 1;
for (var i = 0; i < numParticles; i++) {
var splashLeft = ObjectPool.get('Splash', Splash);
splashLeft.x = self.x - cootGraphics.width / 2;
splashLeft.y = self.y + cootGraphics.height / 2;
game.addChildAt(splashLeft, game.getChildIndex(foreground1) - 1);
var splashRight = ObjectPool.get('Splash', Splash);
splashRight.x = self.x + cootGraphics.width / 2;
splashRight.y = self.y + cootGraphics.height / 2;
game.addChildAt(splashRight, game.getChildIndex(foreground1) - 1);
}
}
}
// Handle dash state
if (self.isDashing) {
if (self.dashTimer === 0) {
LK.getSound('dashsound').play(); // Play dashsound when dash starts
}
if (self.dashTimer < self.dashDelay) {
// Dash forward
if (self.x < 2048 * 0.75) {
// Stop at 3/4 of screen width
self.x += self.dashSpeed * 1.1;
}
// Create echo trail effect only if PowerDash is active
if (self.isPowerDashActive) {
var echo = ObjectPool.get('EchoTrail', EchoTrail);
echo.x = self.x;
echo.y = self.y;
game.addChild(echo);
}
self.dashTimer++;
} else {
var returnSpeed = 10; // Fixed return speed
self.x += Math.sign(self.originalX - self.x) * Math.min(returnSpeed, Math.abs(self.originalX - self.x));
if (Math.abs(self.x - self.originalX) < returnSpeed) {
self.x = self.originalX;
tween(self, {
x: self.originalX
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
self.isDashing = false;
self.dashTimer = 0;
}
});
}
}
}
// Ensure smooth return to original position if falling and returning from a dash
if (self.isFalling && self.isReturning) {
var returnSpeed = 10; // Fixed return speed
self.x += Math.sign(self.originalX - self.x) * Math.min(returnSpeed, Math.abs(self.originalX - self.x));
if (Math.abs(self.x - self.originalX) < returnSpeed) {
self.x = self.originalX;
self.isReturning = false;
}
}
self.ensureGraphicsOrder();
};
self.isDiving = false;
self.isDashing = false;
self.dashSpeed = 15;
self.dashDelay = 60;
self.dashTimer = 0;
self.originalX = self.x;
self.jump = function () {
if (canPerformAction()) {
// Cancel any existing tweens on this object
tween.stop(self);
self.isJumping = true;
LK.getSound('jumpsound').play();
self.originalX = self.x;
self.isReturning = false;
self.originalY = self.y;
// Fixed jump height - much higher than before
var jumpHeight = 805; // Try with a specific large value
// Jump tween with steep curve
tween(self, {
y: self.y - jumpHeight
}, {
duration: 450,
// Longer duration
easing: tween.backOut,
onFinish: function onFinish() {
// Fall tween back to original position
tween(self, {
y: self.originalY
}, {
duration: 600,
// Longer duration down too
easing: tween.backIn,
onFinish: function onFinish() {
self.isJumping = false;
}
});
}
});
}
};
self.dash = function () {
if (canPerformAction()) {
self.isDashing = true;
self.originalX = self.x;
self.dashTimer = 0;
}
};
self.dive = function () {
if (canPerformAction()) {
LK.getSound('cootdive').play(); // Play cootdive sound effect when the coot starts to dive
self.isDiving = true;
self.returnDelay = Date.now() + 5000;
self.isReturning = false;
self.underwaterTint = 0x4444ff;
self.originalX = self.x;
self.originalY = self.y;
self.y += self.speed * 2;
generateSplashParticles(2, 4);
}
};
function canPerformAction() {
return !self.isJumping && !self.isDiving && !self.isReturning && !self.isDashing;
}
function generateSplashParticles(min, max) {
var numParticles = Math.floor(Math.random() * (max - min + 1)) + min;
for (var i = 0; i < numParticles; i++) {
var splash = ObjectPool.get('Splash', Splash);
splash.x = self.x;
splash.y = self.y + cootGraphics.height / 2;
game.addChildAt(splash, game.getChildIndex(foreground1) - 1);
}
}
});
var DayCountDisplay = Container.expand(function () {
var self = Container.call(this);
var dayText = new Text2('Day ' + DayCount, {
size: 100,
fill: 0xFFFFFF
});
dayText.anchor.set(0.5, 0.5);
dayText.alpha = 0; // Set alpha to 0 by default
self.addChild(dayText);
self.show = function () {
dayText.setText('Day ' + DayCount);
dayText.alpha = 1;
LK.setTimeout(function () {
dayText.alpha = 0; // Set alpha back to 0 instead of destroying
}, 1500);
// Set a timer to make the day count display visible for 3 seconds every time the day count increases
LK.setTimeout(function () {
dayText.alpha = 1;
LK.setTimeout(function () {
dayText.alpha = 0;
}, 1500);
}, 1500);
};
});
var DuckWarningIcon = Container.expand(function () {
var self = Container.call(this);
var iconGraphics = self.attachAsset('DuckIcon', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.75
});
self.flashInterval = 0;
self.update = function () {
self.flashInterval++;
if (coot.isWarningActive) {
if (self.flashInterval % 30 < 15) {
iconGraphics.alpha = 0.75;
} else {
iconGraphics.alpha = 0.5;
}
} else {
iconGraphics.alpha = 0;
}
};
});
var EagleWarningIcon = Container.expand(function () {
var self = Container.call(this);
var iconGraphics = self.attachAsset('EagleIcon', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.75
});
self.flashInterval = 0;
self.update = function () {
self.flashInterval++;
if (coot.isWarningActive) {
if (self.flashInterval % 30 < 15) {
iconGraphics.alpha = 0.75;
} else {
iconGraphics.alpha = 0.5;
}
} else {
iconGraphics.alpha = 0;
}
};
});
var EchoTrail = Container.expand(function () {
var self = Container.call(this);
var echoGraphics;
self.fadeRate = 0.05;
self.init = function () {
if (!echoGraphics) {
echoGraphics = self.attachAsset('coot', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
} else {
echoGraphics.alpha = 0.5;
}
return self;
};
self.reset = function () {
if (echoGraphics) {
echoGraphics.alpha = 0.5;
}
return self;
};
self.destroy = function () {
if (self.parent) {
self.parent.removeChild(self);
}
ObjectPool.recycle('EchoTrail', self);
};
self.update = function () {
echoGraphics.alpha -= self.fadeRate;
if (echoGraphics.alpha <= 0) {
self.destroy();
}
};
return self.init();
});
var Firefly = Container.expand(function () {
var self = Container.call(this);
var fireflyGraphics = self.attachAsset('Firefly', {
anchorX: 0.5,
anchorY: 0.5
});
// Initialize firefly properties
self.speedX = Math.random() * 8 - 4; // Further increased random horizontal speed
self.speedY = Math.random() * 8 - 4; // Further increased random vertical speed
self.fadeRate = 0.005; // Much slower fade rate for slow pulsing
self.flickerTimer = Math.random() * 240 + 120; // Random flicker timer between 2 to 4 seconds
self.alphaDirection = 1; // Direction of alpha change (1 for fade in, -1 for fade out)
self.lifetime = Math.random() * 180 + 180; // Random lifetime between 3 to 5 seconds
self.update = function () {
// Move the firefly
self.x += self.speedX;
self.y += self.speedY;
// Create trail effect
var trail = ObjectPool.get('FireflyTrail', FireflyTrail);
trail.x = self.x;
trail.y = self.y;
game.addChild(trail);
// Loop around the screen
if (self.x < 0) {
self.x = 2048;
}
if (self.x > 2048) {
self.x = 0;
}
if (self.y < 0) {
self.y = 2732;
}
if (self.y > 2732) {
self.y = 0;
}
// Flicker effect
self.flickerTimer--;
if (self.flickerTimer <= 0) {
fireflyGraphics.alpha = Math.random(); // Random alpha for flicker
self.flickerTimer = Math.random() * 60 + 30; // Reset flicker timer
}
// Fade in and out
fireflyGraphics.alpha += self.fadeRate * self.alphaDirection;
if (fireflyGraphics.alpha <= 0 || fireflyGraphics.alpha >= 1) {
self.alphaDirection *= -1; // Reverse fade direction
}
// Destroy firefly after its lifetime
self.lifetime--;
if (self.lifetime <= 0) {
self.destroy();
}
};
});
var FireflyTrail = Container.expand(function () {
var self = Container.call(this);
var trailGraphics;
self.fadeRate = 0.02;
self.init = function () {
if (!trailGraphics) {
trailGraphics = self.attachAsset('FireflyTrail', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
} else {
trailGraphics.alpha = 1;
}
return self;
};
self.reset = function () {
if (trailGraphics) {
trailGraphics.alpha = 1;
}
return self;
};
self.destroy = function () {
if (self.parent) {
self.parent.removeChild(self);
}
ObjectPool.recycle('FireflyTrail', self);
};
self.update = function () {
trailGraphics.alpha -= self.fadeRate;
if (trailGraphics.alpha <= 0) {
self.destroy();
}
};
return self.init();
});
var FishWarningIcon = Container.expand(function () {
var self = Container.call(this);
var iconGraphics = self.attachAsset('FishIcon', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.75
});
self.flashInterval = 0;
self.update = function () {
self.flashInterval++;
if (coot.isWarningActive) {
if (self.flashInterval % 30 < 15) {
iconGraphics.alpha = 0.75;
} else {
iconGraphics.alpha = 0.5;
}
} else {
iconGraphics.alpha = 0;
}
};
});
// Define a single obstacleTypes array as a constant
// Class for the foreground
var Foreground = Container.expand(function () {
var self = Container.call(this);
var foregroundGraphics = self.attachAsset('Foreground', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.isFirst = false; // Will be set during initialization
self.update = function () {
// Only move the first piece
if (self.isFirst) {
self.x -= self.speed * gameSpeed; // Apply gameSpeed to movement
// Move second piece to maintain fixed distance
self.other.x = self.x + self.width;
// Reset both pieces when first goes off screen
if (self.x < -self.width / 1.5) {
// Reset earlier - when piece is mostly off screen
self.x += self.width;
self.other.x = self.x + self.width;
}
}
};
});
var GoldSparkle = Container.expand(function () {
var self = Container.call(this);
var sparkleGraphics;
// Initialize variables but don't create graphics yet
self.speedX = 0;
self.speedY = 0;
self.fadeRate = 0.02;
self.coin = null; // Reference to associated coin
self.init = function () {
// Only create graphics if they don't exist
if (!sparkleGraphics) {
sparkleGraphics = self.attachAsset('GoldSparkle', {
anchorX: 0.5,
anchorY: 0.5
});
}
// Set initial properties
self.speedX = (Math.random() * 2 - 1) * 0.75;
self.speedY = (Math.random() * 2 - 1) * 0.75;
sparkleGraphics.alpha = 1;
return self;
};
self.reset = function () {
// Reset properties without recreating the graphics
self.speedX = (Math.random() * 2 - 1) * 0.75;
self.speedY = (Math.random() * 2 - 1) * 0.75;
self.coin = null;
if (sparkleGraphics) {
sparkleGraphics.alpha = 1;
}
return self;
};
self.cleanup = function () {
// Clear any references to other objects
self.coin = null;
};
self.destroy = function () {
// Instead of destroying, return to pool
if (self.parent) {
self.parent.removeChild(self);
}
ObjectPool.recycle('GoldSparkle', self);
};
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
sparkleGraphics.alpha -= self.fadeRate;
if (sparkleGraphics.alpha <= 0) {
self.destroy(); // This now recycles instead of destroying
}
};
return self.init(); // Initialize on creation
});
var HatShopButton = Container.expand(function () {
var self = Container.call(this);
var hatShopButtonGraphics = self.attachAsset('HatShopButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function () {
gameLogo.visible = false;
if (gameLogo.playButton) {
gameLogo.playButton.visible = false;
}
if (gameLogo.instructionsButton) {
gameLogo.instructionsButton.visible = false;
}
if (gameLogo.hatShopButton) {
gameLogo.hatShopButton.visible = false;
}
showHatShop();
};
});
var HatShopScreen = Container.expand(function () {
var self = Container.call(this);
// Title
var shopTitle = new Text2('Hat Shop', {
size: 100,
fill: 0xFFFFFF,
fontWeight: "bold"
});
shopTitle.anchor.set(0.5, 0);
shopTitle.x = 2048 / 2;
shopTitle.y = 2732 * 0.15;
self.addChild(shopTitle);
// Initialize purchased hats array if not present
if (!storage.purchasedHats) {
storage.purchasedHats = [];
}
// Hat grid
var gridItems = [];
var itemsPerRow = 3;
var itemWidth = 400;
var itemHeight = 400;
var padding = 50;
var startX = 2048 / 2 - (itemWidth + padding) * (itemsPerRow / 2) + itemWidth / 2 + 2048 * 0.01;
var startY = 2732 * 0.27;
availableHats.forEach(function (hat, index) {
var row = Math.floor(index / itemsPerRow);
var col = index % itemsPerRow;
var itemContainer = new Container();
itemContainer.x = startX + col * (itemWidth + padding);
itemContainer.y = startY + row * (itemHeight + padding);
// Background
var bg = LK.getAsset('Collisionblock', {
anchorX: 0.5,
anchorY: 0.5,
width: itemWidth,
height: itemHeight,
alpha: 0.5
});
itemContainer.addChild(bg);
// Hat image
var hatImg = LK.getAsset(hat.id, {
anchorX: 0.5,
anchorY: 0.5,
y: -50,
scaleX: 1.35,
// Increase width by 25%
scaleY: 1.35 // Increase height by 25%
});
itemContainer.addChild(hatImg);
// Hat name
var nameText = new Text2(hat.name, {
size: 50,
// Increased by 15%
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.y = 50;
itemContainer.addChild(nameText);
// Price or status
var isPurchased = storage.purchasedHats && storage.purchasedHats.indexOf(hat.id) !== -1;
var isEquipped = storage.equippedHat === hat.id;
var statusText;
if (isEquipped) {
statusText = new Text2('EQUIPPED', {
size: 40,
fill: 0x00FF00
});
} else if (isPurchased) {
statusText = new Text2('OWNED', {
size: 40,
fill: 0xAAAAAA
});
} else {
statusText = new Text2(hat.price + ' coins', {
size: 40,
// Increased by 15%
fill: 0xFFD700
});
}
statusText.anchor.set(0.5, 0);
statusText.y = 100;
itemContainer.addChild(statusText);
// Add interaction
itemContainer.down = function () {
handleHatSelection(hat, statusText);
};
self.addChild(itemContainer);
gridItems.push(itemContainer);
});
// Back button
var backButton = new Text2('Back', {
size: 100,
fill: 0xFFFFFF,
fontWeight: "bold"
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 2048 / 2;
backButton.y = 2732 * 0.875;
self.addChild(backButton);
backButton.down = function () {
self.menuBackground.destroy();
self.destroy();
isTitleScreen = true;
gameLogo.visible = true;
if (gameLogo.playButton) {
gameLogo.playButton.visible = true;
}
if (gameLogo.instructionsButton) {
gameLogo.instructionsButton.visible = true;
}
if (gameLogo.hatShopButton) {
gameLogo.hatShopButton.visible = true;
}
};
function handleHatSelection(hat, statusText) {
var isPurchased = storage.purchasedHats && storage.purchasedHats.indexOf(hat.id) !== -1;
var isEquipped = storage.equippedHat === hat.id;
if (isEquipped) {
// Unequip hat
storage.equippedHat = null;
// Update UI
self.menuBackground.destroy();
self.destroy();
showHatShop();
} else if (isPurchased) {
// Equip hat
storage.equippedHat = hat.id;
// Update UI
self.menuBackground.destroy();
self.destroy();
showHatShop();
} else if (storage.coins >= hat.price) {
// Purchase hat
storage.coins -= hat.price;
// Initialize array if needed
if (!storage.purchasedHats) {
storage.purchasedHats = [];
}
// Get current purchased hats as array
var currentHats = getPurchasedHats();
// Add new hat
currentHats.push(hat.id);
// Save back to storage
savePurchasedHats(currentHats);
storage.equippedHat = hat.id;
// Update UI
if (typeof coinDisplay !== 'undefined') {
coinDisplay.setText('Coins: ' + storage.coins);
}
// Update coin counter in game
coinCount = storage.coins;
coinCounter.setText(Math.min(coinCount, 9999).toString());
// Refresh shop
self.menuBackground.destroy();
self.destroy();
showHatShop();
// Show purchase confirmation
var confirmText = new Text2('Purchased ' + hat.name + '!', {
size: 100,
fill: 0x00FF00
});
confirmText.anchor.set(0.5, 0.5);
confirmText.x = 2048 / 2;
confirmText.y = 2732 / 2;
game.addChild(confirmText);
LK.setTimeout(function () {
confirmText.destroy();
}, 1500);
// Play purchase sound
LK.getSound('coin').play();
} else {
// Not enough coins
var errorText = new Text2('Not enough coins!', {
size: 100,
fill: 0xFF0000
});
errorText.anchor.set(0.5, 0.5);
errorText.x = 2048 / 2;
errorText.y = 2732 / 2;
game.addChild(errorText);
LK.setTimeout(function () {
errorText.destroy();
}, 1500);
}
}
});
var InstructionsButton = Container.expand(function () {
var self = Container.call(this);
var instructionsButtonGraphics = self.attachAsset('InstructionsButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function () {
gameLogo.visible = false;
if (gameLogo.playButton) {
gameLogo.playButton.visible = false;
}
if (gameLogo.instructionsButton) {
gameLogo.instructionsButton.visible = false;
}
showInstructions();
};
});
var InstructionsScreen = Container.expand(function () {
var self = Container.call(this);
var instructionsText = new Text2('Swipe up to jump,\ndown to dive,\nand right to dash!', {
size: 80,
fill: 0xFFFFFF,
align: "center"
});
instructionsText.anchor.set(0.5, 0);
instructionsText.x = 2048 / 2;
instructionsText.y = 2732 * 0.15;
self.addChild(instructionsText);
// Add power-up icons and explanations
var powerUpsInfo = [{
type: 'CoinMagnet',
text: 'Coin Magnet: Attracts coins'
}, {
type: 'Shield',
text: 'Shield: Protects from one hit'
}, {
type: 'PhoenixFeather',
text: 'Phoenix Feather: Revive once'
}, {
type: 'PowerDash',
text: 'Power Dash: Dash while jumping or diving'
}, {
type: 'Warning',
text: 'Warning: Alerts of upcoming obstacles'
}];
powerUpsInfo.forEach(function (info, index) {
var icon = LK.getAsset(info.type, {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300 - 2048 * 0.10,
// Move icons further left
y: 2732 * 0.30 + index * 200
});
self.addChild(icon);
var text = new Text2(info.text, {
size: 60,
fill: 0xFFFFFF,
align: "left"
});
text.anchor.set(0, 0.5);
text.x = 2048 / 2 - 300 + 2048 * 0.035 - 2048 * 0.07;
text.y = icon.y + 20;
self.addChild(text);
});
var instructionDetails = new Text2('Jump over the Rock,\nDive away from the Owl,\nDash away from the Fish.\n100 Coins = 1 heart.', {
size: 70,
fill: 0xFFFFFF,
align: "center"
});
instructionDetails.anchor.set(0.5, 0);
instructionDetails.x = 2048 / 2;
instructionDetails.y = 2732 * 0.66; // Move up by 5% from the original position
self.addChild(instructionDetails);
var backButton = new Text2('Back', {
size: 100,
fill: 0xFFFFFF,
fontWeight: "bold"
});
backButton.anchor.set(0.5, 0.5);
backButton.x = 2048 / 2;
backButton.y = 2732 * 0.88; // Move back button down by 1.5%
self.addChild(backButton);
backButton.down = function () {
self.menuBackground.destroy(); // Remove the MenuBackground
self.destroy();
isTitleScreen = true;
gameLogo.visible = true;
if (gameLogo.playButton) {
gameLogo.playButton.visible = true;
}
if (gameLogo.instructionsButton) {
gameLogo.instructionsButton.visible = true;
}
};
});
// Class for the midgroundtrees
var Midgroundtrees = Container.expand(function () {
var self = Container.call(this);
var midgroundtreesGraphics = self.attachAsset('Midgroundtrees', {
anchorX: 0.5,
anchorY: 0.7
});
self.speed = 3;
self.update = function () {
self.x -= self.speed * gameSpeed;
// self.y = coot.y;
if (self.x <= -self.width / 2) {
self.x += self.width * 2;
}
};
});
var Moon = Container.expand(function () {
var self = Container.call(this);
var moonGraphics = self.attachAsset('Moon', {
anchorX: 0.5,
anchorY: 0.5
});
// Initialize moon properties
self.startX = -moonGraphics.width / 2; // Start off-screen to the left
self.startY = 2732 * 0.25; // Match the Sun's position (25% from the top)
self.endX = 2048 + moonGraphics.width / 2; // End off-screen to the right
self.arcHeight = 2732 * 0.1; // Match the Sun's arc height
self.duration = 2 * 60 * 60; // 2 minutes in ticks (assuming 60 FPS)
self.ticks = 0;
// For the Moon class, replace the update function with:
self.update = function () {
if (!self.tweenStarted) {
// Set initial position
self.x = self.startX;
self.y = self.startY;
self.tweenStarted = true;
// First tween: Move up in an arc
tween(self, {
x: self.startX + (self.endX - self.startX) * 0.5,
y: self.startY - self.arcHeight
}, {
duration: 60 * 1000,
// 1 minute in milliseconds
easing: tween.sineInOut,
onFinish: function onFinish() {
// Second tween: Move down in an arc to end position
tween(self, {
x: self.endX,
y: self.startY
}, {
duration: 60 * 1000,
// 1 minute in milliseconds
easing: tween.sineInOut,
onFinish: function onFinish() {
self.destroy();
// Switch to day state
isDay = true;
isNight = false;
DayCount += 1;
dayCountDisplay.show();
// Stop spawning fireflies
if (fireflySpawnInterval) {
LK.clearInterval(fireflySpawnInterval);
fireflySpawnInterval = null;
}
var newSun = new Sun();
newSun.x = newSun.startX; // Use the sun's defined starting X position
newSun.y = newSun.startY; // Set the proper starting Y position
game.addChild(newSun);
}
});
}
});
}
};
});
// Moved Owl logic into Obstacle class
var Obstacle = Container.expand(function (type) {
var self = Container.call(this);
var graphics;
var collisionBlock = self.addChild(new CollisionBlock());
collisionBlock.x = 0;
collisionBlock.y = 0;
graphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
// Add a Ripple instance to the Duck obstacle
if (type === 'Duck') {
self.lastRippleTime = Date.now();
var ripple = ObjectPool.get('Ripple', Ripple);
ripple.x = 0; // Center the ripple on the Duck
ripple.y = graphics.height / 2; // Position the ripple at the bottom of the Duck
self.addChildAt(ripple, 0); // Add ripple below the Duck but above the water layer
}
self.speedX = -6;
if (type === 'Fish') {
self.isJumping = false;
self.pauseTime = 0;
graphics.tint = 0x4444ff;
graphics.alpha = 0.7;
}
if (type === 'Fish') {
self.speedX = -6; // Back to original fast swimming speed
self.jumpSpeed = 0.05; // Slow jump speed control
self.isJumping = false;
self.isPausing = false;
self.pauseTime = 0;
self.lastSplashTime = Date.now(); // Add splash timer
graphics.tint = 0x4444ff; // Set underwater blue tint initially
graphics.alpha = 0.7; // Set underwater opacity initially
self.isFalling = false;
self.jumpVelocity = 0;
self.jumpStartY = self.y; // Store original position
self.jumpTime = 0; // Initialize jump time counter
self.y = 2732 * 0.90; // Set Fish Y position to the bottom of the Coot's dive
}
if (type === 'Owl') {
self.speedX = -6; // Speed of the owl moving left
self.speedY = 0; // Initial vertical speed
self.x = 2048 + graphics.width / 2; // Spawn at the top right of the screen
self.isDiving = false; // Initialize isDiving state
self.hasDived = false; // Initialize hasDived state to false by default
if (!self.soundPlayed && self.x >= 2048 - 50) {
LK.getSound('owl').play();
self.soundPlayed = true; // Set flag to prevent repeating
}
}
self.update = function () {
self.x += self.speedX * gameSpeed;
if (type === 'Fish') {
// Move horizontally
self.x += self.speedX;
// Generate underwater splash effects
if (!self.isJumping && Date.now() - self.lastSplashTime >= 200) {
// Generate every 200ms
var numSplashes = Math.floor(Math.random() * 2) + 1; // 1-2 splashes
for (var i = 0; i < numSplashes; i++) {
var splash = ObjectPool.get('Splash', Splash);
splash.x = self.x + (Math.random() * graphics.width - graphics.width / 2); // Random position along fish body
splash.y = self.y;
splash.tint = 0x4444ff; // Match fish's underwater tint
game.addChildAt(splash, game.getChildIndex(self) - 1);
}
self.lastSplashTime = Date.now();
}
// Check if crossing under Coot
if (self.x <= coot.x && !self.isJumping) {
self.speedX = 0;
graphics.alpha = 0;
self.pauseTime += 0.02;
// Add this check for when to reappear
if (self.pauseTime >= 1 && !self.isJumping) {
LK.getSound('fishsplash').play();
graphics.alpha = 1;
graphics.rotation = Math.PI / 2;
self.isJumping = true;
self.jumpStartY = self.y;
// Create splash burst when starting jump
var burstSplashes = Math.floor(Math.random() * 4) + 6;
for (var i = 0; i < burstSplashes; i++) {
var splash = ObjectPool.get('Splash', Splash);
splash.x = self.x + (Math.random() * graphics.width - graphics.width / 2);
splash.y = self.jumpStartY - Math.random() * 200;
game.addChildAt(splash, game.getChildIndex(self) - 1);
}
// Create jump tween
// Change the tint directly at specific points instead of in onUpdate
// Create jump tween with faster duration
tween(self, {
y: self.jumpStartY - 864 // Jump height
}, {
duration: 350,
// 0.4 seconds up
easing: tween.sineOut,
onFinish: function onFinish() {
// Create fall tween with faster duration
tween(self, {
y: self.jumpStartY + 50 // Go slightly below starting point
}, {
duration: 300,
// 0.4 seconds down
easing: tween.sineIn,
onFinish: function onFinish() {
Obstacle.lastDestroyTime = Date.now();
currentObstacle = null;
self.destroy();
}
});
}
});
}
}
if (self.isJumping) {
// Hardcoded tint change based on absolute position
var jumpProgress = Math.abs(self.y - self.jumpStartY) / 864;
if (self.y < self.jumpStartY - 100) {
graphics.tint = 0xffffff;
graphics.alpha = 1.0;
} else {
graphics.tint = 0x4444ff;
graphics.alpha = 0.7;
}
}
} else if (type === 'Duck') {
// Add sinusoidal wave pattern travel for Duck
if (type === 'Duck') {
self.y += Math.sin(self.x * 0.00625) * 2.7; // Adjusted sinusoidal wave pattern with further reduced frequency and increased amplitude
}
if (type === 'Duck') {
// Play duck sound every 2 seconds
if (LK.ticks % 100 == 0) {
// 120 frames = 2 seconds at 60 FPS
LK.getSound('duck').play();
}
}
// Ripple spawn logic
if (Date.now() - self.lastRippleTime >= 500) {
var ripple = ObjectPool.get('Ripple', Ripple);
ripple.x = self.x;
ripple.y = self.y + graphics.height / 2;
game.addChildAt(ripple, game.getChildIndex(self) - 1);
self.lastRippleTime = Date.now();
}
// Existing destroy logic
if (self.x <= -self.width / 2) {
self.destroy();
Obstacle.lastDestroyTime = Date.now();
console.log("Obstacle destroyed at " + Obstacle.lastDestroyTime);
}
} else if (type === 'Owl') {
if (coot) {
if (!self.isDiving && self.x > coot.x) {
self.x += self.speedX * gameSpeed; // Move Owl left until it reaches Coot's X position
} else {
if (!self.hasDived) {
self.isDiving = true; // Set isDiving state
if (coot.intersects(collisionBlock)) {
self.hasDived = true; // Set hasDived state only on intersection with Coot
}
self.speedX = 0; // Stop left movement
if (!coot.isDiving) {
self.speedY = (coot.y - self.y) / 40; // Adjust speed to track Coot's Y position at half speed
self.y += self.speedY * gameSpeed; // Move Owl towards Coot
self.x += (coot.x - self.x) / 20 * gameSpeed; // Move Owl towards Coot's X position
}
if (self.y >= coot.originalY) {
self.isDiving = false; // Remove isDiving state
self.speedX = -6; // Set speed to fly away to the left
self.speedY = -3; // Add upward movement
} else if (self.isDiving) {
var shadowClone = ObjectPool.get('ShadowClone', ShadowClone);
shadowClone.x = self.x;
shadowClone.y = self.y;
game.addChild(shadowClone);
}
}
}
// Check if Coot's state requires Owl to fly off
if (self.isDiving && (coot.isDiving || coot.isReturning || coot.returnDelay > 0 || coot.intersects(collisionBlock))) {
self.isDiving = false; // Remove isDiving state
self.speedX = -6; // Set speed to fly away to the left
self.speedY = -3; // Add upward movement
self.x += self.speedX * gameSpeed;
self.y += self.speedY * gameSpeed; // Move Owl upwards as it flies away
if (self.x < -self.width / 2) {
self.destroy(); // Remove Owl from the game when off-screen
}
}
}
}
collisionBlock.x = 0;
collisionBlock.y = 0;
};
});
var OwlWarningIcon = Container.expand(function () {
var self = Container.call(this);
var iconGraphics = self.attachAsset('OwlIcon', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.75
});
self.flashInterval = 0;
self.update = function () {
self.flashInterval++;
if (coot.isWarningActive) {
if (self.flashInterval % 30 < 15) {
iconGraphics.alpha = 0.75;
} else {
iconGraphics.alpha = 0.5;
}
} else {
iconGraphics.alpha = 0;
}
};
});
var PhoenixFeatherIcon = Container.expand(function () {
var self = Container.call(this);
var iconGraphics = self.attachAsset('PhoenixFeatherIcon', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2 // Start dim, not opaque
});
self.update = function () {
iconGraphics.alpha = coot && coot.hasPhoenixFeather ? 1 : 0.2; // Set alpha based on possession
};
});
var PlayButton = Container.expand(function () {
var self = Container.call(this);
var playButtonGraphics = self.attachAsset('PlayButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.down = function () {
// Exit title screen
LK.getSound('powerup').play(); // Play coothurt sound
LK.effects.flashScreen(0xFFFFFF, 300); // Flash screen red for 100ms
LK.setTimeout(function () {
// Slide out all elements
isTitleScreen = false;
var slideOutInterval = LK.setInterval(function () {
gameLogo.x += 50;
self.x += 50;
if (gameLogo.instructionsButton) {
gameLogo.instructionsButton.x += 50;
}
// Make sure to include the hat shop button here
if (gameLogo.hatShopButton) {
gameLogo.hatShopButton.x += 50;
}
if (gameLogo.x > 2048 + gameLogo.width / 2) {
LK.clearInterval(slideOutInterval);
self.destroy();
if (gameLogo.instructionsButton) {
gameLogo.instructionsButton.destroy();
}
if (gameLogo.hatShopButton) {
gameLogo.hatShopButton.destroy();
}
// Start game after animations
startGame();
}
}, 16);
}, 500);
};
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
var powerUpGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
// Initialize power-up properties
self.speedX = -5; // Speed of the power-up moving left
self.collected = false; // Flag to check if the power-up is collected
self.update = function () {
// Add pulsating effect
var pulseScale = 0.05 * Math.sin(LK.ticks * 0.1) + 1;
powerUpGraphics.scale.set(pulseScale);
// Add bobbing effect
self.y += Math.sin(LK.ticks * 0.05) * 0.5;
// Move the power-up left
self.x += self.speedX * gameSpeed;
// Check for collection by the Coot
if (coot && !self.collected && coot.intersects(self)) {
if (type === 'PhoenixFeather' && coot.hasPhoenixFeather || type === 'Shield' && coot.isShielded) {
return; // Do not collect if already active
}
self.collected = true;
activatePowerUp(type);
self.destroy();
}
// Destroy the power-up if it goes off screen
if (self.x < -powerUpGraphics.width / 2) {
self.destroy();
}
};
function activatePowerUp(type) {
LK.getSound('powerup').play(); // Play powerup sound effect when a power-up is collected
var powerUpConfig = {
'CoinMagnet': {
duration: 15000,
state: 'isMagnetActive'
},
'PowerDash': {
duration: 30000,
state: 'isPowerDashActive'
},
'Warning': {
duration: 30000,
state: 'isWarningActive'
},
'Shield': {
duration: 0,
// Shield is instant and doesn't have a duration
state: 'isShielded'
},
'PhoenixFeather': {
duration: 0,
// Phoenix Feather is instant and doesn't have a duration
state: 'hasPhoenixFeather'
}
};
var config = powerUpConfig[type];
var startTime = Date.now();
if (config) {
// Removed interval-based state management
if (type === 'Shield') {
coot.isShielded = true;
coot.shieldGraphics = coot.addChild(LK.getAsset('Shield', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: coot.width / 200,
// Scale to cover Coot
scaleY: coot.height / 204.2,
// Scale to cover Coot
alpha: 0.5 // Set opacity to 50%
}));
return; // Exit early as Shield doesn't need a timer
}
if (type === 'PhoenixFeather') {
coot.hasPhoenixFeather = true;
return; // Exit early as Phoenix Feather doesn't need a timer
}
coot[config.state] = true; // Ensure state is set to true
coot.powerUpTimers[type].endTime = Date.now() + config.duration; // Set power-up end time
var existingPowerUp = powerUpDisplay.activePowerUps.find(function (p) {
return p.type === type;
});
if (existingPowerUp) {
// Calculate remaining time in milliseconds
var timeRemaining = existingPowerUp.startTime + existingPowerUp.duration - Date.now();
if (timeRemaining > 0) {
config.duration += timeRemaining; // Add remaining time to new duration
startTime = Date.now(); // Reset start time to now for accurate duration
}
LK.clearInterval(existingPowerUp.interval);
existingPowerUp.startTime = Date.now(); // Update startTime to reflect new total duration
existingPowerUp.duration = config.duration; // Update duration
existingPowerUp.interval = createInterval(config, existingPowerUp.startTime);
existingPowerUp.timer = config.duration / 1000; // Update visual timer
return; // Exit early as we have updated the existing power-up
}
powerUpDisplay.addPowerUp(type, config.duration / 1000, startTime); // Include startTime for accurate remaining time
}
}
});
var PowerUpDisplay = Container.expand(function () {
var self = Container.call(this);
self.x = 2048 / 2; // Center horizontally
self.y = 2732 * 0.9; // Position at bottom center
self.activePowerUps = [];
self.update = function () {
// Update each power-up display
self.activePowerUps.forEach(function (powerUp, index) {
powerUp.timer -= (Date.now() - powerUp.lastUpdateTime) / 1000; // Decrease timer based on real time
powerUp.lastUpdateTime = Date.now(); // Update the last update time
powerUp.text.setText(Math.max(0, Math.ceil(powerUp.timer)) + 's'); // Ensure timer doesn't show negative values
// Remove power-up if timer reaches 0
if (powerUp.timer <= 0) {
powerUp.icon.destroy();
powerUp.text.destroy();
self.activePowerUps.splice(index, 1);
self.repositionPowerUps();
}
});
};
self.addPowerUp = function (type, duration) {
var existingPowerUp = self.activePowerUps.find(function (p) {
return p.type === type;
});
if (existingPowerUp) {
existingPowerUp.timer += duration;
existingPowerUp.lastUpdateTime = Date.now();
existingPowerUp.text.setText(Math.max(0, Math.ceil(existingPowerUp.timer)) + 's'); // Update visual timer
return;
}
var iconAssetMap = {
'CoinMagnet': 'CoinMagnet',
'PowerDash': 'PowerDash',
'Warning': 'Warning',
'Shield': 'Shield',
'PhoenixFeather': 'PhoenixFeather'
};
if (iconAssetMap[type]) {
var icon = LK.getAsset(iconAssetMap[type], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0
});
} else {
console.error("Power-up type not found in iconAssetMap: ", type);
return;
}
var text = new Text2(duration + 's', {
size: 100,
fill: 0xFFFFFF
});
text.anchor.set(0.5, 0.5);
self.addChild(icon);
self.addChild(text);
self.activePowerUps.push({
icon: icon,
text: text,
timer: duration,
type: type,
lastUpdateTime: Date.now()
});
self.repositionPowerUps();
};
self.repositionPowerUps = function () {
var totalWidth = self.activePowerUps.reduce(function (acc, powerUp) {
return acc + powerUp.icon.width + powerUp.text.width + 40; // 40 is the new spacing
}, 0);
var startX = -totalWidth / 2;
self.activePowerUps.forEach(function (powerUp) {
powerUp.icon.x = startX + powerUp.icon.width / 2;
powerUp.text.x = powerUp.icon.x + powerUp.icon.width / 2 + 40; // 40 is the new spacing between icon and text
startX += powerUp.icon.width + powerUp.text.width + 20;
});
};
});
var Ripple = Container.expand(function () {
var self = Container.call(this);
var rippleGraphics;
self.initialScale = 0.05;
self.growthRate = 0.02;
self.fadeRate = 0.01;
self.maxScale = 2.0;
self.init = function () {
if (!rippleGraphics) {
rippleGraphics = self.attachAsset('Ripple', {
anchorX: 0.5,
anchorY: 0.5
});
}
rippleGraphics.scaleX = self.initialScale;
rippleGraphics.scaleY = self.initialScale;
rippleGraphics.alpha = 1;
return self;
};
self.reset = function () {
rippleGraphics.scaleX = self.initialScale;
rippleGraphics.scaleY = self.initialScale;
rippleGraphics.alpha = 1;
return self;
};
self.destroy = function () {
if (self.parent) {
self.parent.removeChild(self);
}
ObjectPool.recycle('Ripple', self);
};
self.update = function () {
rippleGraphics.scaleX += self.growthRate;
rippleGraphics.scaleY += self.growthRate;
rippleGraphics.alpha -= self.fadeRate;
if (rippleGraphics.scaleX >= self.maxScale || rippleGraphics.alpha <= 0) {
self.destroy();
}
};
return self.init();
});
var ShadowClone = Container.expand(function () {
var self = Container.call(this);
var cloneGraphics;
self.fadeRate = 0.05;
self.init = function () {
if (!cloneGraphics) {
cloneGraphics = self.attachAsset('Owl', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
} else {
cloneGraphics.alpha = 0.5;
}
return self;
};
self.reset = function () {
if (cloneGraphics) {
cloneGraphics.alpha = 0.5;
}
return self;
};
self.destroy = function () {
if (self.parent) {
self.parent.removeChild(self);
}
ObjectPool.recycle('ShadowClone', self);
};
self.update = function () {
cloneGraphics.alpha -= self.fadeRate;
if (cloneGraphics.alpha <= 0) {
self.destroy();
}
};
return self.init();
});
// Static respawn delay property
// Class for the water splash particles
var Splash = Container.expand(function () {
var self = Container.call(this);
var splashGraphics;
var scale;
// Initialize variables but don't create graphics
self.speedX = 0;
self.speedY = 0;
self.gravity = 0.1;
self.init = function () {
// Only create graphics if they don't exist
if (!splashGraphics) {
scale = Math.random() * 0.75 + 0.25;
splashGraphics = self.attachAsset('Watersplash', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale,
scaleY: scale
});
}
// Set initial properties
self.speedX = Math.random() * 2 - 1;
self.speedY = -Math.random() * 5;
splashGraphics.alpha = 1;
return self;
};
self.reset = function () {
// Reset properties without recreating the graphics
self.speedX = Math.random() * 2 - 1;
self.speedY = -Math.random() * 5;
if (splashGraphics) {
splashGraphics.alpha = 1;
splashGraphics.rotation = 0;
}
return self;
};
self.cleanup = function () {
// Clear any references to other objects
self.coin = null;
};
self.destroy = function () {
// Instead of destroying, return to pool
self.parent.removeChild(self);
ObjectPool.recycle('Splash', self);
};
self.update = function () {
self.x += self.speedX * gameSpeed;
self.y += self.speedY * gameSpeed;
self.speedY += self.gravity;
// Add rotation based on the speed of the particle
self.rotation += self.speedX / 10;
if (self.y > 2732) {
// If the particle is below the screen
self.destroy(); // This now recycles instead of destroying
}
};
return self.init(); // Initialize on creation
});
var Sun = Container.expand(function () {
var self = Container.call(this);
var sunGraphics = self.attachAsset('Sun', {
anchorX: 0.5,
anchorY: 0.5
});
// Initialize sun properties
self.startX = -sunGraphics.width / 2; // Start off-screen to the left
self.startY = 2732 * 0.25; // 25% from the top
self.endX = 2048 + sunGraphics.width / 2; // End off-screen to the right
self.arcHeight = 2732 * 0.1; // Arc height
self.duration = 2 * 60 * 60; // 2 minutes in ticks (assuming 60 FPS)
self.ticks = 0;
// For the Sun class, replace the update function with:
self.update = function () {
if (!self.tweenStarted) {
self.tweenStarted = true;
// Create path tween for the arc motion
tween(self, {
x: self.endX,
y: self.startY - self.arcHeight
}, {
duration: 2 * 60 * 1000,
// 2 minutes in milliseconds
easing: tween.sineInOut,
onFinish: function onFinish() {
self.destroy();
isDay = false;
isNight = true;
// Start spawning fireflies
fireflySpawnInterval = LK.setInterval(function () {
var newFirefly = new Firefly();
newFirefly.x = Math.random() * 2048;
newFirefly.y = Math.random() * 2732;
game.addChild(newFirefly);
}, 1000);
game.addChild(new Moon());
}
});
}
};
});
// Class for the water
var Water = Container.expand(function () {
var self = Container.call(this);
var waterGraphics = self.attachAsset('Water', {
anchorX: 0.5,
anchorY: 0.7
});
self.speed = 2;
self.update = function () {
self.x -= self.speed * gameSpeed;
// self.y = midgroundtrees1.y + midgroundtrees1.height / 2;
if (self.x <= -self.width / 2) {
self.x += self.width * 2;
}
};
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function savePurchasedHats(hatsArray) {
if (Array.isArray(hatsArray)) {
storage.purchasedHats = hatsArray.join(",");
}
}
function getPurchasedHats() {
if (!storage.purchasedHats) {
return [];
}
return storage.purchasedHats.split ? storage.purchasedHats.split(",") : storage.purchasedHats;
}
var cootGraphics = {
width: 550,
height: 423.5
}; // Define cootGraphics with default dimensions
var availableHats = [{
id: 'TopHat',
name: 'Top Hat',
price: 100,
x: cootGraphics.width / 2.65,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.7,
rotation: -0.35
}, {
id: 'Cowboy',
name: 'Cowboy Hat',
price: 200,
x: cootGraphics.width / 2.4,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.6,
rotation: -0.35
}, {
id: 'Crown',
name: 'Royal Crown',
price: 800,
x: cootGraphics.width / 2.65,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.7,
rotation: -0.35
}, {
id: 'Beanie',
name: 'Toque',
price: 200,
x: cootGraphics.width / 2.65,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.7,
rotation: -0.35
}, {
id: 'Captain',
name: 'Captain Hat',
price: 500,
x: cootGraphics.width / 2.65,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.7,
rotation: -0.35
}, {
id: 'Sockhat',
name: 'Cap of Legend',
price: 700,
x: cootGraphics.width / 3,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.4,
rotation: 0.25
}, {
id: 'Propeller',
name: 'Propeller Cap',
price: 1000,
x: cootGraphics.width / 2.65,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.7,
rotation: -0.35
}, {
id: 'Sombrero',
name: 'Sombrero',
price: 300,
x: cootGraphics.width / 2.65,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.7,
rotation: -0.35
}, {
id: 'Knight',
name: 'Knight Helmet',
price: 500,
x: cootGraphics.width / 2.8,
y: -cootGraphics.height / 3 + cootGraphics.height / 1.95,
rotation: 0.27
}, {
id: 'Viking',
name: 'Viking Helmet',
price: 600,
x: cootGraphics.width / 2.7,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.6,
rotation: -0.35
}, {
id: 'Astronaut',
name: 'Astronaut Helmet',
price: 600,
x: cootGraphics.width / 2.95,
y: -cootGraphics.height / 3 + cootGraphics.height / 2,
rotation: 0.27
}, {
id: 'Fedora',
name: 'Fedora',
price: 100,
x: cootGraphics.width / 2.6,
y: -cootGraphics.height / 3 + cootGraphics.height / 2.7,
rotation: -0.15
}];
availableHats.sort(function (a, b) {
return a.price - b.price;
});
function showHatShop() {
var menuBackground = LK.getAsset('MenuBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(menuBackground);
var hatShopScreen = new HatShopScreen();
game.addChild(hatShopScreen);
hatShopScreen.menuBackground = menuBackground;
}
function updateBackgroundFade() {
// Check for day to night transition (Sun)
if (isDay && !isNight) {
game.children.forEach(function (child) {
if (child instanceof Sun) {
// Use x position to determine fade progress
// When sun is halfway across screen, start fading
if (child.x > 2048 * 0.85) {
var fadeProgress = (child.x - 2048 * 0.85) / (2048 * 0.25);
nightBackground.alpha = Math.min(fadeProgress, 1);
}
}
});
}
// Check for night to day transition (Moon)
if (isNight && !isDay) {
game.children.forEach(function (child) {
if (child instanceof Moon) {
// Use x position to determine fade progress
// When moon is halfway across screen, start fading out
if (child.x > 2048 * 0.85) {
var fadeProgress = (child.x - 2048 * 0.85) / (2048 * 0.25);
nightBackground.alpha = Math.max(1 - fadeProgress, 0);
}
}
});
}
}
// Define createInterval function to fix ReferenceError
var ObjectPool = {
pools: {},
getPool: function getPool(type) {
if (!this.pools[type]) {
this.pools[type] = [];
}
return this.pools[type];
},
get: function get(type, Constructor, initParams) {
var pool = this.getPool(type);
var obj;
if (pool.length > 0) {
obj = pool.pop();
if (typeof obj.reset === 'function') {
obj.reset(initParams);
}
return obj;
} else {
return new Constructor(initParams);
}
},
recycle: function recycle(type, object) {
// Don't store null or undefined objects
if (!object) {
return;
}
var pool = this.getPool(type);
// Limit pool size to prevent memory issues
if (pool.length < 50) {
// Clear any references that might cause memory leaks
if (typeof object.cleanup === 'function') {
object.cleanup();
}
pool.push(object);
}
}
};
function createInterval(config, startTime) {
return LK.setInterval(function () {
var timeRemaining = config.duration - (Date.now() - startTime);
if (timeRemaining <= 0) {
LK.clearInterval(this);
}
}, 1000);
}
function showInstructions() {
var menuBackground = LK.getAsset('MenuBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(menuBackground);
var instructionsScreen = new InstructionsScreen();
game.addChild(instructionsScreen);
instructionsScreen.menuBackground = menuBackground; // Store reference for later removal
}
function startGame() {
// Fade in background music
LK.playMusic('backgroundmusic', {
fade: {
start: 0,
end: 0.5,
duration: 1500
}
});
gameActive = true;
Obstacle.lastDestroyTime = Date.now();
// Create coot but start off-screen
coot = new Coot();
coot.x = -400; // Start off-screen left
coot.y = 2732 * 0.75 - 25;
game.addChild(coot); // Add coot normally
coot.updateHat();
game.setChildIndex(foreground1, game.children.length - 1);
game.setChildIndex(foreground2, game.children.length - 1);
// Initialize other game elements
if (powerUpDisplay && powerUpDisplay.parent) {
game.setChildIndex(powerUpDisplay, game.children.length - 1);
}
coot.phoenixFeatherIcon = phoenixFeatherIcon;
heartIcons.forEach(function (icon, index) {
icon.alpha = index < coot.lives ? 1 : 0.2;
});
dayCountDisplay = new DayCountDisplay();
LK.gui.top.addChild(dayCountDisplay);
dayCountDisplay.y = 2732 * 0.25;
sun = game.addChild(new Sun());
sun.x = sun.startX;
sun.y = sun.startY;
// Play whistle and start game sequence
LK.getSound('whistle').play();
LK.setTimeout(function () {
gameSpeed = 1.5;
// Animate coot running in
tween(coot, {
x: 2048 * 0.20
}, {
duration: 1000,
easing: tween.quadOut,
onFinish: function onFinish() {
// Store final positions after coot reaches destination
coot.originalX = coot.x;
coot.originalY = coot.y;
coot.y = coot.y || 0; // Initialize coot.y if undefined
coot.originalY = coot.originalY || 0; // Initialize coot.originalY if undefined
}
});
}, 1000); // Wait for whistle sound to finish
}
function handleShieldHit() {
// Remove shield asset
if (coot.shieldGraphics) {
coot.shieldGraphics.destroy();
coot.shieldGraphics = null;
}
coot.isShielded = false;
coot.isInvincible = true; // Set Coot to invincible state
LK.getSound('coothurt').play(); // Play coothurt sound effect when the shield is removed
// Flash effect for invincibility
var flashInterval = LK.setInterval(function () {
coot.children[0].alpha = coot.children[0].alpha === 1 ? 0.5 : 1;
}, 100);
LK.setTimeout(function () {
LK.clearInterval(flashInterval);
coot.children[0].alpha = 1;
coot.isInvincible = false;
}, 1500);
}
// Define a single obstacleTypes array as a constant
var obstacleTypes = ['Duck', 'Fish', 'Owl'];
var obstacleSpawnChances = {
Duck: 1,
Fish: 1,
Owl: 1
};
Obstacle.getRandomDelay = function (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
};
Obstacle.spawnDelay = 6000; // Further reduce default spawn delay to 6 seconds
Obstacle.respawnDelay = {
Duck: Obstacle.getRandomDelay(2250, 3750),
// Reduce Duck respawn delay by 25%
Fish: Obstacle.getRandomDelay(2250, 3750),
// Reduce Fish respawn delay by 25%
Owl: Obstacle.getRandomDelay(2250, 3750) // Add Owl respawn delay
};
Obstacle.lastDestroyTime = Date.now() - 5000; // Initialize to ensure first spawn check passes
var background = game.addChild(LK.getAsset('Background', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2048 / 2500,
scaleY: 2732 / 2500
}));
var nightBackground = game.addChild(LK.getAsset('NightBackground', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 2048 / 2500,
scaleY: 2732 / 2500,
alpha: 0
}));
var DayCount = 1; // Global variable to keep track of the number of days, initialized to 1 at game start
// Initialize the sun after title screen
var sun;
var isDay = true; // Initialize day state
var isNight = false; // Initialize night state
// Initialize the midgroundtrees
var midgroundtrees1 = game.addChild(new Midgroundtrees());
midgroundtrees1.x = 2048 / 2;
midgroundtrees1.y = 2732 * 0.7;
var midgroundtrees2 = game.addChild(new Midgroundtrees());
midgroundtrees2.x = 2048 / 2 + midgroundtrees2.width;
midgroundtrees2.y = 2732 * 0.7;
// Initialize the water
var water1 = game.addChild(new Water());
water1.x = 2048 / 2;
water1.y = midgroundtrees1.y + midgroundtrees1.height / 2;
var water2 = game.addChild(new Water());
water2.x = 2048 / 2 + water2.width;
water2.y = midgroundtrees2.y + midgroundtrees2.height / 2;
// Initialize game logo
var gameLogo = LK.getAsset('GameIcon', {
anchorX: 0.5,
anchorY: 0.5,
x: -100,
// Start off-screen to the left
y: 2732 * 0.4 // Move up by 10% of the screen height
});
game.addChild(gameLogo);
// Initialize game variables
var coot;
var dragStartY = null; // Initialize dragStartY to null
var dragStartX = null; // Initialize dragStartX to null
var gameSpeed = 0; // Global variable for game speed, set to 0 during title screen
var isTitleScreen = true; // Flag to indicate if the game is in the title screen state
var gameActive = false; // Flag to indicate if the game is actively being played
var speedIncreaseInterval = 30000; // 30 seconds in milliseconds
var maxGameSpeed = 5.0; // Maximum game speed
var lastSpeedIncreaseTime = Date.now(); // Track the last time speed was increased
var score = 0;
var coinCount = storage.coins || 0; // Initialize coinCount from storage
var coinCounter = new Text2(Math.min(coinCount, 999).toString(), {
size: 150,
fill: 0xFFFFFF
});
coinCounter.anchor.set(1, 0); // Anchor to the top right corner
var currentObstacle = null;
var warningIcon = null;
// Initialize the foreground
var foreground1 = new Foreground();
var foreground2 = new Foreground();
// Removed duplicate addChild calls for foregrounds
game.addChild(foreground1);
game.addChild(foreground2);
foreground1.x = 2048 / 2;
foreground1.y = 2732 * 0.9;
foreground2.x = foreground1.x + foreground1.width;
foreground2.y = 2732 * 0.9;
foreground1.isFirst = true;
foreground1.other = foreground2;
foreground2.other = foreground1;
// Validate positioning
if (Math.abs(foreground2.x - (foreground1.x + foreground1.width)) > 1) {
console.error("Foreground pieces are not correctly positioned!");
}
// Add coin counter display
var coinCounter = new Text2('0', {
size: 150,
// Increased size for better visibility
fill: 0xFFFFFF
});
coinCounter.anchor.set(1, 0); // Anchor to the top right corner
// Add coin asset beside the coin counter
var coinIcon = LK.getAsset('Coin', {
anchorX: 1,
anchorY: 0,
x: -coinCounter.width - 10,
// Position to the left of the counter with some padding
y: 0
});
LK.gui.topRight.addChild(coinIcon);
LK.gui.topRight.addChild(coinCounter);
// Add heart icons to represent Coot's lives
var heartIcons = [];
for (var i = 0; i < 3; i++) {
// Always create 3 heart icons
var heartIcon = LK.getAsset('HeartLife', {
anchorX: 0,
anchorY: 0,
x: -coinCounter.width - 10 - i * 110,
y: coinIcon.height + 10
});
LK.gui.topRight.addChild(heartIcon);
heartIcons.push(heartIcon);
}
// Update heart icons based on remaining lives
heartIcons.forEach(function (icon, index) {
icon.alpha = coot && index < coot.lives ? 1 : 0.2; // Dim the heart if life is lost
});
// Initialize PowerUpDisplay after foregrounds are set up
var powerUpDisplay = new PowerUpDisplay();
game.addChild(powerUpDisplay);
// Initialize Phoenix Feather icon at the start of the game
var phoenixFeatherIcon = new PhoenixFeatherIcon();
// coot.phoenixFeatherIcon will be set after coot initialization
phoenixFeatherIcon.x = heartIcons[0].x + heartIcons[0].width / 2 - phoenixFeatherIcon.width / 2; // Center below the leftmost heart icon
phoenixFeatherIcon.y = heartIcons[0].y + heartIcons[0].height + 70; // Increase padding below the heart icon
LK.gui.topRight.addChild(phoenixFeatherIcon);
var dayCountDisplay;
game.update = function () {
if (DayCount > 1 && !dayCountDisplay.visible) {
dayCountDisplay.show();
}
updateBackgroundFade();
if (isTitleScreen) {
// Update coinCounter text on the title screen
coinCounter.setText(Math.min(coinCount, 9999).toString());
coinIcon.x = -coinCounter.width - 10; // Update coin icon position to align with the coin counter
// Only update background, midground trees, water, and foreground during title screen
[midgroundtrees1, midgroundtrees2, water1, water2, foreground1, foreground2].forEach(function (element) {
element.update();
});
// Animate game logo sliding in from the left
if (gameLogo.x < 2048 / 2) {
// Move logo
gameLogo.x += 30; // Slide in quickly
// Move buttons at the same time
if (gameLogo.playButton) {
// Calculate how far along the animation we are (0-1)
var progress = (gameLogo.x + 100) / (2048 / 2 + 100);
// Position playButton in the center
gameLogo.playButton.x = -100 + progress * (2048 / 2 + 100);
// Position other buttons relative to playButton
gameLogo.instructionsButton.x = gameLogo.playButton.x - 320;
gameLogo.hatShopButton.x = gameLogo.playButton.x + 350;
}
} else {
// Ensure everything is at final position
gameLogo.x = 2048 / 2;
if (gameLogo.playButton) {
gameLogo.playButton.x = 2048 / 2;
gameLogo.instructionsButton.x = 2048 / 2 - 320;
gameLogo.hatShopButton.x = 2048 / 2 + 350;
}
}
if (!gameLogo.playButton && !gameLogo.instructionsButton && !gameLogo.hatShopButton) {
var playButton = new PlayButton();
playButton.x = -100; // Start off-screen to the left, like the logo
playButton.y = gameLogo.y + gameLogo.height / 2 + 2732 * 0.06;
game.addChild(playButton);
gameLogo.playButton = playButton;
var instructionsButton = new InstructionsButton();
instructionsButton.x = -100; // Start off-screen to the left
instructionsButton.y = playButton.y + playButton.height;
game.addChild(instructionsButton);
gameLogo.instructionsButton = instructionsButton;
var hatShopButton = new HatShopButton();
hatShopButton.x = -100; // Start off-screen to the left
hatShopButton.y = playButton.y + playButton.height;
game.addChild(hatShopButton);
gameLogo.hatShopButton = hatShopButton;
}
return; // Exit update function to prevent further updates
}
if (coot) {
coot.update();
}
phoenixFeatherIcon.update();
// Increase game speed every 20 seconds
if (!isTitleScreen && Date.now() - lastSpeedIncreaseTime >= speedIncreaseInterval) {
if (gameSpeed < maxGameSpeed) {
gameSpeed = Math.min(gameSpeed + 0.1, maxGameSpeed);
// Decrease respawn delay for all obstacles
Obstacle.respawnDelay.Eagle = Math.max(Obstacle.respawnDelay.Eagle - 500, 1000);
Obstacle.respawnDelay.Duck = Math.max(Obstacle.respawnDelay.Duck - 500, 1000);
Obstacle.respawnDelay.Fish = Math.max(Obstacle.respawnDelay.Fish - 500, 1000);
Obstacle.respawnDelay.Owl = Math.max(Obstacle.respawnDelay.Owl - 500, 1000);
}
lastSpeedIncreaseTime = Date.now();
}
// Define createInterval function to fix ReferenceError
// Removed the timer counter increment and display logic
// Check for collision between the Coot and the Collision Block of the current obstacle
if (currentObstacle && coot.intersects(currentObstacle.children[0])) {
if (coot.isShielded) {
handleShieldHit();
} else if (coot.isInvincible) {
// Do nothing, invincible state
} else {
coot.lives -= 1;
LK.getSound('coothurt').play(); // Play coothurt sound when Coot gets hit
if (coot.lives <= 0 && coot.hasPhoenixFeather) {
LK.getSound('phoenix').play(); // Play phoenix sound effect when lives are refilled
coot.hasPhoenixFeather = false; // Remove Phoenix Feather
phoenixFeatherIcon.update(); // Update Phoenix Feather icon
coot.lives = 3; // Refill all lives
heartIcons.forEach(function (icon) {
icon.alpha = 1; // Restore all heart icons
});
// Add Phoenix effect
coot.isInvincible = true; // Set Coot to invincible state
LK.effects.flashScreen(0xff0000, 1000); // Flash screen red for 1 second
var phoenix = LK.getAsset('Phoenix', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5 // Set initial opacity to 50%
});
phoenix.x = 2048 / 2;
phoenix.y = 2732 / 2;
game.addChild(phoenix);
LK.setTimeout(function () {
phoenix.destroy(); // Destroy the Phoenix asset
coot.isInvincible = false; // Remove invincibility after effect
}, 1500); // Wait for 1.5 seconds
} else if (coot.lives <= 0) {
coot.isFalling = true;
coot.fallStartY = coot.y;
coot.fallRotation = 0;
coot.fallSpeed = 5;
heartIcons[0].alpha = 0.2; // Dim the last heart icon to indicate all lives lost
} else {
coot.isInvincible = true;
heartIcons[coot.lives].alpha = 0.2; // Dim the heart icon to indicate a lost life
// Flash effect for invincibility
LK.effects.flashScreen(0xff0000, 100); // Flash screen red for 100ms
var flashInterval = LK.setInterval(function () {
coot.children[0].alpha = coot.children[0].alpha === 1 ? 0.5 : 1;
}, 100);
LK.setTimeout(function () {
LK.clearInterval(flashInterval);
coot.children[0].alpha = 1;
coot.isInvincible = false;
}, 1500);
}
}
}
// Update PowerUpDisplay
powerUpDisplay.update();
// Update the midgroundtrees
midgroundtrees1.update();
midgroundtrees2.update();
// Update the water
water1.update();
water2.update();
// Update the current obstacle
// Check if the current obstacle needs destruction
if (currentObstacle && currentObstacle.x < -currentObstacle.width / 2) {
currentObstacle.destroy();
Obstacle.lastDestroyTime = Date.now();
currentObstacle = null;
}
// Check if it's time to spawn a new obstacle
if (gameActive && !currentObstacle) {
var timeSinceLastDestroy = Date.now() - Obstacle.lastDestroyTime;
if (timeSinceLastDestroy >= Obstacle.spawnDelay - 1000 && !warningIcon) {
if (coot.isWarningActive) {
LK.getSound('alert').play(); // Play alert sound when warning icon is displayed and player has the warning power up
}
var totalChance = obstacleSpawnChances.Duck + obstacleSpawnChances.Fish;
if (isNight) {
totalChance += obstacleSpawnChances.Owl;
}
var randomChance = Math.random() * totalChance;
var cumulativeChance = 0;
for (var i = 0; i < obstacleTypes.length; i++) {
if (obstacleTypes[i] === 'Eagle' && isDay || obstacleTypes[i] === 'Owl' && isNight || obstacleTypes[i] !== 'Eagle' && obstacleTypes[i] !== 'Owl') {
cumulativeChance += obstacleSpawnChances[obstacleTypes[i]];
if (randomChance < cumulativeChance) {
currentObstacleType = obstacleTypes[i];
obstacleSpawnChances[currentObstacleType] = 1; // Reset chance on spawn
break;
}
}
}
var warningIconClassMap = {
'Duck': DuckWarningIcon,
'Eagle': EagleWarningIcon,
'Fish': FishWarningIcon,
'Owl': OwlWarningIcon // Add OwlIcon to warning icons
};
warningIcon = game.addChild(new warningIconClassMap[currentObstacleType]());
warningIcon.x = 2048 / 2;
warningIcon.y = 2732 / 2;
}
if (timeSinceLastDestroy >= Obstacle.spawnDelay) {
if (warningIcon) {
warningIcon.destroy();
warningIcon = null;
}
currentObstacle = game.addChild(new Obstacle(currentObstacleType));
currentObstacle.x = 2048 + currentObstacle.width / 2; // Ensure it starts off-screen
currentObstacle.y = currentObstacleType === 'Duck' ? 2732 * 0.80 : currentObstacleType === 'Eagle' ? 2732 * 0.25 : currentObstacleType === 'Fish' ? 2732 * 0.85 : 2732 * 0.10; // Owl positioned at 10% from the top
// Increase spawn chance for obstacles not selected
obstacleTypes.forEach(function (type) {
if (type !== currentObstacleType) {
obstacleSpawnChances[type] += 0.5; // Increase chance by 0.5 for each non-selected obstacle
}
});
}
}
if (currentObstacle) {
currentObstacle.update();
}
// Check for collision between the Coot and Coins
game.children.forEach(function (child) {
if (child instanceof Coin && coot && coot.intersects(child) && !child.collecting) {
LK.getSound('coin').play(); // Play coin sound effect when coot touches the coin
child.collecting = true;
child.targetX = 2048 + child.children[0].width; // Target off-screen to the right
child.targetY = -child.children[0].height; // Target off-screen to the top
}
});
// Update the foreground
foreground1.update();
foreground2.update();
// Spawn butterflies randomly
if (isDay && LK.ticks % 720 == 0) {
// Every 3 seconds at 60 FPS
var newButterfly = ObjectPool.get('Butterfly', Butterfly);
newButterfly.x = 2048 + newButterfly.width / 2; // Start off-screen to the right
newButterfly.y = Math.random() * (2732 * 0.6) + 2732 * 0.2; // Random Y position within midground tree layer
game.addChild(newButterfly);
}
// Calculate spawn interval based on coin magnet state
var baseSpawnRate = 120; // Normal 2-second spawn rate
var magnetSpawnRate = 30; // 25% faster (120 * 0.75 = 90)
var currentSpawnRate = coot && coot.hasCoinMagnet ? magnetSpawnRate : baseSpawnRate;
if (LK.ticks % currentSpawnRate == 0) {
var newCoin = new Coin();
newCoin.x = 2048 + newCoin.width / 2;
newCoin.y = Math.random() * (2732 * 0.45) + 2732 * 0.35;
game.addChild(newCoin);
}
// Spawn power ups randomly
if (LK.ticks % 1080 == 0) {
// Every 3 seconds at 60 FPS
var powerUpTypes = [{
type: 'Shield',
weight: 20
}, {
type: 'PowerDash',
weight: 25
}, {
type: 'CoinMagnet',
weight: 25
}, {
type: 'Warning',
weight: 20
}, {
type: 'PhoenixFeather',
weight: 10
}];
var totalWeight = powerUpTypes.reduce(function (acc, powerUp) {
return acc + powerUp.weight;
}, 0);
var randomWeight = Math.random() * totalWeight;
var cumulativeWeight = 0;
var selectedPowerUpType;
for (var i = 0; i < powerUpTypes.length; i++) {
cumulativeWeight += powerUpTypes[i].weight;
if (randomWeight < cumulativeWeight) {
selectedPowerUpType = powerUpTypes[i].type;
break;
}
}
// Check if Coot already has a shield or Phoenix feather
if (selectedPowerUpType === 'Shield' && coot.isShielded || selectedPowerUpType === 'PhoenixFeather' && coot.hasPhoenixFeather) {
// Skip spawning this power-up
return;
}
var newPowerup = new PowerUp(selectedPowerUpType);
newPowerup.x = 2048 + newPowerup.width / 2; // Start off-screen to the right
newPowerup.y = Math.random() * (2732 * 0.45) + 2732 * 0.35; // Random Y position between 35% and 80% of the screen height
game.addChild(newPowerup);
}
};
// Handle touch events for jumping and diving
game.down = function (x, y, obj) {
dragStartY = y; // Record the initial y position when touch starts
dragStartX = x; // Record the initial x position when touch starts
};
game.move = function (x, y, obj) {
if (!isTitleScreen && dragStartY !== null && dragStartX !== null) {
if (x - dragStartX > 50) {
// Only allow dash during these states if PowerDash is active
if (coot.isJumping || coot.isFalling || coot.isDiving || coot.isReturning || coot.returnDelay > 0) {
// Check for PowerDash before allowing
if (coot.isPowerDashActive) {
coot.isDashing = true;
}
} else {
// Normal dash on ground, no PowerDash needed
coot.isDashing = true;
}
dragStartX = null; // Reset dragStartX to prevent repeated dashes
}
if (y - dragStartY > 50 && coot && coot.originalY !== undefined && coot.y !== undefined && coot.y === coot.originalY) {
// Check if the drag is downward and significant
coot.dive();
dragStartY = null; // Reset dragStartY to prevent repeated dives
} else if (dragStartY - y > 50 && coot.y === coot.originalY) {
// Check if the drag is upward and significant
coot.jump();
dragStartY = null; // Reset dragStartY to prevent repeated jumps
}
}
};
game.up = function (x, y, obj) {
dragStartY = null; // Reset dragStartY when touch ends
dragStartX = null; // Reset dragStartX when touch ends
};
shining moon. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a single shining yellowish golden coin with the boat on it. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
a colorful, cartoon style boat with an orange and blue color scheme. the boat has a small flag on top, round windows and a curved hull , with the BOAT text on it with bold letters. the design is vibrant, playful and optimized for a mobile game. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
white water bubble. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
single rock. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
gold sparkle. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
single gold sparkle. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
red shining heart symbol. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
whale head in octogonal box with green background asset. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
orange life rings asset that revive from water. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
single rounded white bubble firefly trail. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
shining sun cartoon style. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
flying owl with blue gold color mix asset. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
coin magnet white blue red in color. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
warning asset. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows