/****
* 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