User prompt
It's an issue where the score was always 0 during game overs and make it so that the score of the current playing gets shown during the out-of-game game over screen instead and reset the score not upon death.
User prompt
reset the score again after the game over happened to show the correct score number during game overs
User prompt
Please make the map selection screen symmetrical.
User prompt
Make sure that the screen flash effect actually happens and that the thunder tree obstacle does not transform if the player jumps over it. Also, change the layout for the map selection so that every map ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make sure that everything flashes white for a short time. Well, as long as the sound for Thunder is playing, a lightning effect gets triggered. Also... Make sure... ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
that all obstacles appear off-screen, but never on-screen if the player can see them.
User prompt
that the thunder appears more often in the Thunder Plains map also make only the background elements flash but not the ground. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Add the Thunder Plains map to the map selection and change its layout so that still every map is clickable at the same size.
User prompt
thunder sound effect and make it play randomly when well playing the Thunder Plains map also when the thunder sound is playing make a lightning screen flash effect play but only slightly ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Yeah, we'll create all things needed for the map. Thunderplanes from ground, background, and trees for obstacles. Also, of course, sounds for landing and for the trees. Ambient music.
User prompt
re-evidence of the laboratory map completely, all of it.
User prompt
Name the class BreakSoundEffect to LaboratoryLandingSoundEffect and use it when landing in the laboratory map.
User prompt
Create a new map called Laboratory, and well, make the Laboratory map have as Obstacle Glass Pane. Create a breaking sound, well, a running into those sound effects, and of course a...
User prompt
Create a landing sound effect for the jungle temple map. Also, delete all evidence of the waterfall map, including its elements.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
musicEnabled: false,
ambienceEnabled: true
});
/****
* Classes
****/
var ArrowTrap = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
self.shootTimer = 0;
self.shootDirection = 1;
self.isShooting = false;
self.shootCooldown = 0;
// Create arrow trap graphics with standardized size
var trapGraphics = self.attachAsset('arrow_trap', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.0,
scaleY: 1.0
});
// Store graphics reference
self.trapGraphics = trapGraphics;
// Set standardized obstacle dimensions - 300x400 pixels
self.width = 300;
self.height = 400;
self.update = function () {
self.x += self.speed;
// Add menacing shooting animation
self.shootTimer += 0.07;
self.shootCooldown--;
// Trigger shooting animation periodically
if (self.shootCooldown <= 0 && Math.random() < 0.025) {
self.isShooting = true;
self.shootCooldown = 70; // 1.2 second cooldown at 60fps
}
if (self.isShooting) {
// Quick shooting motion
self.y += Math.sin(self.shootTimer * 5) * 2;
self.trapGraphics.rotation = Math.sin(self.shootTimer * 5) * 0.1;
self.trapGraphics.scaleX = 1.0 + Math.sin(self.shootTimer * 5) * 0.1;
self.trapGraphics.scaleY = 1.0 + Math.sin(self.shootTimer * 5) * 0.1;
// Stop shooting after a brief moment
if (self.shootTimer > Math.PI) {
self.isShooting = false;
self.shootTimer = 0;
}
} else {
// Subtle mechanical movement when not shooting
self.y += Math.sin(self.shootTimer) * 0.3;
self.trapGraphics.rotation = Math.sin(self.shootTimer * 0.3) * 0.02;
}
};
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
self.honkTimer = 0;
self.honkCooldown = 0;
self.isHonking = false;
// Create car graphics with much bigger size
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
scaleY: 1.5
});
// Store graphics reference
self.carGraphics = carGraphics;
// Add random color variety to white parts of the car
var randomColors = [0xffffff, 0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff, 0xffa500, 0x800080, 0xffc0cb];
var randomColor = randomColors[Math.floor(Math.random() * randomColors.length)];
self.carGraphics.tint = randomColor;
// Set standardized obstacle dimensions - 600x300 pixels (much bigger)
self.width = 600;
self.height = 300;
self.update = function () {
self.x += self.speed;
// Add driving animation
self.honkTimer += 0.05;
self.honkCooldown--;
// Trigger honking animation periodically
if (self.honkCooldown <= 0 && Math.random() < 0.015) {
self.isHonking = true;
self.honkCooldown = 80; // 1.3 second cooldown at 60fps
}
if (self.isHonking) {
// Quick bounce motion for honking
self.y += Math.sin(self.honkTimer * 4) * 1;
self.carGraphics.scaleX = 1.5 + Math.sin(self.honkTimer * 4) * 0.1;
self.carGraphics.scaleY = 1.5 + Math.sin(self.honkTimer * 4) * 0.1;
// Stop honking after a brief moment
if (self.honkTimer > Math.PI) {
self.isHonking = false;
self.honkTimer = 0;
}
} else {
// Gentle driving motion when not honking
self.y += Math.sin(self.honkTimer) * 0.3;
}
};
return self;
});
var CarnivorousPlant = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
self.snapTimer = 0;
self.snapDirection = 1;
self.isSnapping = false;
self.snapCooldown = 0;
// Create carnivorous plant graphics with standardized size
var plantGraphics = self.attachAsset('carnivorousPlant', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.75,
scaleY: 0.75
});
// Store graphics reference
self.plantGraphics = plantGraphics;
// Set standardized obstacle dimensions - 300x225 pixels
self.width = 300;
self.height = 225;
self.update = function () {
self.x += self.speed;
// Add menacing snapping animation
self.snapTimer += 0.08;
self.snapCooldown--;
// Trigger snapping animation periodically
if (self.snapCooldown <= 0 && Math.random() < 0.02) {
self.isSnapping = true;
self.snapCooldown = 60; // 1 second cooldown at 60fps
}
if (self.isSnapping) {
// Quick snap motion
self.y += Math.sin(self.snapTimer * 3) * 2;
self.plantGraphics.rotation = Math.sin(self.snapTimer * 3) * 0.1;
self.plantGraphics.scaleX = 0.75 + Math.sin(self.snapTimer * 3) * 0.1;
self.plantGraphics.scaleY = 0.75 + Math.sin(self.snapTimer * 3) * 0.1;
// Stop snapping after a brief moment
if (self.snapTimer > Math.PI * 2) {
self.isSnapping = false;
self.snapTimer = 0;
}
} else {
// Gentle swaying when not snapping
self.y += Math.sin(self.snapTimer) * 0.5;
self.plantGraphics.rotation = Math.sin(self.snapTimer * 0.5) * 0.03;
}
};
return self;
});
var Dripstone = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
self.dropTimer = 0;
self.isDropping = false;
self.dropCooldown = 0;
// Create dripstone graphics with standardized size
var dripstoneGraphics = self.attachAsset('dripstone', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 3.0,
scaleY: 2.0
});
// Store graphics reference
self.dripstoneGraphics = dripstoneGraphics;
// Set standardized obstacle dimensions - 300x600 pixels
self.width = 300;
self.height = 600;
self.update = function () {
self.x += self.speed;
// Add menacing dripping animation
self.dropTimer += 0.05;
self.dropCooldown--;
// Trigger dropping animation periodically
if (self.dropCooldown <= 0 && Math.random() < 0.01) {
self.isDropping = true;
self.dropCooldown = 90; // 1.5 second cooldown at 60fps
}
if (self.isDropping) {
// Quick drop motion
self.y += Math.sin(self.dropTimer * 5) * 3;
self.dripstoneGraphics.scaleY = 2.0 + Math.sin(self.dropTimer * 5) * 0.2;
// Stop dropping after a brief moment
if (self.dropTimer > Math.PI) {
self.isDropping = false;
self.dropTimer = 0;
}
} else {
// Gentle swaying when not dropping
self.y += Math.sin(self.dropTimer) * 0.3;
}
};
return self;
});
var JungleTreeTrunk = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
// Create jungle tree trunk graphics with standardized size
var trunkGraphics = self.attachAsset('palmTree', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.5,
scaleY: 2.5,
tint: 0x8B4513 // Brown color to make it look like a tree trunk
});
// Store graphics reference
self.trunkGraphics = trunkGraphics;
// Set standardized obstacle dimensions - 300x400 pixels
self.width = 300;
self.height = 400;
self.update = function () {
self.x += self.speed;
// Add slight swaying animation like a standing tree trunk
self.trunkGraphics.rotation = Math.sin(LK.ticks * 0.02) * 0.03;
};
return self;
});
var LanguageButton = Container.expand(function (language, callback) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.graphics = buttonGraphics;
var buttonText = new Text2(language, {
size: 120,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function () {
LK.getSound('Gear').play();
if (callback) callback();
};
return self;
});
var LaserWall = Container.expand(function (laserType) {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
self.laserType = laserType || 'laser_wall';
self.pulseTimer = 0;
self.pulseInterval = 60; // Pulse every second at 60fps
self.isPulsing = false;
self.pulseDirection = 1;
self.pulseIntensity = 0.5;
// Available laser wall types for animation
self.laserTypes = ['laser_wall', 'laser_wall_2', 'laser_wall_3', 'laser_wall_4'];
self.currentTypeIndex = 0;
self.animationTimer = 0;
self.animationInterval = Math.floor(Math.random() * 10) + 5; // Random interval between 5-15 frames (much faster)
// Create laser wall graphics with standardized scaling
var laserGraphics = self.attachAsset(self.laserType, {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 3.0,
scaleY: 1.0
});
// Store graphics reference for later manipulation
self.laserGraphics = laserGraphics;
// Set standardized obstacle dimensions - 300x600 pixels
self.width = 300;
self.height = 600;
// Function to change laser wall type with animation
self.changeLaserType = function () {
// Get random new type different from current
var newTypeIndex;
do {
newTypeIndex = Math.floor(Math.random() * self.laserTypes.length);
} while (newTypeIndex === self.currentTypeIndex);
self.currentTypeIndex = newTypeIndex;
var newType = self.laserTypes[self.currentTypeIndex];
// Animate transition with tween
tween(self.laserGraphics, {
alpha: 0.2,
scaleX: 2.5
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove old graphics and create new
if (self.laserGraphics) {
self.removeChild(self.laserGraphics);
}
self.laserGraphics = self.attachAsset(newType, {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.5,
scaleY: 1.0,
alpha: 0.2
});
// Animate back to normal
tween(self.laserGraphics, {
alpha: self.pulseIntensity,
scaleX: 3.0
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
// Set new random interval for next change
self.animationInterval = Math.floor(Math.random() * 10) + 5; // Much faster animation
self.animationTimer = 0;
};
self.update = function () {
self.x += self.speed;
// Handle laser type animation
self.animationTimer++;
if (self.animationTimer >= self.animationInterval) {
self.changeLaserType();
}
// Handle pulsing animation
self.pulseTimer++;
if (self.pulseTimer >= self.pulseInterval) {
self.isPulsing = !self.isPulsing;
self.pulseTimer = 0;
}
// Add pulsing glow effect
if (self.isPulsing) {
self.pulseIntensity += self.pulseDirection * 0.05;
if (self.pulseIntensity >= 1.0) {
self.pulseDirection = -1;
self.pulseIntensity = 1.0;
} else if (self.pulseIntensity <= 0.5) {
self.pulseDirection = 1;
self.pulseIntensity = 0.5;
}
if (self.laserGraphics) {
self.laserGraphics.alpha = self.pulseIntensity;
}
}
// Add slight vertical movement for dynamic feel
self.y += Math.sin(LK.ticks * 0.1) * 2;
};
return self;
});
var LavaGeyser = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
self.isErupting = false;
self.eruptionTimer = 0;
self.eruptionInterval = 120; // Erupts every 2 seconds at 60fps
self.eruptionDuration = 30; // Erupts for 0.5 seconds
// Create and attach the lava geyser graphics with standardized scaling
var geyserGraphics = self.attachAsset('lavaGeyser', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 3.0,
scaleY: 4.0
});
// Store graphics reference for later manipulation
self.geyserGraphics = geyserGraphics;
// Set standardized obstacle dimensions - 300x400 pixels
self.width = 300;
self.height = 400;
self.update = function () {
self.x += self.speed;
// Handle eruption timing
self.eruptionTimer++;
if (!self.isErupting && self.eruptionTimer >= self.eruptionInterval) {
self.isErupting = true;
self.eruptionTimer = 0;
// Make geyser glow bright red when erupting with pulsing effect
self.geyserGraphics.tint = 0xff2222;
self.geyserGraphics.alpha = 0.9 + Math.sin(LK.ticks * 0.5) * 0.1;
} else if (self.isErupting && self.eruptionTimer >= self.eruptionDuration) {
self.isErupting = false;
self.eruptionTimer = 0;
// Return to enhanced normal color for better visibility
self.geyserGraphics.tint = 0xffffff;
self.geyserGraphics.alpha = 1.0;
}
// Add slight vertical movement when erupting
if (self.isErupting) {
self.y += Math.sin(LK.ticks * 0.3) * 3;
}
};
return self;
});
var MenuButton = Container.expand(function (text, callback) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5
});
var buttonText = new Text2(text, {
size: 120,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.down = function () {
LK.getSound('Lever').play();
if (callback) callback();
};
return self;
});
var MusicButton = Container.expand(function (isEnabled, callback) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('menuButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.6
});
self.graphics = buttonGraphics;
self.enabled = isEnabled;
self.callback = callback;
var buttonText = new Text2('MUSIC: ' + (isEnabled ? 'AN' : 'AUS'), {
size: 80,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.5);
self.addChild(buttonText);
self.text = buttonText;
// Update button appearance
self.updateAppearance = function () {
self.text.setText('MUSIC: ' + (self.enabled ? 'AN' : 'AUS'));
self.graphics.tint = self.enabled ? 0x90ee90 : 0x666666;
};
self.updateAppearance();
self.down = function () {
if (self.enabled) {
LK.getSound('Started').play();
} else {
LK.getSound('Lever').play();
}
self.enabled = !self.enabled;
self.updateAppearance();
if (self.callback) self.callback(self.enabled);
};
return self;
});
var PalmTree = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
// Create palm tree graphics with standardized size
var palmTreeGraphics = self.attachAsset('palmTree', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 3.0,
scaleY: 3.0
});
// Store graphics reference
self.palmTreeGraphics = palmTreeGraphics;
// Set standardized obstacle dimensions - 300x400 pixels
self.width = 300;
self.height = 400;
self.update = function () {
self.x += self.speed;
// Add subtle swaying animation
palmTreeGraphics.rotation = Math.sin(LK.ticks * 0.02) * 0.05;
};
return self;
});
var PileOfAsh = Container.expand(function () {
var self = Container.call(this);
// Create pile of ash graphics
var ashGraphics = self.attachAsset('pileOfAsh', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 2.0,
scaleY: 1.5
});
self.ashGraphics = ashGraphics;
self.creationTime = LK.ticks;
// Set dimensions
self.width = ashGraphics.width;
self.height = ashGraphics.height;
self.update = function () {
// Slowly fade out over time
var timeSinceCreation = LK.ticks - self.creationTime;
if (timeSinceCreation > 180) {
// 3 seconds at 60fps
self.ashGraphics.alpha = Math.max(0, 1 - (timeSinceCreation - 180) / 120);
}
// Add slight wind effect
self.ashGraphics.x += Math.sin(LK.ticks * 0.02) * 0.5;
};
return self;
});
var Pillar = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
// Create pillar graphics extending full screen height and wider
var pillarGraphics = self.attachAsset('pillar', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.5,
scaleY: 3.415
});
// Store graphics reference
self.pillarGraphics = pillarGraphics;
// Set dimensions to match full screen height and wider width - 300x2732 pixels
self.width = 300;
self.height = 2732;
self.update = function () {
self.x += self.speed;
// Add subtle ancient temple atmosphere
self.pillarGraphics.alpha = 0.95 + Math.sin(LK.ticks * 0.01) * 0.05;
};
return self;
});
var Pirate = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
self.swayTimer = 0;
self.swayDirection = 1;
self.isAttacking = false;
self.attackCooldown = 0;
// Create pirate graphics with standardized size
var pirateGraphics = self.attachAsset('pirate', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 1.0,
scaleY: 1.0
});
// Store graphics reference
self.pirateGraphics = pirateGraphics;
// Set standardized obstacle dimensions - 300x400 pixels
self.width = 300;
self.height = 400;
self.update = function () {
self.x += self.speed;
// Add menacing swaying animation
self.swayTimer += 0.06;
self.attackCooldown--;
// Trigger attack animation periodically
if (self.attackCooldown <= 0 && Math.random() < 0.02) {
self.isAttacking = true;
self.attackCooldown = 80; // 1.3 second cooldown at 60fps
}
if (self.isAttacking) {
// Quick attack motion
self.y += Math.sin(self.swayTimer * 4) * 2;
self.pirateGraphics.rotation = Math.sin(self.swayTimer * 4) * 0.15;
self.pirateGraphics.scaleX = 1.0 + Math.sin(self.swayTimer * 4) * 0.1;
self.pirateGraphics.scaleY = 1.0 + Math.sin(self.swayTimer * 4) * 0.1;
// Stop attacking after a brief moment
if (self.swayTimer > Math.PI) {
self.isAttacking = false;
self.swayTimer = 0;
}
} else {
// Gentle swaying when not attacking
self.y += Math.sin(self.swayTimer) * 0.5;
self.pirateGraphics.rotation = Math.sin(self.swayTimer * 0.5) * 0.05;
}
};
return self;
});
var SkinButton = Container.expand(function (skinType, callback) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('skinButton', {
anchorX: 0.5,
anchorY: 0.5
});
self.graphics = buttonGraphics;
var skinPreview = self.attachAsset(skinType, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
self.down = function () {
LK.getSound('Lever').play();
if (callback) callback();
};
return self;
});
var Turtle = Container.expand(function () {
var self = Container.call(this);
self.skinType = 'turtle_default';
self.originalSkin = 'turtle_default';
self.isJumping = false;
self.jumpSpeed = 0;
self.groundY = 0;
self.gravity = 2.4;
self.jumpPower = -90;
self.graphics = null;
self.setSkin = function (skinType) {
if (self.graphics) {
self.removeChild(self.graphics);
}
self.skinType = skinType;
self.originalSkin = skinType;
self.graphics = self.attachAsset(skinType, {
anchorX: 0.5,
anchorY: 1.0
});
// Make turtle completely black in silhouette mountain map except for golden turtle
if (currentBackground === 'background_silhouette_mountain' && skinType !== 'turtle_golden' && skinType !== 'turtle_golden_jump') {
self.graphics.tint = 0x000000;
} else {
self.graphics.tint = 0xffffff;
}
// Set the container's dimensions to match the graphics
self.width = self.graphics.width;
self.height = self.graphics.height;
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
// Adjust jump power for golden turtle based on game speed to maintain consistent difficulty
var adjustedJumpPower = self.jumpPower;
if (self.originalSkin === 'turtle_golden' && typeof speedMultiplier !== 'undefined') {
// Scale jump power with speed multiplier to maintain same relative jump height
adjustedJumpPower = self.jumpPower * speedMultiplier;
}
self.jumpSpeed = adjustedJumpPower;
LK.getSound('jump').play();
// Track jump for Explorer achievement
if (gameState === 'playing' && !hasJumpedOnMap[currentBackground]) {
hasJumpedOnMap[currentBackground] = true;
// Check if all maps have been jumped on
var allMapsJumped = true;
var mapKeys = ['background_beach', 'background_jungle', 'background_volcano', 'background_space', 'background_swamp', 'background_city', 'background_silhouette_mountain', 'background_pirate', 'background_ancient_temple'];
for (var i = 0; i < mapKeys.length; i++) {
if (!hasJumpedOnMap[mapKeys[i]]) {
allMapsJumped = false;
break;
}
}
if (allMapsJumped && !storage.explorerAchieved) {
storage.explorerAchieved = true;
showAchievementNotification("Explorer");
}
}
// Change skin when jumping
var jumpSkin = self.originalSkin;
if (self.originalSkin === 'turtle_default') {
jumpSkin = 'turtle_jump';
} else if (self.originalSkin === 'turtle_suit') {
jumpSkin = 'turtle_suit_jump';
} else if (self.originalSkin === 'turtle_red') {
jumpSkin = 'turtle_suit_jump';
} else if (self.originalSkin === 'turtle_golden') {
jumpSkin = 'turtle_golden_jump';
}
if (jumpSkin !== self.skinType) {
if (self.graphics) {
self.removeChild(self.graphics);
}
self.skinType = jumpSkin;
self.graphics = self.attachAsset(jumpSkin, {
anchorX: 0.5,
anchorY: 1.0
});
// Make turtle completely black in silhouette mountain map except for golden turtle
if (currentBackground === 'background_silhouette_mountain' && jumpSkin !== 'turtle_golden' && jumpSkin !== 'turtle_golden_jump') {
self.graphics.tint = 0x000000;
} else {
self.graphics.tint = 0xffffff;
}
// Set the container's dimensions to match the graphics
self.width = self.graphics.width;
self.height = self.graphics.height;
}
}
};
self.update = function () {
if (self.isJumping) {
self.jumpSpeed += self.gravity;
self.y += self.jumpSpeed;
if (self.y >= self.groundY) {
self.y = self.groundY;
self.isJumping = false;
self.jumpSpeed = 0;
// Play landing sound based on current map
var landingSound = 'Land';
if (currentBackground === 'background_jungle') {
landingSound = 'out_of_mud_landing_swamp';
} else if (currentBackground === 'background_volcano') {
landingSound = 'landing_volcano';
} else if (currentBackground === 'background_space') {
landingSound = 'landing_space';
} else if (currentBackground === 'background_swamp') {
landingSound = 'landing_swamp';
} else if (currentBackground === 'background_city') {
landingSound = 'landing_city';
} else if (currentBackground === 'background_silhouette_mountain') {
landingSound = 'landing_silhouette_mountain';
} else if (currentBackground === 'background_pirate') {
landingSound = 'pirate_landing';
} else if (currentBackground === 'background_ancient_temple') {
landingSound = 'landing_ancient_temple';
}
LK.getSound(landingSound).play();
// Create dust particles
createDustParticles(self.x, self.y);
// Restore original skin when landing
if (self.skinType !== self.originalSkin) {
if (self.graphics) {
self.removeChild(self.graphics);
}
self.skinType = self.originalSkin;
self.graphics = self.attachAsset(self.originalSkin, {
anchorX: 0.5,
anchorY: 1.0
});
// Make turtle completely black in silhouette mountain map except for golden turtle
if (currentBackground === 'background_silhouette_mountain' && self.originalSkin !== 'turtle_golden') {
self.graphics.tint = 0x000000;
} else {
self.graphics.tint = 0xffffff;
}
// Set the container's dimensions to match the graphics
self.width = self.graphics.width;
self.height = self.graphics.height;
}
}
}
};
return self;
});
var TurtleBack = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.collected = false;
var backGraphics = self.attachAsset('turtle_back', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 2.5
});
// Store graphics reference
self.backGraphics = backGraphics;
// Set standardized obstacle dimensions - 300x400 pixels
self.width = 300;
self.height = 400;
self.update = function () {
self.x += self.speed;
// Add floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game states
var gameState = 'menu'; // 'menu', 'playing', 'gameOver'
var gamePaused = false; // Controls whether game movement is paused
var darkOverlay = null; // Dark overlay for game over effect
var achievementContainer = null; // Container for achievement notifications
var currentSkin = storage.selectedSkin || 'turtle_default';
var currentLanguage = storage.selectedLanguage || 'English';
var languageMenuContainer = null;
var settingsMenuContainer = null;
var mapMenuContainer = null;
var highscoreWindowContainer = null;
var currentBackground = storage.selectedBackground || 'background_beach';
var backgroundElement = null;
var menuOverlay = null;
// Music settings
var musicEnabled = storage.musicEnabled;
var ambienceEnabled = storage.ambienceEnabled;
var musicVolume = storage.musicVolume || 0.5;
var currentMusicTrack = null;
// Language translations
var translations = {
English: {
title: 'Turtle Run',
play: 'PLAY',
skins: 'SKINS',
back: 'BACK',
chooseSkin: 'Choose Skin',
selectLanguage: 'Select Language',
highScore: 'High Score: '
},
Deutsch: {
title: 'Turtlerun',
play: 'SPIELEN',
skins: 'SKINS',
back: 'ZURÜCK',
chooseSkin: 'Skin auswählen',
selectLanguage: 'Sprache auswählen',
highScore: 'Highscore: '
}
};
// Function to get translated text
function getText(key) {
return translations[currentLanguage][key] || translations['English'][key];
}
// Game elements
var turtle = null;
var palmTrees = [];
var obstacles = [];
var turtleBacks = [];
var ground = null;
var scoreText = null;
var gameScore = 0;
var lastBoulderTime = 0;
var boulderInterval = 200; // milliseconds - much more frequent spawning
var minBoulderInterval = 40; // extremely aggressive minimum interval
var difficultyLevel = 1;
var speedMultiplier = 1.0;
// Achievement tracking
var hasJumpedOnMap = {
'background_beach': false,
'background_jungle': false,
'background_volcano': false,
'background_space': false,
'background_swamp': false,
'background_city': false,
'background_silhouette_mountain': false,
'background_pirate': false,
'background_ancient_temple': false
};
// Menu elements
var menuContainer = null;
var skinMenuContainer = null;
// Use stored background preference
currentBackground = storage.selectedBackground || 'background_beach';
var backgroundScrollSpeed = -12;
var backgroundElements = [];
// Create scrolling background system
function createScrollingBackground() {
// Create two background elements for seamless scrolling
for (var i = 0; i < 2; i++) {
var bg = game.addChild(LK.getAsset(currentBackground, {
anchorX: 0,
anchorY: 0,
x: i * 2048,
y: 0
}));
backgroundElements.push(bg);
}
}
// Initialize scrolling background
createScrollingBackground();
// Create menu overlay for better contrast
function createMenuOverlay() {
if (!menuOverlay) {
menuOverlay = game.addChild(LK.getAsset('background_sky', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0.5,
tint: 0x000000
}));
}
}
// Create dark overlay for game over effect
function createDarkOverlay() {
if (!darkOverlay) {
darkOverlay = game.addChild(LK.getAsset('background_sky', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 20.48,
scaleY: 27.32,
alpha: 0,
tint: 0x000000
}));
// Make sure it's on top of everything except UI
game.setChildIndex(darkOverlay, game.children.length - 1);
}
}
function showMenuOverlay() {
if (menuOverlay) {
menuOverlay.visible = true;
// Ensure overlay is above background but below menu elements
game.setChildIndex(menuOverlay, 2);
}
}
function hideMenuOverlay() {
if (menuOverlay) {
menuOverlay.visible = false;
}
}
// Initialize ground
function createGround() {
var groundAsset = 'ground';
if (currentBackground === 'background_jungle') {
groundAsset = 'ground_jungle';
} else if (currentBackground === 'background_volcano') {
groundAsset = 'ground_volcano';
} else if (currentBackground === 'background_space') {
groundAsset = 'ground_space';
} else if (currentBackground === 'background_swamp') {
groundAsset = 'ground_swamp';
} else if (currentBackground === 'background_city') {
groundAsset = 'ground_city';
} else if (currentBackground === 'background_silhouette_mountain') {
groundAsset = 'ground_silhouette_mountain';
} else if (currentBackground === 'background_pirate') {
groundAsset = 'ground_pirate';
} else if (currentBackground === 'background_ancient_temple') {
groundAsset = 'ground_ancient_temple';
}
return LK.getAsset(groundAsset, {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - 270,
scaleY: 1.5
});
}
ground = game.addChild(createGround());
// Initialize score display
var initialScore = storage.currentScore || 0;
scoreText = new Text2(initialScore.toString(), {
size: 300,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
scoreText.y = 50;
// Set initial color based on saved score
if (initialScore >= 40) {
scoreText.fill = 0xff00ff; // Magenta for 40+
scoreText.tint = 0xff00ff;
} else if (initialScore >= 25) {
scoreText.fill = 0xffd700; // Gold
scoreText.tint = 0xffd700;
} else if (initialScore >= 20) {
scoreText.fill = 0xc0c0c0; // Silver
scoreText.tint = 0xc0c0c0;
} else if (initialScore >= 10) {
scoreText.fill = 0xcd7f32; // Bronze
scoreText.tint = 0xcd7f32;
} else {
scoreText.fill = 0xffffff; // Default white
scoreText.tint = 0xffffff;
}
function createMainMenu() {
createMenuOverlay();
showMenuOverlay();
menuContainer = game.addChild(new Container());
// Title
var titleText = new Text2(getText('title'), {
size: 360,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 800;
// Add black outline effect by creating shadow text
var titleShadow = new Text2(getText('title'), {
size: 360,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 4;
titleShadow.y = 800 + 4;
menuContainer.addChild(titleShadow);
menuContainer.addChild(titleText);
// Play button
var playButton = menuContainer.addChild(new MenuButton(getText('play'), function () {
startGame();
}));
playButton.x = 2048 / 2;
playButton.y = 1200;
// Skins button
var skinsButton = menuContainer.addChild(new MenuButton(getText('skins'), function () {
showSkinMenu();
}));
skinsButton.x = 2048 / 2;
skinsButton.y = 1500;
// Map button
var mapButton = menuContainer.addChild(LK.getAsset('mapButton', {
anchorX: 0.5,
anchorY: 0.5
}));
mapButton.x = 2048 / 2 - 200;
mapButton.y = 1800;
mapButton.down = function () {
LK.getSound('Map').play();
showMapMenu();
};
// High Scores and Achievements button
var highscoreButton = menuContainer.addChild(LK.getAsset('HighscoreButton', {
anchorX: 0.5,
anchorY: 0.5
}));
highscoreButton.x = 2048 / 2 + 200;
highscoreButton.y = 1800;
highscoreButton.down = function () {
LK.getSound('Trophy').play();
showHighscoreWindow();
};
// Gear icon for language selection
var gearIcon = menuContainer.addChild(LK.getAsset('gearIcon', {
anchorX: 0.5,
anchorY: 0.5
}));
gearIcon.x = 2048 - 150;
gearIcon.y = 150;
gearIcon.down = function () {
LK.getSound('Gear').play();
showSettingsMenu();
};
// Show current map display
var currentMapContainer = menuContainer.addChild(new Container());
currentMapContainer.x = 2048 / 2;
currentMapContainer.y = 2300;
// Get current map info
var currentMapName = 'Strand';
if (currentBackground === 'background_jungle') {
currentMapName = 'Dschungel';
} else if (currentBackground === 'background_volcano') {
currentMapName = 'Vulkan';
} else if (currentBackground === 'background_space') {
currentMapName = 'Space';
} else if (currentBackground === 'background_swamp') {
currentMapName = 'Swamp';
} else if (currentBackground === 'background_city') {
currentMapName = 'City';
} else if (currentBackground === 'background_silhouette_mountain') {
currentMapName = 'Silhouette Mountain';
} else if (currentBackground === 'background_pirate') {
currentMapName = 'Pirate';
} else if (currentBackground === 'background_ancient_temple') {
currentMapName = 'Ancient Temple';
}
// Current map preview
var currentMapPreview = currentMapContainer.addChild(LK.getAsset(currentBackground, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.16
}));
// Current map name
var currentMapNameText = new Text2(currentMapName, {
size: 60,
fill: 0xFFFFFF
});
currentMapNameText.anchor.set(0.5, 0.5);
currentMapNameText.x = 0;
currentMapNameText.y = 120;
currentMapContainer.addChild(currentMapNameText);
// Highlight current map
currentMapPreview.tint = 0x90ee90;
// High score display
var highScore = storage.highScore || 0;
var highScoreText = new Text2(getText('highScore') + highScore, {
size: 180,
fill: 0xFFFFFF
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 2048 / 2;
highScoreText.y = 2500;
menuContainer.addChild(highScoreText);
// Turtle decoration removed
}
function showSkinMenu() {
if (menuContainer) {
menuContainer.visible = false;
}
skinMenuContainer = game.addChild(new Container());
// Title
var titleText = new Text2(getText('chooseSkin'), {
size: 240,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 600;
// Add title shadow for better contrast
var titleShadow = new Text2(getText('chooseSkin'), {
size: 240,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 3;
titleShadow.y = 600 + 3;
skinMenuContainer.addChild(titleShadow);
skinMenuContainer.addChild(titleText);
// Skin options
var skins = ['turtle_default', 'turtle_red'];
// Add golden turtle if unlocked
if (storage.goldenTurtleUnlocked) {
skins.push('turtle_golden');
}
var startX = 2048 / 2 - 600;
var startY = 900;
for (var i = 0; i < skins.length; i++) {
var skinButton = skinMenuContainer.addChild(new SkinButton(skins[i], function (skin) {
return function () {
currentSkin = skin;
storage.selectedSkin = skin;
hideSkinMenu();
};
}(skins[i])));
skinButton.x = startX + i * 600;
skinButton.y = startY;
// Highlight selected skin
if (skins[i] === currentSkin) {
skinButton.graphics.tint = 0x90ee90;
}
}
// Back button
var backButton = skinMenuContainer.addChild(new MenuButton(getText('back'), function () {
hideSkinMenu();
}));
backButton.x = 2048 / 2;
backButton.y = 1400;
}
function hideSkinMenu() {
if (skinMenuContainer) {
skinMenuContainer.destroy();
skinMenuContainer = null;
}
if (menuContainer) {
menuContainer.visible = true;
}
showMenuOverlay();
}
function showSettingsMenu() {
if (menuContainer) {
menuContainer.visible = false;
}
settingsMenuContainer = game.addChild(new Container());
// Title
var titleText = new Text2('Settings', {
size: 240,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 600;
// Add title shadow for better contrast
var titleShadow = new Text2('Settings', {
size: 240,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 3;
titleShadow.y = 600 + 3;
settingsMenuContainer.addChild(titleShadow);
settingsMenuContainer.addChild(titleText);
// Music toggle button
var musicToggle = settingsMenuContainer.addChild(new MusicButton(musicEnabled, function (enabled) {
musicEnabled = enabled;
storage.musicEnabled = enabled;
if (!musicEnabled) {
LK.stopMusic();
currentMusicTrack = null;
} else {
playBackgroundMusic();
}
}));
musicToggle.x = 2048 / 2;
musicToggle.y = 1100;
// Language button
var languageButton = settingsMenuContainer.addChild(new MenuButton(getText('selectLanguage'), function () {
hideSettingsMenu();
showLanguageMenu();
}));
languageButton.x = 2048 / 2;
languageButton.y = 1400;
// Reset button
var resetButton = settingsMenuContainer.addChild(new MenuButton('RESET', function () {
showResetConfirmation();
}));
resetButton.x = 2048 / 2;
resetButton.y = 1700;
// Make reset button red
resetButton.children[0].tint = 0xff0000; // Tint the button graphics red
// Back button
var backButton = settingsMenuContainer.addChild(new MenuButton(getText('back'), function () {
hideSettingsMenu();
}));
backButton.x = 2048 / 2;
backButton.y = 2000;
}
function hideSettingsMenu() {
if (settingsMenuContainer) {
settingsMenuContainer.destroy();
settingsMenuContainer = null;
}
if (menuContainer) {
menuContainer.visible = true;
}
showMenuOverlay();
}
var resetConfirmationContainer = null;
function showResetConfirmation() {
if (settingsMenuContainer) {
settingsMenuContainer.visible = false;
}
LK.getSound('Alarm').play();
resetConfirmationContainer = game.addChild(new Container());
// Title
var titleText = new Text2('Are you sure you want to reset?', {
size: 180,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 1000;
// Add title shadow for better contrast
var titleShadow = new Text2('Are you sure you want to reset?', {
size: 180,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 3;
titleShadow.y = 1000 + 3;
resetConfirmationContainer.addChild(titleShadow);
resetConfirmationContainer.addChild(titleText);
// Flash screen red repeatedly
var flashInterval = LK.setInterval(function () {
LK.effects.flashScreen(0xff0000, 200); // Flash red for 200ms
}, 500); // Repeat every 500ms
resetConfirmationContainer.flashInterval = flashInterval; // Store interval ID to clear later
// Yes button
var yesButton = resetConfirmationContainer.addChild(new MenuButton('Confirm Reset', function () {
LK.getSound('Warning').play();
performReset();
hideResetConfirmation();
}));
// Tint the button graphics red
yesButton.children[0].tint = 0xff0000;
yesButton.x = 2048 / 2 - 300;
yesButton.y = 1400;
// No button
var noButton = resetConfirmationContainer.addChild(new MenuButton('NO', function () {
hideResetConfirmation();
}));
noButton.x = 2048 / 2 + 300;
noButton.y = 1400;
}
function hideResetConfirmation() {
if (resetConfirmationContainer) {
if (resetConfirmationContainer.flashInterval) {
LK.clearInterval(resetConfirmationContainer.flashInterval);
}
resetConfirmationContainer.destroy();
resetConfirmationContainer = null;
}
if (settingsMenuContainer) {
settingsMenuContainer.visible = true;
}
}
function performReset() {
// Reset all stored data
storage.highScore = 0;
storage.currentScore = 0;
storage.selectedSkin = 'turtle_default';
storage.selectedBackground = 'background_beach';
storage.selectedLanguage = 'English';
storage.goldenTurtleUnlocked = false;
storage.musicEnabled = false;
storage.ambienceEnabled = true;
// Disable all interactions during reset animation
gamePaused = true;
// Disable all menu interactions by hiding all menu containers
if (resetConfirmationContainer) {
resetConfirmationContainer.visible = false;
}
if (settingsMenuContainer) {
settingsMenuContainer.visible = false;
}
if (menuContainer) {
menuContainer.visible = false;
}
if (skinMenuContainer) {
skinMenuContainer.visible = false;
}
if (mapMenuContainer) {
mapMenuContainer.visible = false;
}
if (highscoreWindowContainer) {
highscoreWindowContainer.visible = false;
}
if (languageMenuContainer) {
languageMenuContainer.visible = false;
}
// Start reset animation
// First, everything turns red slowly
tween(game, {
tint: 0xff0000
}, {
duration: 1000,
easing: tween.linear,
onFinish: function onFinish() {
// Then everything turns dark slowly
createDarkOverlay();
// Ensure darkOverlay exists before tweening
if (darkOverlay) {
tween(darkOverlay, {
alpha: 1.0
}, {
duration: 1000,
easing: tween.linear,
onFinish: function onFinish() {
// Then everything explodes
// Create an explosion effect (simple particle burst)
for (var i = 0; i < 50; i++) {
var particle = game.addChild(LK.getAsset('sandParticle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
tint: 0xff0000 + Math.random() * 0x00ffff // Random red to yellow tint
}));
var speed = Math.random() * 30 + 10;
var angle = Math.random() * Math.PI * 2;
var velocityX = Math.cos(angle) * speed;
var velocityY = Math.sin(angle) * speed;
var gravity = 1.5;
var lifespan = Math.random() * 60 + 30; // particles last 0.5 to 1 second
particle.update = function () {
this.x += velocityX;
this.y += velocityY;
velocityY += gravity;
this.alpha -= 1 / lifespan;
if (this.alpha <= 0) {
this.destroy();
}
};
}
// Display reset success message
var resetMessage = new Text2(currentLanguage === 'Deutsch' ? 'Speicherdaten erfolgreich gelöscht' : 'Your data has been successfully reset.', {
size: 100,
fill: 0xFFFFFF
});
resetMessage.anchor.set(0.5, 0.5);
resetMessage.x = 2048 / 2;
resetMessage.y = 2732 / 2;
resetMessage.fill = 0xff0000; // Make the text red
game.addChild(resetMessage);
// Play the Memories sound effect when the message appears
LK.getSound('Memories').play();
// Fade out the message after a short delay
tween(resetMessage, {
alpha: 0
}, {
duration: 1500,
delay: 1000,
onFinish: function onFinish() {
resetMessage.destroy();
// After the message fades out, fade the dark overlay to white
if (darkOverlay) {
tween(darkOverlay, {
tint: 0xffffff // Change tint to white
}, {
duration: 1000,
// Duration for fading to white
easing: tween.linear
});
}
}
});
// Reset current game variables after animation
currentSkin = 'turtle_default';
currentBackground = 'background_beach';
currentLanguage = 'English';
musicEnabled = false;
ambienceEnabled = true;
// Reset score display
scoreText.setText('0');
scoreText.fill = 0xffffff;
scoreText.tint = 0xffffff;
// Stop any music
LK.stopMusic();
currentMusicTrack = null;
// Change background to default
changeBackground('background_beach');
// Return to main menu
hideSettingsMenu();
// Reset game tint
game.tint = 0xffffff;
// Re-enable interactions after reset animation
gamePaused = false;
// Re-show main menu
if (menuContainer) {
menuContainer.visible = true;
}
// Clean up dark overlay after explosion
if (darkOverlay) {
// Animate the dark overlay to fade to white
tween(darkOverlay, {
tint: 0xffffff,
alpha: 1.0 // Stay white for a moment
}, {
duration: 1500,
// Duration for fading to white
easing: tween.linear,
onFinish: function onFinish() {
// Stay white for a quarter second, then fade out and clear
LK.setTimeout(function () {
if (darkOverlay) {
tween(darkOverlay, {
alpha: 0.0 // Fade out the alpha as well
}, {
duration: 500,
// Fade out quickly
easing: tween.linear,
onFinish: function onFinish() {
darkOverlay.destroy();
darkOverlay = null;
}
});
}
}, 250); // Stay white for 250ms
}
});
}
}
});
} // Close darkOverlay existence check
}
});
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
createMainMenu();
}
function showLanguageMenu() {
if (menuContainer) {
menuContainer.visible = false;
}
languageMenuContainer = game.addChild(new Container());
// Title
var titleText = new Text2(getText('selectLanguage'), {
size: 240,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 800;
// Add title shadow for better contrast
var titleShadow = new Text2(getText('selectLanguage'), {
size: 240,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 3;
titleShadow.y = 800 + 3;
languageMenuContainer.addChild(titleShadow);
languageMenuContainer.addChild(titleText);
// Language options
var languages = ['English', 'Deutsch'];
var startY = 1200;
for (var i = 0; i < languages.length; i++) {
var langButton = languageMenuContainer.addChild(new LanguageButton(languages[i], function (lang) {
return function () {
currentLanguage = lang;
storage.selectedLanguage = lang;
hideLanguageMenu();
// Refresh main menu with new language
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
createMainMenu();
};
}(languages[i])));
langButton.x = 2048 / 2;
langButton.y = startY + i * 300;
// Highlight selected language
if (languages[i] === currentLanguage) {
langButton.graphics.tint = 0x90ee90;
}
}
// Back button
var backButton = languageMenuContainer.addChild(new MenuButton(getText('back'), function () {
hideLanguageMenu();
}));
backButton.x = 2048 / 2;
backButton.y = 2500;
}
function hideLanguageMenu() {
if (languageMenuContainer) {
languageMenuContainer.destroy();
languageMenuContainer = null;
}
showSettingsMenu();
}
function showHighscoreWindow() {
if (menuContainer) {
menuContainer.visible = false;
}
highscoreWindowContainer = game.addChild(new Container());
// Create scrollable content container
var scrollContainer = highscoreWindowContainer.addChild(new Container());
var scrollY = 0;
var maxScrollY = 0;
var contentHeight = 0;
// Title
var titleText = new Text2('High Scores and Achievements', {
size: 180,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 400;
// Add title shadow for better contrast
var titleShadow = new Text2('High Scores and Achievements', {
size: 180,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 3;
titleShadow.y = 400 + 3;
highscoreWindowContainer.addChild(titleShadow);
highscoreWindowContainer.addChild(titleText);
var currentY = 600;
// High Score Section
var highScoreLabel = new Text2('High Score', {
size: 150,
fill: 0xFFD700
});
highScoreLabel.anchor.set(0.5, 0.5);
highScoreLabel.x = 2048 / 2;
highScoreLabel.y = currentY;
scrollContainer.addChild(highScoreLabel);
currentY += 200;
// Get highscore
var highScore = storage.highScore || 0;
if (highScore === 0) {
// No high score yet - show message
var noScoreText = new Text2('No high score yet!', {
size: 120,
fill: 0xFFFFFF
});
noScoreText.anchor.set(0.5, 0.5);
noScoreText.x = 2048 / 2;
noScoreText.y = currentY;
scrollContainer.addChild(noScoreText);
currentY += 150;
} else {
// Show high score with trophy styling
var highScoreText = new Text2(highScore.toString(), {
size: 200,
fill: 0xFFD700
});
highScoreText.anchor.set(0.5, 0.5);
highScoreText.x = 2048 / 2;
highScoreText.y = currentY;
scrollContainer.addChild(highScoreText);
currentY += 200;
// Add trophy icon or decoration
var trophyText = new Text2('🏆', {
size: 120,
fill: 0xFFD700
});
trophyText.anchor.set(0.5, 0.5);
trophyText.x = 2048 / 2;
trophyText.y = currentY;
scrollContainer.addChild(trophyText);
currentY += 150;
}
// Achievements Section
currentY += 100;
var achievementsLabel = new Text2('Achievements', {
size: 150,
fill: 0x90ee90
});
achievementsLabel.anchor.set(0.5, 0.5);
achievementsLabel.x = 2048 / 2;
achievementsLabel.y = currentY;
scrollContainer.addChild(achievementsLabel);
currentY += 200;
// Check and display achievements
var achievements = [];
// Bronze Rank Achievement
if (storage.highScore >= 10) {
achievements.push({
name: 'Bronze Rank',
desc: 'Reach score of 10'
});
}
// Silver Rank Achievement
if (storage.highScore >= 20) {
achievements.push({
name: 'Silver Rank',
desc: 'Reach score of 20'
});
}
// Gold Rank Achievement
if (storage.highScore >= 25) {
achievements.push({
name: 'Gold Rank',
desc: 'Reach score of 25'
});
}
// Master Rank Achievement
if (storage.highScore >= 40) {
achievements.push({
name: 'Master Rank',
desc: 'Reach score of 40'
});
}
// Explorer Achievement
if (storage.explorerAchieved) {
achievements.push({
name: 'Explorer',
desc: 'Jump on every map'
});
}
// Display achievements or no achievements message
if (achievements.length === 0) {
var noAchievementsText = new Text2('No achievements yet!', {
size: 120,
fill: 0xFFFFFF
});
noAchievementsText.anchor.set(0.5, 0.5);
noAchievementsText.x = 2048 / 2;
noAchievementsText.y = currentY;
scrollContainer.addChild(noAchievementsText);
currentY += 150;
} else {
for (var i = 0; i < achievements.length; i++) {
// Achievement name
var achievementName = new Text2(achievements[i].name, {
size: 100,
fill: 0xFFD700
});
achievementName.anchor.set(0.5, 0.5);
achievementName.x = 2048 / 2;
achievementName.y = currentY;
scrollContainer.addChild(achievementName);
currentY += 120;
// Achievement description
var achievementDesc = new Text2(achievements[i].desc, {
size: 80,
fill: 0xCCCCCC
});
achievementDesc.anchor.set(0.5, 0.5);
achievementDesc.x = 2048 / 2;
achievementDesc.y = currentY;
scrollContainer.addChild(achievementDesc);
currentY += 150;
}
}
// Calculate content height and max scroll
contentHeight = currentY;
maxScrollY = Math.max(0, contentHeight - 1800); // Visible area height
// Scroll handling
var isScrolling = false;
var lastTouchY = 0;
highscoreWindowContainer.down = function (x, y) {
if (gamePaused) return;
isScrolling = true;
lastTouchY = y;
};
highscoreWindowContainer.move = function (x, y) {
if (gamePaused) return;
if (isScrolling) {
var deltaY = y - lastTouchY;
scrollY += deltaY;
scrollY = Math.max(-maxScrollY, Math.min(0, scrollY));
scrollContainer.y = scrollY;
lastTouchY = y;
}
};
highscoreWindowContainer.up = function () {
if (gamePaused) return;
isScrolling = false;
};
// Back button
var backButton = highscoreWindowContainer.addChild(new MenuButton(getText('back'), function () {
if (gamePaused) return;
hideHighscoreWindow();
}));
backButton.x = 2048 / 2;
backButton.y = 2500;
}
function hideHighscoreWindow() {
if (highscoreWindowContainer) {
highscoreWindowContainer.destroy();
highscoreWindowContainer = null;
}
if (menuContainer) {
menuContainer.visible = true;
}
showMenuOverlay();
}
function showMapMenu() {
if (menuContainer) {
menuContainer.visible = false;
}
mapMenuContainer = game.addChild(new Container());
// Title
var titleText = new Text2('Select Map', {
size: 240,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 400;
// Add title shadow for better contrast
var titleShadow = new Text2('Select Map', {
size: 240,
fill: 0x000000
});
titleShadow.anchor.set(0.5, 0.5);
titleShadow.x = 2048 / 2 + 3;
titleShadow.y = 400 + 3;
mapMenuContainer.addChild(titleShadow);
mapMenuContainer.addChild(titleText);
// Map options with background previews
var mapTypes = [{
id: 'background_beach',
name: 'Strand'
}, {
id: 'background_jungle',
name: 'Dschungel'
}, {
id: 'background_volcano',
name: 'Vulkan'
}, {
id: 'background_space',
name: 'Space'
}, {
id: 'background_swamp',
name: 'Swamp'
}, {
id: 'background_city',
name: 'City'
}, {
id: 'background_silhouette_mountain',
name: 'Silhouette Mountain'
}, {
id: 'background_pirate',
name: 'Pirate'
}, {
id: 'background_ancient_temple',
name: 'Ancient Temple'
}];
// Grid layout parameters for 9 maps: 3 maps per row, 3 rows
var mapsPerRow = 3;
var totalRows = 4;
var mapWidth = 350;
var mapHeight = 280;
var spacingX = 360;
var spacingY = 450;
var startX = 2048 / 2 - spacingX * (mapsPerRow - 1) / 2;
var startY = 1000;
for (var i = 0; i < mapTypes.length; i++) {
var mapContainer = mapMenuContainer.addChild(new Container());
// Calculate grid position
var row = Math.floor(i / mapsPerRow);
var col = i % mapsPerRow;
// Position in grid
mapContainer.x = startX + col * spacingX;
mapContainer.y = startY + row * spacingY;
// Background preview with consistent size
var bgPreview = mapContainer.addChild(LK.getAsset(mapTypes[i].id, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: mapWidth / 2048,
scaleY: mapHeight / 2732
}));
// Map name with consistent text size
var mapNameText = new Text2(mapTypes[i].name, {
size: 80,
fill: 0xFFFFFF
});
mapNameText.anchor.set(0.5, 0.5);
mapNameText.x = 0;
mapNameText.y = 180;
// Add text shadow for better contrast
var mapNameShadow = new Text2(mapTypes[i].name, {
size: 80,
fill: 0x000000
});
mapNameShadow.anchor.set(0.5, 0.5);
mapNameShadow.x = 2;
mapNameShadow.y = 182;
mapContainer.addChild(mapNameShadow);
mapContainer.addChild(mapNameText);
// Highlight selected map
if (mapTypes[i].id === currentBackground) {
bgPreview.tint = 0x90ee90;
}
// Make clickable with consistent hit area
mapContainer.down = function (mapType) {
return function () {
LK.getSound('Lever').play();
changeBackground(mapType.id);
hideMapMenu();
};
}(mapTypes[i]);
// Set consistent hit area for better touch interaction
mapContainer.width = mapWidth;
mapContainer.height = mapHeight + 100; // Extra height for text
}
// Back button
var backButton = mapMenuContainer.addChild(new MenuButton(getText('back'), function () {
hideMapMenu();
}));
backButton.x = 2048 / 2;
backButton.y = 2400;
}
function hideMapMenu() {
if (mapMenuContainer) {
mapMenuContainer.destroy();
mapMenuContainer = null;
}
if (menuContainer) {
menuContainer.visible = true;
}
showMenuOverlay();
}
function playBackgroundMusic() {
var musicTrack = null;
if (gameState === 'menu') {
if (musicEnabled) {
musicTrack = 'Menus';
}
} else if (gameState === 'playing') {
// During gameplay, play music if music is enabled, otherwise play ambience if ambience is enabled
if (musicEnabled) {
if (currentBackground === 'background_beach') {
musicTrack = 'MusicBeach';
} else if (currentBackground === 'background_jungle') {
musicTrack = 'MusicJungle';
} else if (currentBackground === 'background_volcano') {
musicTrack = 'MusicLava';
} else if (currentBackground === 'background_space') {
musicTrack = 'MusicSpace';
} else if (currentBackground === 'background_swamp') {
musicTrack = 'MusicSwamp';
} else if (currentBackground === 'background_city') {
musicTrack = 'City';
} else if (currentBackground === 'background_pirate') {
musicTrack = 'Pirate';
} else if (currentBackground === 'background_ancient_temple') {
musicTrack = 'Ancient_Temple';
}
} else if (ambienceEnabled) {
// Play ambience tracks for gameplay even when music is disabled
if (currentBackground === 'background_beach') {
musicTrack = 'Beach';
} else if (currentBackground === 'background_jungle') {
musicTrack = 'Jungle';
} else if (currentBackground === 'background_volcano') {
musicTrack = 'Volcano';
} else if (currentBackground === 'background_space') {
musicTrack = 'Space';
} else if (currentBackground === 'background_swamp') {
musicTrack = 'Swamp';
} else if (currentBackground === 'background_city') {
musicTrack = 'City';
} else if (currentBackground === 'background_silhouette_mountain') {
musicTrack = 'SilhouetteMountain';
} else if (currentBackground === 'background_pirate') {
musicTrack = 'Beach';
} else if (currentBackground === 'background_ancient_temple') {
musicTrack = 'Ancient_Temple';
}
}
}
if (musicTrack && musicTrack !== currentMusicTrack) {
LK.playMusic(musicTrack);
currentMusicTrack = musicTrack;
}
}
function changeBackground(newBackground) {
// Clean up existing background elements
for (var i = 0; i < backgroundElements.length; i++) {
backgroundElements[i].destroy();
}
backgroundElements = [];
currentBackground = newBackground;
storage.selectedBackground = newBackground;
// Recreate scrolling background with new background
for (var i = 0; i < 2; i++) {
var bg = game.addChild(LK.getAsset(currentBackground, {
anchorX: 0,
anchorY: 0,
x: i * 2048,
y: 0
}));
backgroundElements.push(bg);
// Move background to back
game.setChildIndex(bg, 0);
}
// Update ground asset
if (ground) {
ground.destroy();
ground = game.addChild(createGround());
}
// Update turtle appearance for silhouette mountain
if (turtle && turtle.graphics) {
if (currentBackground === 'background_silhouette_mountain' && turtle.originalSkin !== 'turtle_golden') {
turtle.graphics.tint = 0x000000;
} else {
turtle.graphics.tint = 0xffffff;
}
}
// Play appropriate background music only during gameplay
if (gameState === 'playing') {
playBackgroundMusic();
}
}
function startGame() {
gameState = 'playing';
gameScore = 0;
lastBoulderTime = 0;
boulderInterval = 200; // much more frequent spawning
// Use selected background for gameplay
changeBackground(currentBackground);
// Recreate ground with correct asset
if (ground) {
ground.destroy();
ground = game.addChild(createGround());
}
// Hide menu and overlay
if (menuContainer) {
menuContainer.visible = false;
}
hideMenuOverlay();
// Create turtle
turtle = game.addChild(new Turtle());
turtle.setSkin(currentSkin);
turtle.x = 300;
// Lower turtle into ground in jungle map to simulate being stuck in mud
if (currentBackground === 'background_jungle') {
turtle.y = ground.y + 30; // Lower turtle by 30 pixels into the ground
turtle.groundY = ground.y + 30;
} else {
turtle.y = ground.y;
turtle.groundY = ground.y;
}
// Clean up obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
}
obstacles = [];
// Clean up palm trees array for backwards compatibility
for (var i = palmTrees.length - 1; i >= 0; i--) {
palmTrees[i].destroy();
}
palmTrees = [];
// Clean up turtle backs
for (var i = turtleBacks.length - 1; i >= 0; i--) {
turtleBacks[i].destroy();
}
turtleBacks = [];
// Clear existing turtle backs
for (var i = turtleBacks.length - 1; i >= 0; i--) {
turtleBacks[i].destroy();
}
turtleBacks = [];
// Save current score and update display with color changes
storage.currentScore = gameScore;
scoreText.setText(gameScore);
// Change score text color based on thresholds
if (gameScore >= 40) {
// Special color for 40+ (rainbow effect using tween)
scoreText.fill = 0xff00ff; // Magenta
tween(scoreText, {
tint: 0x00ffff
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(scoreText, {
tint: 0xffff00
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(scoreText, {
tint: 0xff00ff
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}
});
} else if (gameScore >= 25) {
// Gold
scoreText.fill = 0xffd700;
scoreText.tint = 0xffd700;
} else if (gameScore >= 20) {
// Silver
scoreText.fill = 0xc0c0c0;
scoreText.tint = 0xc0c0c0;
} else if (gameScore >= 10) {
// Bronze
scoreText.fill = 0xcd7f32;
scoreText.tint = 0xcd7f32;
} else {
// Default white
scoreText.fill = 0xffffff;
scoreText.tint = 0xffffff;
}
// Start background music
playBackgroundMusic();
}
function gameOver() {
actualGameOver();
}
function actualGameOver() {
gameState = 'gameOver';
gamePaused = true; // Stop all movement immediately
// Update high score
if (gameScore > (storage.highScore || 0)) {
storage.highScore = gameScore;
}
// Note: Specific hit sounds are now played on collision detection
// Flash screen turquoise for laser wall hits, red for others, but skip for carnivorous plant
var shouldFlash = true;
var flashColor = 0xff0000; // Default red
// Check collision types
for (var i = 0; i < obstacles.length; i++) {
if (turtle && turtle.intersects(obstacles[i])) {
if (obstacles[i] instanceof LaserWall) {
flashColor = 0x40e0d0; // Turquoise for laser wall collision
break;
} else if (obstacles[i] instanceof CarnivorousPlant) {
shouldFlash = false; // Don't flash for carnivorous plant - it has its own effect
break;
}
}
}
// Only flash if not eaten by carnivorous plant
if (shouldFlash) {
LK.effects.flashScreen(flashColor, 500);
}
// Create dark overlay and animate it
createDarkOverlay();
tween(darkOverlay, {
alpha: 1.0
}, {
duration: 1000,
easing: tween.easeInOut
});
// Show game over after delay - wait for hit sound to complete
LK.setTimeout(function () {
LK.showGameOver('Score: ' + gameScore);
// Reset everything after game over is shown
resetGame();
}, 1500);
}
function resetGame() {
gameState = 'menu';
gamePaused = false; // Reset pause state
// Keep dark overlay - don't clean it up until retry is pressed
// if (darkOverlay) {
// darkOverlay.destroy();
// darkOverlay = null;
// }
// Stop background music when leaving gameplay
LK.stopMusic();
currentMusicTrack = null;
// Reset game variables
gameScore = 0;
lastBoulderTime = 0;
boulderInterval = 200;
difficultyLevel = 1;
speedMultiplier = 1.0;
// Clean up turtle
if (turtle) {
turtle.destroy();
turtle = null;
}
// Clean up obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
}
obstacles = [];
// Clean up palm trees array for backwards compatibility
for (var i = palmTrees.length - 1; i >= 0; i--) {
palmTrees[i].destroy();
}
palmTrees = [];
// Reset score display and color
scoreText.setText('0');
scoreText.fill = 0xffffff; // Reset to white
scoreText.tint = 0xffffff;
// Stop any ongoing color tweens
tween.stop(scoreText, {
tint: true
});
// Show menu
if (menuContainer) {
menuContainer.visible = true;
showMenuOverlay();
} else {
createMainMenu();
}
// Play menu music when returning to menu
playBackgroundMusic();
}
function showAchievementNotification(achievementName) {
// Create achievement container if it doesn't exist
if (achievementContainer) {
achievementContainer.destroy();
}
achievementContainer = game.addChild(new Container());
// Create white text box background
var textBox = achievementContainer.addChild(LK.getAsset('background_sky', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 15,
scaleY: 2,
tint: 0xffffff,
alpha: 0.9
}));
// Create trophy icon
var trophyIcon = achievementContainer.addChild(LK.getAsset('HighscoreButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
x: -200,
y: 0
}));
// Create achievement text
var achievementText = new Text2(achievementName, {
size: 120,
fill: 0x000000
});
achievementText.anchor.set(0.5, 0.5);
achievementText.x = 50; // Move text slightly right to make room for trophy
achievementContainer.addChild(achievementText);
// Position at bottom of screen initially
achievementContainer.x = 2048 / 2;
achievementContainer.y = 2732 + 100; // Start below screen
// Animation sequence: fly up to bottom, stay briefly, then fly down
tween(achievementContainer, {
y: 2732 - 200 // Fly to bottom area of screen
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Stay at bottom for a moment
tween(achievementContainer, {
y: 2732 - 200 // Stay in place
}, {
duration: 1500,
onFinish: function onFinish() {
// Fly back down off screen
tween(achievementContainer, {
y: 2732 + 100
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
if (achievementContainer) {
achievementContainer.destroy();
achievementContainer = null;
}
}
});
}
});
}
});
}
function createDustParticles(x, y) {
for (var i = 0; i < 8; i++) {
var particle = game.addChild(LK.getAsset('sandParticle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5 + Math.random() * 0.8,
scaleY: 0.5 + Math.random() * 0.8,
x: x + (Math.random() - 0.5) * 120,
y: y - 10,
alpha: 0.8
}));
// Create flying sand effect with arc trajectory
var targetY = y - Math.random() * 150 - 80;
var targetX = particle.x + (Math.random() - 0.5) * 200;
var midY = y - Math.random() * 80 - 40;
// First phase: launch upward with arc
tween(particle, {
y: midY,
x: targetX * 0.6,
alpha: 0.9
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second phase: fall down with gravity
tween(particle, {
y: targetY,
x: targetX,
alpha: 0
}, {
duration: 600,
easing: tween.easeIn,
onFinish: function onFinish() {
particle.destroy();
}
});
}
});
}
}
// Touch input for jumping
game.down = function (x, y, obj) {
if (gameState === 'playing' && turtle && !gamePaused) {
turtle.jump();
}
};
// Handle retry button press to clear dark overlay
LK.on('retry', function () {
// Clear dark overlay when retry is pressed
if (darkOverlay) {
darkOverlay.destroy();
darkOverlay = null;
}
});
// Main game loop
game.update = function () {
// Don't update anything if game is paused
if (gamePaused) return;
// Update scrolling background only when playing
if (gameState === 'playing') {
for (var i = 0; i < backgroundElements.length; i++) {
var bg = backgroundElements[i];
bg.x += backgroundScrollSpeed;
// Reset position when background moves off screen
if (bg.x <= -2048) {
// Find the other background element and position relative to it
var otherBg = backgroundElements[1 - i];
bg.x = otherBg.x + 2048;
}
}
}
if (gameState === 'playing') {
// Spawn obstacles based on current map
if (LK.ticks - lastBoulderTime > boulderInterval) {
var obstacle = null;
if (currentBackground === 'background_beach') {
obstacle = game.addChild(new PalmTree());
} else if (currentBackground === 'background_jungle') {
obstacle = game.addChild(new JungleTreeTrunk());
} else if (currentBackground === 'background_volcano') {
obstacle = game.addChild(new LavaGeyser());
} else if (currentBackground === 'background_space') {
// Randomly spawn laser walls for space map
var laserWallTypes = ['laser_wall', 'laser_wall_2', 'laser_wall_3', 'laser_wall_4'];
var randomLaserType = laserWallTypes[Math.floor(Math.random() * laserWallTypes.length)];
obstacle = game.addChild(new LaserWall(randomLaserType));
} else if (currentBackground === 'background_swamp') {
obstacle = game.addChild(new CarnivorousPlant());
} else if (currentBackground === 'background_city') {
obstacle = game.addChild(new Car());
} else if (currentBackground === 'background_silhouette_mountain') {
obstacle = game.addChild(new Dripstone());
} else if (currentBackground === 'background_pirate') {
obstacle = game.addChild(new Pirate());
} else if (currentBackground === 'background_ancient_temple') {
obstacle = game.addChild(new ArrowTrap());
}
if (obstacle) {
obstacle.x = 2048 + 300; // Spawn further off-screen to hide spawning
obstacle.y = ground.y;
obstacle.speed = obstacle.speed * speedMultiplier; // Apply speed multiplier
obstacles.push(obstacle);
// Spawn pillar for ancient temple (ensuring no crossing with obstacles)
if (currentBackground === 'background_ancient_temple' && Math.random() < 0.6) {
var pillar = game.addChild(new Pillar());
// Position pillar at safe distance from obstacle
pillar.x = obstacle.x + 800 + Math.random() * 400; // 800-1200 pixels away from obstacle
pillar.y = ground.y;
pillar.speed = pillar.speed * speedMultiplier;
obstacles.push(pillar); // Add to obstacles array for movement but not collision
}
}
lastBoulderTime = LK.ticks;
// Progressive difficulty system with speed cap at score 20
var newDifficultyLevel = Math.floor(gameScore / 5) + 1;
if (newDifficultyLevel > difficultyLevel) {
difficultyLevel = newDifficultyLevel;
// Cap speed multiplier at 1.0 after score 20 (no speed increase)
if (gameScore >= 20) {
speedMultiplier = 1.0; // No speed increase after score 20
} else {
speedMultiplier = Math.min(1.0, 1.0 + (difficultyLevel - 1) * 0.2); // 20% speed increase per level until score 20
}
// Play celebrate sound when difficulty increases
LK.getSound('celebrate').play();
}
// Increase spawn rate more moderately
if (boulderInterval > minBoulderInterval) {
var reduction = Math.max(5, Math.floor(difficultyLevel * 2)); // More moderate reduction
boulderInterval = Math.max(minBoulderInterval, boulderInterval - reduction);
}
}
// Move pillars to front for foreground rendering
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i] instanceof Pillar) {
// Move pillar to front (but below ground)
var groundIndex = game.getChildIndex(ground);
var pillarIndex = game.getChildIndex(obstacles[i]);
if (pillarIndex < groundIndex) {
game.setChildIndex(obstacles[i], groundIndex - 1);
}
}
}
// Update obstacles and check collisions
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
// Remove off-screen obstacles (hide despawning)
if (obstacle.x < -300) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check if obstacle was passed for scoring (skip pillars as they don't count)
if (!obstacle.passed && obstacle.x < turtle.x && !(obstacle instanceof Pillar)) {
obstacle.passed = true;
gameScore++;
// Save current score and update display with color changes
storage.currentScore = gameScore;
scoreText.setText(gameScore);
// Change score text color based on thresholds
if (gameScore >= 40) {
// Special color for 40+ (rainbow effect using tween)
scoreText.fill = 0xff00ff; // Magenta
tween(scoreText, {
tint: 0x00ffff
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(scoreText, {
tint: 0xffff00
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(scoreText, {
tint: 0xff00ff
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}
});
// Play special trophy sound for reaching 40 if this is the first time
if (gameScore == 40 && (storage.highScore || 0) < 40) {
LK.getSound('trophy_gold').play();
showAchievementNotification("Master Rank");
}
} else if (gameScore >= 25) {
// Gold
scoreText.fill = 0xffd700;
scoreText.tint = 0xffd700;
// Play gold trophy sound for reaching 25 if this is the first time
if (gameScore == 25 && (storage.highScore || 0) < 25) {
LK.getSound('trophy_gold').play();
// Unlock golden turtle skin permanently
storage.goldenTurtleUnlocked = true;
// Flash screen golden and pause game briefly
LK.effects.flashScreen(0xffd700, 1000);
// Show achievement notification
showAchievementNotification("Gold Rank");
// Pause game for 1 second
var gameWasPaused = false;
LK.setTimeout(function () {
gameWasPaused = true;
}, 1000);
}
} else if (gameScore >= 20) {
// Silver
scoreText.fill = 0xc0c0c0;
scoreText.tint = 0xc0c0c0;
// Play silver trophy sound for reaching 20 if this is the first time
if (gameScore == 20 && (storage.highScore || 0) < 20) {
LK.getSound('trophy_silver').play();
showAchievementNotification("Silver Rank");
}
} else if (gameScore >= 10) {
// Bronze
scoreText.fill = 0xcd7f32;
scoreText.tint = 0xcd7f32;
// Play bronze trophy sound for reaching 10 if this is the first time
if (gameScore == 10 && (storage.highScore || 0) < 10) {
LK.getSound('trophy_bronze').play();
showAchievementNotification("Bronze Rank");
}
} else {
// Default white
scoreText.fill = 0xffffff;
scoreText.tint = 0xffffff;
}
// Play celebration sound for scoring (but not when ranking up)
var isRankingUp = gameScore == 10 && (storage.highScore || 0) < 10 || gameScore == 20 && (storage.highScore || 0) < 20 || gameScore == 25 && (storage.highScore || 0) < 25 || gameScore == 40 && (storage.highScore || 0) < 40;
if (!isRankingUp) {
LK.getSound('Secret').play();
}
}
// Check collision with turtle (skip collision for pillars as they are foreground elements)
if (turtle && turtle.intersects(obstacle) && !(obstacle instanceof Pillar)) {
// Play specific hit sound based on obstacle type
if (obstacle instanceof PalmTree) {
LK.getSound('hit_palm_tree').play();
} else if (obstacle instanceof JungleTreeTrunk) {
LK.getSound('hit_palm_tree').play();
} else if (obstacle instanceof LavaGeyser) {
LK.getSound('hit_lava_geyser').play();
// Create pile of ash at turtle's position
var ash = game.addChild(new PileOfAsh());
ash.x = turtle.x;
ash.y = turtle.y;
// Hide the turtle
turtle.visible = false;
} else if (obstacle instanceof LaserWall) {
LK.getSound('hit_laser_wall').play();
// Create pile of ash at turtle's position (like lava geyser)
var ash = game.addChild(new PileOfAsh());
ash.x = turtle.x;
ash.y = turtle.y;
// Hide the turtle immediately
turtle.visible = false;
// Add a slight delay before game over to show the ash transformation
LK.setTimeout(function () {
gameOver();
}, 100);
return;
} else if (obstacle instanceof Car) {
LK.getSound('hit_car').play();
} else if (obstacle instanceof Dripstone) {
LK.getSound('hit_dripstone').play();
} else if (obstacle instanceof Pirate) {
LK.getSound('hit_pirate').play();
} else if (obstacle instanceof ArrowTrap) {
LK.getSound('hit_arrow_trap').play();
} else if (obstacle instanceof CarnivorousPlant) {
LK.getSound('hit_carnivorous_plant').play();
// Create dark overlay for carnivorous plant eating effect
createDarkOverlay();
// Start with green tint and gradually darken
darkOverlay.tint = 0x00ff00; // Start green
darkOverlay.alpha = 0.3; // Start slightly visible
// First phase: slowly turn green
tween(darkOverlay, {
alpha: 0.6,
tint: 0x00cc00 // Darker green
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Second phase: get darker green
tween(darkOverlay, {
alpha: 0.8,
tint: 0x006600 // Even darker green
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Third phase: very dark green
tween(darkOverlay, {
alpha: 0.95,
tint: 0x003300 // Very dark green
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Final phase: almost black
tween(darkOverlay, {
alpha: 1.0,
tint: 0x001100 // Almost black green
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Trigger game over after the eating animation
gameOver();
}
});
}
});
}
});
}
});
return; // Don't call gameOver immediately
}
gameOver();
return;
}
}
}
};
// Initialize main menu
createMainMenu();
// Initialize background music
playBackgroundMusic(); ===================================================================
--- original.js
+++ change.js
@@ -202,78 +202,8 @@
}
};
return self;
});
-var GlassPane = Container.expand(function () {
- var self = Container.call(this);
- self.speed = -24;
- self.passed = false;
- self.shatterTimer = 0;
- self.isShaking = false;
- self.shakeIntensity = 1;
- // Create glass pane graphics with standardized size
- var glassGraphics = self.attachAsset('glass_pane', {
- anchorX: 0.5,
- anchorY: 1.0,
- scaleX: 3.0,
- scaleY: 1.5
- });
- // Store graphics reference
- self.glassGraphics = glassGraphics;
- // Set standardized obstacle dimensions - 300x600 pixels
- self.width = 300;
- self.height = 600;
- self.update = function () {
- self.x += self.speed;
- // Add subtle glass shimmer effect
- self.shatterTimer += 0.03;
- if (self.isShaking) {
- // Intense shaking before breaking
- self.y += (Math.random() - 0.5) * self.shakeIntensity * 4;
- self.glassGraphics.rotation = (Math.random() - 0.5) * self.shakeIntensity * 0.1;
- self.glassGraphics.alpha = 0.7 + Math.random() * 0.3;
- self.shakeIntensity += 0.2;
- } else {
- // Gentle shimmer when not breaking
- self.glassGraphics.alpha = 0.85 + Math.sin(self.shatterTimer * 2) * 0.15;
- self.y += Math.sin(self.shatterTimer) * 0.2;
- }
- };
- // Method to trigger shattering animation
- self.shatter = function () {
- if (!self.isShaking) {
- self.isShaking = true;
- self.shakeIntensity = 1;
- // Create glass shards effect
- for (var i = 0; i < 12; i++) {
- var shard = game.addChild(LK.getAsset('glass_pane', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: self.x + (Math.random() - 0.5) * 100,
- y: self.y + (Math.random() - 0.5) * 100,
- scaleX: 0.3 + Math.random() * 0.4,
- scaleY: 0.3 + Math.random() * 0.4,
- alpha: 0.8
- }));
- var velocityX = (Math.random() - 0.5) * 15;
- var velocityY = Math.random() * -8 - 5;
- var gravity = 0.8;
- var rotation = (Math.random() - 0.5) * 0.3;
- shard.update = function () {
- this.x += velocityX;
- this.y += velocityY;
- this.rotation += rotation;
- this.alpha -= 0.02;
- velocityY += gravity;
- if (this.alpha <= 0 || this.y > 2732) {
- this.destroy();
- }
- };
- }
- }
- };
- return self;
-});
var JungleTreeTrunk = Container.expand(function () {
var self = Container.call(this);
self.speed = -24;
self.passed = false;
@@ -296,15 +226,8 @@
self.trunkGraphics.rotation = Math.sin(LK.ticks * 0.02) * 0.03;
};
return self;
});
-var LaboratoryLandingSoundEffect = Container.expand(function () {
- var self = Container.call(this);
- self.play = function () {
- LK.getSound('landing_laboratory').play();
- };
- return self;
-});
var LanguageButton = Container.expand(function (language, callback) {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('menuButton', {
anchorX: 0.5,
@@ -706,9 +629,9 @@
if (gameState === 'playing' && !hasJumpedOnMap[currentBackground]) {
hasJumpedOnMap[currentBackground] = true;
// Check if all maps have been jumped on
var allMapsJumped = true;
- var mapKeys = ['background_beach', 'background_jungle', 'background_volcano', 'background_space', 'background_swamp', 'background_city', 'background_silhouette_mountain', 'background_pirate', 'background_ancient_temple', 'background_laboratory'];
+ var mapKeys = ['background_beach', 'background_jungle', 'background_volcano', 'background_space', 'background_swamp', 'background_city', 'background_silhouette_mountain', 'background_pirate', 'background_ancient_temple'];
for (var i = 0; i < mapKeys.length; i++) {
if (!hasJumpedOnMap[mapKeys[i]]) {
allMapsJumped = false;
break;
@@ -776,13 +699,8 @@
} else if (currentBackground === 'background_pirate') {
landingSound = 'pirate_landing';
} else if (currentBackground === 'background_ancient_temple') {
landingSound = 'landing_ancient_temple';
- } else if (currentBackground === 'background_laboratory') {
- // Use LaboratoryLandingSoundEffect for laboratory map
- var laboratorySound = new LaboratoryLandingSoundEffect();
- laboratorySound.play();
- return; // Early return to avoid playing the default landing sound
}
LK.getSound(landingSound).play();
// Create dust particles
createDustParticles(self.x, self.y);
@@ -910,10 +828,9 @@
'background_swamp': false,
'background_city': false,
'background_silhouette_mountain': false,
'background_pirate': false,
- 'background_ancient_temple': false,
- 'background_laboratory': false
+ 'background_ancient_temple': false
};
// Menu elements
var menuContainer = null;
var skinMenuContainer = null;
@@ -998,10 +915,8 @@
} else if (currentBackground === 'background_pirate') {
groundAsset = 'ground_pirate';
} else if (currentBackground === 'background_ancient_temple') {
groundAsset = 'ground_ancient_temple';
- } else if (currentBackground === 'background_laboratory') {
- groundAsset = 'ground_laboratory';
}
return LK.getAsset(groundAsset, {
anchorX: 0,
anchorY: 0,
@@ -1125,10 +1040,8 @@
} else if (currentBackground === 'background_pirate') {
currentMapName = 'Pirate';
} else if (currentBackground === 'background_ancient_temple') {
currentMapName = 'Ancient Temple';
- } else if (currentBackground === 'background_laboratory') {
- currentMapName = 'Laboratory';
}
// Current map preview
var currentMapPreview = currentMapContainer.addChild(LK.getAsset(currentBackground, {
anchorX: 0.5,
@@ -1836,13 +1749,10 @@
name: 'Pirate'
}, {
id: 'background_ancient_temple',
name: 'Ancient Temple'
- }, {
- id: 'background_laboratory',
- name: 'Laboratory'
}];
- // Grid layout parameters for 10 maps: 3 maps per row, 4 rows (with 1 map in last row)
+ // Grid layout parameters for 9 maps: 3 maps per row, 3 rows
var mapsPerRow = 3;
var totalRows = 4;
var mapWidth = 350;
var mapHeight = 280;
@@ -1940,10 +1850,8 @@
} else if (currentBackground === 'background_pirate') {
musicTrack = 'Pirate';
} else if (currentBackground === 'background_ancient_temple') {
musicTrack = 'Ancient_Temple';
- } else if (currentBackground === 'background_laboratory') {
- musicTrack = 'Laboratory';
}
} else if (ambienceEnabled) {
// Play ambience tracks for gameplay even when music is disabled
if (currentBackground === 'background_beach') {
@@ -1963,10 +1871,8 @@
} else if (currentBackground === 'background_pirate') {
musicTrack = 'Beach';
} else if (currentBackground === 'background_ancient_temple') {
musicTrack = 'Ancient_Temple';
- } else if (currentBackground === 'background_laboratory') {
- musicTrack = 'Laboratory';
}
}
}
if (musicTrack && musicTrack !== currentMusicTrack) {
@@ -2366,10 +2272,8 @@
} else if (currentBackground === 'background_pirate') {
obstacle = game.addChild(new Pirate());
} else if (currentBackground === 'background_ancient_temple') {
obstacle = game.addChild(new ArrowTrap());
- } else if (currentBackground === 'background_laboratory') {
- obstacle = game.addChild(new GlassPane());
}
if (obstacle) {
obstacle.x = 2048 + 300; // Spawn further off-screen to hide spawning
obstacle.y = ground.y;
@@ -2546,13 +2450,8 @@
} else if (obstacle instanceof Pirate) {
LK.getSound('hit_pirate').play();
} else if (obstacle instanceof ArrowTrap) {
LK.getSound('hit_arrow_trap').play();
- } else if (obstacle instanceof GlassPane) {
- LK.getSound('hit_glass_pane').play();
- LK.getSound('glass_break').play();
- // Trigger shattering animation
- obstacle.shatter();
} else if (obstacle instanceof CarnivorousPlant) {
LK.getSound('hit_carnivorous_plant').play();
// Create dark overlay for carnivorous plant eating effect
createDarkOverlay();
The sand ground of some sort of beach. In-Game asset. 2d. High contrast. No shadows
A small world map. In-Game asset. 2d. High contrast. No shadows
I land turtle with googly eyes wearing a suit and looking to the right. In-Game asset. 2d. High contrast. No shadows
Irregular old land turtle wearing a suit with a red tie in a jumping position. In-Game asset. 2d. High contrast. No shadows
A settings button I can without any background it’s a gear. In-Game asset. 2d. High contrast. No shadows
The beach of a tropical island, though the ocean is not to be seen in the background there is just a vibrant jungle. In-Game asset. 2d. High contrast. No shadows
A lush and vibrant jungle. In-Game asset. 2d. High contrast. No shadows
A area filled with lava and a sky two it’s still pretty vibrant but make sure it can loop without looking bad. In-Game asset. 2d. High contrast. No shadows
Junglee grass ground which is a bit overgrown. In-Game asset. 2d. High contrast. No shadows
Volcanic rocks for a ground. In-Game asset. 2d. High contrast. No shadows
I completely dark red square with some orange parts more in the middle representing a lava block. In-Game asset. 2d. High contrast. No shadows
A crazy almost neon green plant that can grow both on in the jungles and on beaches. In-Game asset. 2d. High contrast. No shadows
A golden land turtle with googly eyes looking to the right. In-Game asset. 2d. High contrast. No shadows
A golden turtle with googly eyes in the jumping position. In-Game asset. 2d. High contrast. No shadows
A pile of burn ash. In-Game asset. 2d. High contrast. No shadows
A trophy without any background. In-Game asset. 2d. High contrast. No shadows
A sterile space station background But it’s still futuristic and definitely seems like it’s made out of metal. In-Game asset. 2d. High contrast. No shadows
A metal plate working as the ground for a space station. In-Game asset. 2d. High contrast. No shadows
A swampy background with mangrove trees and overall overgrown but lush aesthetics. In-Game asset. 2d. High contrast. No shadows
A muddy swamp ground. In-Game asset. 2d. High contrast. No shadows
A background, this picture of a flash eating plant Not eating flesh at least not yet. In-Game asset. 2d. High contrast. No shadows
The street ground of a city. In-Game asset. 2d. High contrast. No shadows
A white car. In-Game asset. 2d. High contrast. No shadows
The skyline of a modern city. In-Game asset. 2d. High contrast. No shadows
Evening image of a mountain range easily looping. In-Game asset. 2d. High contrast. No shadows
The tropical ocean with the shore of an island in the far background easily looping itself. In-Game asset. 2d. High contrast. No shadows
A pirate. In-Game asset. 2d. High contrast. No shadows
The wooden floor of a pirate ship. In-Game asset. 2d. High contrast. No shadows
The pillar of an ancient jungle temple. In-Game asset. 2d. High contrast. No shadows
Ancient trap able to dispense arrows to PS it’s target. In-Game asset. 2d. High contrast. No shadows
The ground for an ancient jungle temple. In-Game asset. 2d. High contrast. No shadows
The background for the inside of an ancient jungle temple. In-Game asset. 2d. High contrast. No shadows
Fully soaked and wet grass and the dirt for the ground element oven to the jump and run. In-Game asset. 2d. High contrast. No shadows
Just a tree. In-Game asset. 2d. High contrast. No shadows
An open space of grass with dark rainy clouds up above Easily looper. In-Game asset. 2d. High contrast. No shadows
hit
Sound effect
jump
Sound effect
Map
Sound effect
Lever
Sound effect
Gear
Sound effect
Land
Sound effect
Secret
Sound effect
celebrate
Sound effect
Started
Sound effect
landing_jungle
Sound effect
landing_volcano
Sound effect
hit_lava_geyser
Sound effect
hit_palm_tree
Sound effect
trophy_bronze
Sound effect
trophy_gold
Sound effect
trophy_silver
Sound effect
Beach
Music
Jungle
Music
Volcano
Music
Warning
Sound effect
Alarm
Sound effect
Memories
Sound effect
Trophy
Sound effect
hit_laser_wall
Sound effect
Space
Music
landing_space
Sound effect
landing_swamp
Sound effect
hit_carnivorous_plant
Sound effect
Swamp
Music
City
Music
hit_car
Sound effect
landing_city
Sound effect
landing_silhouette_mountain
Sound effect
hit_dripstone
Sound effect
hit_pirate
Sound effect
pirate_landing
Sound effect
hit_arrow_trap
Sound effect
landing_ancient_temple
Sound effect
landing_thunderplanes
Sound effect
hit_thundertree
Sound effect
Thunderplanes
Music
thunder
Sound effect