/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BolaProjectile = Container.expand(function () {
var self = Container.call(this);
var bolaGraphics = self.attachAsset('bola', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -8; // Move upward
self.update = function () {
self.y += self.speed; // Move upward (negative Y)
};
return self;
});
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubbleGraphics = self.attachAsset('burbuja', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 2;
bubbleGraphics.alpha = 0.6 + Math.random() * 0.3;
var scale = 0.5 + Math.random() * 1.5;
bubbleGraphics.scaleX = scale;
bubbleGraphics.scaleY = scale;
// Add floating animation for bubbles
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.02 + self.floatOffset) * 0.5;
};
return self;
});
var Cloud = Container.expand(function () {
var self = Container.call(this);
var cloudGraphics = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 2;
cloudGraphics.alpha = 0.6 + Math.random() * 0.3;
var scale = 0.5 + Math.random() * 1.5;
cloudGraphics.scaleX = scale;
cloudGraphics.scaleY = scale * 0.6;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
// Add rotation animation
self.rotationSpeed = 0.1;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.05 + self.floatOffset) * 1.0;
// Add rotation
coinGraphics.rotation += self.rotationSpeed;
// Add subtle pulsing effect
var scale = 1.0 + Math.sin(LK.ticks * 0.08) * 0.1;
coinGraphics.scaleX = scale;
coinGraphics.scaleY = scale;
};
return self;
});
var EstepicursorObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('estepicursor', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed * 0.5; // Move slowly to the left
self.update = function () {
self.x -= self.speed;
};
return self;
});
var FlyingItem = Container.expand(function () {
var self = Container.call(this);
var itemGraphics = self.attachAsset('over', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
// Add glow effect
itemGraphics.tint = 0x00FFFF;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.05 + self.floatOffset) * 1.5;
// Add pulsing glow effect
itemGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.1) * 0.3;
};
return self;
});
var Ground = Container.expand(function () {
var self = Container.call(this);
var groundGraphics = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var HongoObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('hongo', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var NieveObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('nieve', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
// Start flip animation when obstacle is created
function startFlipAnimation() {
tween(obstacleGraphics, {
scaleX: -1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(obstacleGraphics, {
scaleX: 1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: startFlipAnimation
});
}
});
}
// Start the flip animation
startFlipAnimation();
return self;
});
var Obstacle3 = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle3', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var PaletaObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('paleta', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Planet = Container.expand(function () {
var self = Container.call(this);
var planetGraphics = self.attachAsset('planeta', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 2;
planetGraphics.alpha = 0.8 + Math.random() * 0.2;
var scale = 0.8 + Math.random() * 1.2;
planetGraphics.scaleX = scale;
planetGraphics.scaleY = scale;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset(currentSkin, {
anchorX: 0.5,
anchorY: 1
});
self.velocityY = 0;
self.isOnGround = false;
self.jumpPower = -40;
self.gravity = 1.2;
self.jumpCount = 0;
self.maxJumps = 2;
self.jump = function () {
if (self.jumpCount < self.maxJumps) {
self.velocityY = self.jumpPower;
self.isOnGround = false;
self.jumpCount++;
LK.getSound('jump').play();
}
};
self.update = function () {
if (isFlying) {
// Flying mode: gentle hovering motion, no gravity
self.y = 800 + Math.sin(LK.ticks * 0.08) * 50;
self.velocityY = 0;
self.isOnGround = false;
self.jumpCount = 0;
// Flying animation
playerGraphics.rotation = Math.sin(LK.ticks * 0.2) * 0.05;
// Add flying glow effect
playerGraphics.tint = 0x00FFFF;
} else if (isInvincible) {
// Invincible mode: normal gravity but golden glow
playerGraphics.tint = 0xFFD700;
self.velocityY += self.gravity;
self.y += self.velocityY;
// Check ground collision
if (self.y >= groundY) {
self.y = groundY;
self.velocityY = 0;
self.isOnGround = true;
self.jumpCount = 0;
}
// Animate running with glow pulse
playerGraphics.rotation = Math.sin(LK.ticks * 0.3) * 0.1;
playerGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.2) * 0.3;
} else {
// Normal mode: apply gravity
playerGraphics.tint = 0xFFFFFF;
playerGraphics.alpha = 1.0;
self.velocityY += self.gravity;
self.y += self.velocityY;
// Check ground collision
if (self.y >= groundY) {
self.y = groundY;
self.velocityY = 0;
self.isOnGround = true;
self.jumpCount = 0;
}
// Animate running
playerGraphics.rotation = Math.sin(LK.ticks * 0.3) * 0.1;
}
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
var portalGraphics = self.attachAsset('portal', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
self.used = false;
self.update = function () {
self.x -= self.speed;
// Portal animation
portalGraphics.rotation += 0.1;
portalGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.15) * 0.3;
};
return self;
});
var PosteObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('poste', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var ShieldItem = Container.expand(function () {
var self = Container.call(this);
var itemGraphics = self.attachAsset('escudo', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
// Add glow effect
itemGraphics.tint = 0xFFD700;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.05 + self.floatOffset) * 1.5;
// Add pulsing glow effect
itemGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.1) * 0.3;
};
return self;
});
var SkyObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle2', {
anchorX: 0.5,
anchorY: 0.5
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height * 0.5; // Center the hitbox
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Sun = Container.expand(function () {
var self = Container.call(this);
var sunGraphics = self.attachAsset('sol', {
anchorX: 0.5,
anchorY: 0.5
});
// Sun doesn't move, it's static
sunGraphics.alpha = 0.9;
var scale = 2.0; // Make sun larger
sunGraphics.scaleX = scale;
sunGraphics.scaleY = scale;
// Add gentle glow animation
self.glowOffset = 0;
self.update = function () {
// Add gentle pulsing glow effect
sunGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.03 + self.glowOffset) * 0.1;
};
return self;
});
var UlcesObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('ulces', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var VolcanObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('volcan', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.shootTimer = 0;
self.update = function () {
self.x -= self.speed;
// Shoot bola projectiles upward
self.shootTimer++;
if (self.shootTimer > 60) {
// Shoot every second (60 frames)
var bola = new BolaProjectile();
bola.x = self.x;
bola.y = self.y - 50; // Start above the volcano
volcanoProjectiles.push(bola);
game.addChild(bola);
self.shootTimer = 0;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var gameSpeed = 8;
var groundY = 2732 - 200;
var player;
var obstacles = [];
var portals = [];
var groundTiles = [];
var distance = 0;
var currentWorld = 0;
var worldTransitioning = false;
var clouds = [];
var planets = [];
var skyObstacles = [];
var bubbles = [];
var volcanoProjectiles = [];
var coins = [];
var coinCount = storage.coinCount || 0;
var flyingItems = [];
var isFlying = false;
var flyingTimer = 0;
var flyingDuration = 600; // 10 seconds at 60 FPS
var shieldItems = [];
var isInvincible = false;
var invincibleTimer = 0;
var invincibleDuration = 600; // 10 seconds at 60 FPS
var lastItemType = 'flying'; // Track last spawned item type for alternating
var currentPortalCount = 0; // Track current portal count
var sun = null; // Static sun for world 4
var worldCounter = 0;
var waterEffect = null;
var playerLives = 3;
var maxLives = 3;
var hearts = [];
var isInvulnerable = false;
var invulnerabilityTime = 2000; // 2 seconds
// Game state variables
var gameState = 'menu'; // 'menu', 'playing', 'shop', or 'practice'
var menuContainer = null;
var shopContainer = null;
var practiceContainer = null;
var isPracticeMode = false;
var practiceStartWorld = 0;
// Shop and skin variables
var currentSkin = storage.currentSkin || 'player';
var ownedSkins = storage.ownedSkins || ['player']; // Player skin is free by default
var skinPrices = {
'player': 0,
'skin': 50,
'skin2': 50,
'skin3': 50,
'skin4': 50,
'skin5': 50,
'skin6': 50,
'skin8': 75,
'skin9': 75,
'skin10': 100,
'skin11': 100,
'skin12': 125
};
// Record tracking
var bestDistance = storage.bestDistance || 0;
var bestWorlds = storage.bestWorlds || 0;
// World configurations
var worlds = [{
bg: 0x87CEEB,
ground: 0x4A90E2,
obstacle: 0xFF5722
},
// Sky world
{
bg: 0x1A237E,
ground: 0x424242,
obstacle: 0xFF9800
},
// Space world
{
bg: 0x006064,
ground: 0x00838F,
obstacle: 0x4CAF50
},
// Underwater world
{
bg: 0xFF6F00,
ground: 0xD84315,
obstacle: 0x795548
},
// Desert world
{
bg: 0xE3F2FD,
ground: 0x90A4AE,
obstacle: 0x607D8B
},
// Ice world
{
bg: 0x3E2723,
ground: 0xFF5722,
obstacle: 0x212121
},
// Lava world
{
bg: 0xFFB6C1,
ground: 0xFF69B4,
obstacle: 0xFF1493
},
// Candy land world
{
bg: 0x228B22,
ground: 0x8B4513,
obstacle: 0x654321
},
// Mushroom world
{
bg: 0x696969,
ground: 0x2F4F4F,
obstacle: 0x778899
} // City world
];
// UI
var distanceText = new Text2('Distance: 0', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
distanceText.anchor.set(0, 0);
distanceText.x = 50;
distanceText.y = 50;
distanceText.visible = false; // Hide initially
LK.gui.topLeft.addChild(distanceText);
var worldText = new Text2('Sky World', {
size: 50,
fill: 0xFFFFFF
});
worldText.anchor.set(0.5, 0);
worldText.visible = false; // Hide initially
LK.gui.top.addChild(worldText);
var worldTitleText = new Text2('', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
worldTitleText.anchor.set(0.5, 0.5);
worldTitleText.x = 1024;
worldTitleText.y = 1366;
worldTitleText.visible = false;
LK.gui.center.addChild(worldTitleText);
function showWorldTitle(worldName) {
worldTitleText.setText(worldName);
worldTitleText.visible = true;
worldTitleText.alpha = 0;
worldTitleText.scaleX = 0.5;
worldTitleText.scaleY = 0.5;
tween(worldTitleText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(worldTitleText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
worldTitleText.visible = false;
}
});
}, 2000);
}
});
}
var worldCounterText = new Text2('Mundos: 0', {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 3,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
worldCounterText.anchor.set(1, 0);
worldCounterText.x = -50;
worldCounterText.y = 50;
worldCounterText.visible = false; // Hide initially
LK.gui.topRight.addChild(worldCounterText);
var coinCounterText = new Text2('Coins: ' + coinCount, {
size: 50,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 3,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
coinCounterText.anchor.set(1, 0);
coinCounterText.x = -50;
coinCounterText.y = 120;
coinCounterText.visible = false; // Hide initially
LK.gui.topRight.addChild(coinCounterText);
// Initialize player
player = game.addChild(new Player());
player.x = 300;
player.y = groundY;
player.visible = false; // Hide player initially
// Create hearts UI
function createHeartsUI() {
// Clear existing hearts
for (var i = 0; i < hearts.length; i++) {
hearts[i].destroy();
}
hearts = [];
// Create heart containers
for (var i = 0; i < maxLives; i++) {
var heartContainer = new Container();
var heart = heartContainer.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
// Position hearts horizontally
heartContainer.x = 50 + i * 100;
heartContainer.y = 150;
// Show/hide based on current lives
if (i < playerLives) {
heart.alpha = 1.0;
heart.tint = 0xFF0000; // Red for active hearts
} else {
heart.alpha = 0.3;
heart.tint = 0x666666; // Gray for lost hearts
}
LK.gui.topLeft.addChild(heartContainer);
hearts.push(heartContainer);
}
}
// Update hearts display
function updateHeartsUI() {
for (var i = 0; i < hearts.length; i++) {
var heart = hearts[i].children[0];
if (i < playerLives) {
heart.alpha = 1.0;
heart.tint = 0xFF0000; // Red for active hearts
} else {
heart.alpha = 0.3;
heart.tint = 0x666666; // Gray for lost hearts
}
}
}
// Lose a life function
function loseLife() {
if (isInvulnerable || playerLives <= 0) return;
playerLives--;
updateHeartsUI();
// Make player invulnerable temporarily
isInvulnerable = true;
// Play pain sound
LK.getSound('pain').play();
// Flash player red to indicate damage
tween(player.children[0], {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(player.children[0], {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
// Remove invulnerability after set time
LK.setTimeout(function () {
isInvulnerable = false;
}, invulnerabilityTime);
// Check if game over
if (playerLives <= 0) {
// Update records before game over (only if not in practice mode)
if (!isPracticeMode) {
var currentDistance = Math.floor(distance / 10);
if (currentDistance > bestDistance) {
bestDistance = currentDistance;
storage.bestDistance = bestDistance;
}
if (worldCounter > bestWorlds) {
bestWorlds = worldCounter;
storage.bestWorlds = bestWorlds;
}
}
LK.getSound('crash').play();
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
// Create start menu
function createStartMenu() {
menuContainer = new Container();
// Menu background
var menuBg = LK.getAsset('cloud', {
scaleX: 15,
scaleY: 20,
alpha: 0.3,
tint: 0x000080
});
menuBg.x = 1024;
menuBg.y = 1366;
menuContainer.addChild(menuBg);
// Game title
var titleText = new Text2('WORD RUNNER', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
menuContainer.addChild(titleText);
// Subtitle
var subtitleText = new Text2('Tap to jump and avoid obstacles!', {
size: 80,
fill: 0xCCCCCC,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 950;
menuContainer.addChild(subtitleText);
// Play button
var playButton = LK.getAsset('portal', {
scaleX: 2,
scaleY: 1.5
});
playButton.x = 1024;
playButton.y = 1400;
playButton.anchor.set(0.5, 0.5);
menuContainer.addChild(playButton);
// Play button text
var playText = new Text2('PLAY', {
size: 100,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1400;
menuContainer.addChild(playText);
// Shop button
var shopButton = LK.getAsset('coin', {
scaleX: 2,
scaleY: 2
});
shopButton.x = 1024;
shopButton.y = 2100;
shopButton.anchor.set(0.5, 0.5);
menuContainer.addChild(shopButton);
// Shop button text
var shopText = new Text2('SHOP', {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
shopText.anchor.set(0.5, 0.5);
shopText.x = 1024;
shopText.y = 2100;
menuContainer.addChild(shopText);
// Store shop button reference for click detection
menuContainer.shopButton = shopButton;
// Instructions
var instructText = new Text2('Collect portals to travel between worlds!', {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
instructText.anchor.set(0.5, 0.5);
instructText.x = 1024;
instructText.y = 1600;
menuContainer.addChild(instructText);
// Records display
var recordsText = new Text2('RECORDS', {
size: 90,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
recordsText.anchor.set(0.5, 0.5);
recordsText.x = 1024;
recordsText.y = 1800;
menuContainer.addChild(recordsText);
var bestDistanceText = new Text2('Best Distance: ' + bestDistance, {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
bestDistanceText.anchor.set(0.5, 0.5);
bestDistanceText.x = 1024;
bestDistanceText.y = 1900;
menuContainer.addChild(bestDistanceText);
var bestWorldsText = new Text2('Best Worlds: ' + bestWorlds, {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
bestWorldsText.anchor.set(0.5, 0.5);
bestWorldsText.x = 1024;
bestWorldsText.y = 1980;
menuContainer.addChild(bestWorldsText);
// Practice button
var practiceButton = LK.getAsset('escudo', {
scaleX: 1.5,
scaleY: 1.5
});
practiceButton.x = 1024;
practiceButton.y = 2300;
practiceButton.anchor.set(0.5, 0.5);
menuContainer.addChild(practiceButton);
// Practice button text
var practiceText = new Text2('PRACTICE', {
size: 70,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
practiceText.anchor.set(0.5, 0.5);
practiceText.x = 1024;
practiceText.y = 2300;
menuContainer.addChild(practiceText);
// Store practice button reference for click detection
menuContainer.practiceButton = practiceButton;
game.addChild(menuContainer);
}
// Create shop menu
function createShopMenu() {
shopContainer = new Container();
// Shop background
var shopBg = LK.getAsset('cloud', {
scaleX: 15,
scaleY: 20,
alpha: 0.3,
tint: 0x000080
});
shopBg.x = 1024;
shopBg.y = 1366;
shopContainer.addChild(shopBg);
// Shop title
var shopTitleText = new Text2('SHOP', {
size: 120,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
shopTitleText.anchor.set(0.5, 0.5);
shopTitleText.x = 1024;
shopTitleText.y = 400;
shopContainer.addChild(shopTitleText);
// Coin counter in shop
var shopCoinText = new Text2('Coins: ' + coinCount, {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
shopCoinText.anchor.set(0.5, 0.5);
shopCoinText.x = 1024;
shopCoinText.y = 550;
shopContainer.addChild(shopCoinText);
// Skin grid
var skinNames = ['player', 'skin', 'skin2', 'skin3', 'skin4', 'skin5', 'skin6', 'skin8', 'skin9', 'skin10', 'skin11', 'skin12'];
var skinButtons = [];
var col = 0;
var row = 0;
for (var i = 0; i < skinNames.length; i++) {
var skinName = skinNames[i];
var skinButton = new Container();
skinButton.skinName = skinName;
// Skin preview
var skinPreview = skinButton.attachAsset(skinName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Position in grid with increased spacing
var startX = 1024 - 700;
var startY = 650;
skinButton.x = startX + col * 500;
skinButton.y = startY + row * 350;
// Price or status text
var priceText;
var isOwned = ownedSkins.indexOf(skinName) !== -1;
var isCurrent = skinName === currentSkin;
if (isCurrent) {
priceText = new Text2('CURRENT', {
size: 40,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 2
});
} else if (isOwned) {
priceText = new Text2('OWNED', {
size: 40,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 2
});
} else {
priceText = new Text2(skinPrices[skinName] + ' COINS', {
size: 45,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
}
priceText.anchor.set(0.5, 0.5);
priceText.y = 100;
skinButton.addChild(priceText);
skinButton.priceText = priceText;
// Add selection border for current skin
if (isCurrent) {
var border = LK.getAsset('coin', {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0.5,
tint: 0x00FF00
});
border.anchor.set(0.5, 0.5);
skinButton.addChild(border);
}
shopContainer.addChild(skinButton);
skinButtons.push(skinButton);
col++;
if (col >= 3) {
col = 0;
row++;
}
}
// Back button
var backButton = LK.getAsset('portal', {
scaleX: 1.5,
scaleY: 1.2
});
backButton.x = 1024;
backButton.y = 2550;
backButton.anchor.set(0.5, 0.5);
shopContainer.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2550;
shopContainer.addChild(backText);
// Store references for click detection
shopContainer.skinButtons = skinButtons;
shopContainer.backButton = backButton;
shopContainer.shopCoinText = shopCoinText;
game.addChild(shopContainer);
}
// Create practice menu
function createPracticeMenu() {
practiceContainer = new Container();
// Practice background
var practiceBg = LK.getAsset('cloud', {
scaleX: 15,
scaleY: 20,
alpha: 0.3,
tint: 0x000080
});
practiceBg.x = 1024;
practiceBg.y = 1366;
practiceContainer.addChild(practiceBg);
// Practice title
var practiceTitleText = new Text2('PRACTICE MODE', {
size: 120,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
practiceTitleText.anchor.set(0.5, 0.5);
practiceTitleText.x = 1024;
practiceTitleText.y = 400;
practiceContainer.addChild(practiceTitleText);
// Instructions
var practiceInstructText = new Text2('Choose starting world', {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
practiceInstructText.anchor.set(0.5, 0.5);
practiceInstructText.x = 1024;
practiceInstructText.y = 550;
practiceContainer.addChild(practiceInstructText);
// World selection buttons
var worldNames = ['Sky World', 'Space World', 'Underwater World', 'Desert World', 'Ice World', 'Lava World', 'Candy Land', 'Mushroom World', 'City World'];
var worldButtons = [];
var col = 0;
var row = 0;
for (var i = 0; i < worldNames.length; i++) {
var worldButton = new Container();
worldButton.worldIndex = i;
// World button background
var buttonBg = LK.getAsset('coin', {
scaleX: 3,
scaleY: 2,
alpha: 0.7,
tint: worlds[i].bg
});
buttonBg.anchor.set(0.5, 0.5);
worldButton.addChild(buttonBg);
// World name text
var worldNameText = new Text2(worldNames[i], {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 2
});
worldNameText.anchor.set(0.5, 0.5);
worldButton.addChild(worldNameText);
// Position in grid
var startX = 1024 - 400;
var startY = 750;
worldButton.x = startX + col * 300;
worldButton.y = startY + row * 200;
practiceContainer.addChild(worldButton);
worldButtons.push(worldButton);
col++;
if (col >= 3) {
col = 0;
row++;
}
}
// Back button
var practiceBackButton = LK.getAsset('portal', {
scaleX: 1.5,
scaleY: 1.2
});
practiceBackButton.x = 1024;
practiceBackButton.y = 2400;
practiceBackButton.anchor.set(0.5, 0.5);
practiceContainer.addChild(practiceBackButton);
var practiceBackText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
practiceBackText.anchor.set(0.5, 0.5);
practiceBackText.x = 1024;
practiceBackText.y = 2400;
practiceContainer.addChild(practiceBackText);
// Store references for click detection
practiceContainer.worldButtons = worldButtons;
practiceContainer.backButton = practiceBackButton;
game.addChild(practiceContainer);
}
// Start the game
function startGame() {
if (gameState !== 'menu') return;
isPracticeMode = false; // Reset practice mode flag
gameState = 'playing';
// Hide menu
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
// Reset lives
playerLives = maxLives;
isInvulnerable = false;
// Update player skin
player.destroy();
player = game.addChild(new Player());
player.x = 300;
player.y = groundY;
// Show player
player.visible = true;
// Create and show hearts UI
createHeartsUI();
// Start music
LK.playMusic('electro');
}
// Open shop
function openShop() {
if (gameState !== 'menu') return;
gameState = 'shop';
// Hide menu
if (menuContainer) {
menuContainer.visible = false;
}
createShopMenu();
}
// Close shop and return to menu
function closeShop() {
if (gameState !== 'shop') return;
gameState = 'menu';
// Hide shop
if (shopContainer) {
shopContainer.destroy();
shopContainer = null;
}
// Show menu
if (menuContainer) {
menuContainer.visible = true;
}
}
// Open practice menu
function openPractice() {
if (gameState !== 'menu') return;
gameState = 'practice';
// Hide menu
if (menuContainer) {
menuContainer.visible = false;
}
createPracticeMenu();
}
// Close practice and return to menu
function closePractice() {
if (gameState !== 'practice') return;
gameState = 'menu';
// Hide practice
if (practiceContainer) {
practiceContainer.destroy();
practiceContainer = null;
}
// Show menu
if (menuContainer) {
menuContainer.visible = true;
}
}
// Start practice mode
function startPracticeMode(worldIndex) {
if (gameState !== 'practice') return;
isPracticeMode = true;
practiceStartWorld = worldIndex;
gameState = 'playing';
// Hide practice menu
if (practiceContainer) {
practiceContainer.destroy();
practiceContainer = null;
}
// Reset game state
currentWorld = worldIndex;
worldCounter = 0; // Don't count worlds in practice
distance = 0;
coinCount = storage.coinCount || 0; // Don't modify coins in practice
// Reset lives
playerLives = maxLives;
isInvulnerable = false;
// Update player skin
player.destroy();
player = game.addChild(new Player());
player.x = 300;
player.y = groundY;
// Show player
player.visible = true;
// Create and show hearts UI
createHeartsUI();
// Set world appearance
game.setBackgroundColor(worlds[currentWorld].bg);
var worldNames = ['Sky World', 'Space World', 'Underwater World', 'Desert World', 'Ice World', 'Lava World', 'Candy Land', 'Mushroom World', 'City World'];
worldText.setText(worldNames[currentWorld]);
showWorldTitle(worldNames[currentWorld] + ' (Practice)');
// Show UI elements
distanceText.visible = true;
worldText.visible = true;
worldCounterText.visible = false; // Hide world counter in practice
coinCounterText.visible = false; // Hide coin counter in practice
// Start music
LK.playMusic('electro');
}
// Buy or select skin
function handleSkinClick(skinName) {
var isOwned = ownedSkins.indexOf(skinName) !== -1;
var price = skinPrices[skinName];
if (isOwned) {
// Switch to this skin
currentSkin = skinName;
storage.currentSkin = currentSkin;
// Refresh shop to update UI
closeShop();
openShop();
} else if (coinCount >= price) {
// Buy the skin
coinCount -= price;
storage.coinCount = coinCount;
ownedSkins.push(skinName);
storage.ownedSkins = ownedSkins;
currentSkin = skinName;
storage.currentSkin = currentSkin;
// Play coin sound
LK.getSound('coin').play();
// Refresh shop to update UI
closeShop();
openShop();
}
}
// Initialize start menu
createStartMenu();
// Initialize clouds
function createCloud() {
var cloud = new Cloud();
cloud.x = 2048 + Math.random() * 1000;
cloud.y = 200 + Math.random() * 800;
return cloud;
}
// Initialize planets
function createPlanet() {
var planet = new Planet();
planet.x = 2048 + Math.random() * 1000;
planet.y = 200 + Math.random() * 800;
return planet;
}
// Initialize bubbles
function createBubble() {
var bubble = new Bubble();
bubble.x = 2048 + Math.random() * 1000;
bubble.y = 200 + Math.random() * 800;
return bubble;
}
// Create initial clouds
for (var i = 0; i < 5; i++) {
var cloud = game.addChild(createCloud());
clouds.push(cloud);
}
// Create initial planets (hidden initially)
for (var i = 0; i < 5; i++) {
var planet = game.addChild(createPlanet());
planet.visible = false;
planets.push(planet);
}
// Create initial bubbles (hidden initially)
for (var i = 0; i < 5; i++) {
var bubble = game.addChild(createBubble());
bubble.visible = false;
bubbles.push(bubble);
}
// Initialize ground tiles
function createGroundTile(x) {
var tile = new Ground();
tile.x = x;
tile.y = groundY;
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
tile.speed = gameSpeed * speedMultiplier;
applyWorldTheme(tile, currentWorld);
return tile;
}
function applyWorldTheme(object, worldIndex) {
var world = worlds[worldIndex];
if (object.children && object.children[0]) {
object.children[0].tint = world.ground;
}
}
// Create initial ground
for (var i = 0; i < 4; i++) {
var tile = game.addChild(createGroundTile(i * 2048));
groundTiles.push(tile);
}
// Obstacle spawning
var obstacleTimer = 0;
var portalTimer = 0;
var skyObstacleTimer = 0;
var flyingItemTimer = 0;
var coinTimer = 0;
function spawnObstacle() {
var obstacle;
// In world 3, spawn Obstacle3 instead of regular Obstacle
if (currentWorld === 2) {
obstacle = new Obstacle3();
} else if (currentWorld === 3) {
// In world 4 (desert world), spawn EstepicursorObstacle (uses estepicursor asset)
obstacle = new EstepicursorObstacle();
} else if (currentWorld === 4) {
// In world 5 (ice world), spawn NieveObstacle (uses nieve asset)
obstacle = new NieveObstacle();
} else if (currentWorld === 5) {
// In world 6 (lava world), spawn VolcanObstacle (uses volcan asset)
obstacle = new VolcanObstacle();
} else if (currentWorld === 6) {
// In world 7 (candy land), spawn PaletaObstacle (uses paleta asset)
obstacle = new PaletaObstacle();
} else if (currentWorld === 7) {
// In world 8 (mushroom world), spawn HongoObstacle (uses hongo asset)
obstacle = new HongoObstacle();
} else if (currentWorld === 8) {
// In world 9 (city world), spawn PosteObstacle (uses poste asset)
obstacle = new PosteObstacle();
} else {
obstacle = new Obstacle();
}
obstacle.x = 2048 + 100;
obstacle.y = groundY;
// Apply world theme only for regular obstacles (not Obstacle3, EstepicursorObstacle, NieveObstacle, VolcanObstacle, PaletaObstacle, HongoObstacle, PosteObstacle or UlcesObstacle)
if (currentWorld !== 2 && currentWorld !== 3 && currentWorld !== 4 && currentWorld !== 5 && currentWorld !== 6 && currentWorld !== 7 && currentWorld !== 8 && obstacle.children && obstacle.children[0]) {
obstacle.children[0].tint = worlds[currentWorld].obstacle;
}
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
if (currentWorld === 3) {
// EstepicursorObstacle moves slowly (now in desert world)
obstacle.speed = gameSpeed * 0.5 * speedMultiplier;
} else {
obstacle.speed = gameSpeed * speedMultiplier;
}
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnPortal() {
var portal = new Portal();
portal.x = 2048 + 200;
portal.y = groundY - 100;
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
portal.speed = gameSpeed * speedMultiplier;
portals.push(portal);
game.addChild(portal);
currentPortalCount++;
}
function spawnSkyObstacle() {
var skyObstacle = new SkyObstacle();
skyObstacle.x = 2048 + 100;
skyObstacle.y = 400 + Math.random() * 800; // Random height in the sky
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
skyObstacle.speed = gameSpeed * speedMultiplier;
skyObstacles.push(skyObstacle);
game.addChild(skyObstacle);
}
function spawnFlyingItem() {
var flyingItem = new FlyingItem();
flyingItem.x = 2048 + 200;
flyingItem.y = 1000 + Math.random() * 400; // Random height in lower middle area
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
flyingItem.speed = gameSpeed * speedMultiplier;
flyingItems.push(flyingItem);
game.addChild(flyingItem);
}
function spawnShieldItem() {
var shieldItem = new ShieldItem();
shieldItem.x = 2048 + 200;
shieldItem.y = 1000 + Math.random() * 400; // Random height in lower middle area
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
shieldItem.speed = gameSpeed * speedMultiplier;
shieldItems.push(shieldItem);
game.addChild(shieldItem);
}
function spawnPowerUpItem() {
// Cycle between flying and shield items
if (lastItemType === 'flying') {
spawnShieldItem();
lastItemType = 'shield';
} else {
spawnFlyingItem();
lastItemType = 'flying';
}
}
function spawnCoin() {
var coin = new Coin();
coin.x = 2048 + 100;
coin.y = 1200 + Math.random() * 800; // Random height in lower area
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
coin.speed = gameSpeed * speedMultiplier;
coins.push(coin);
game.addChild(coin);
}
function spawnCoinGroup() {
// Spawn 3-5 coins in a line with some spacing
var numCoins = 3 + Math.floor(Math.random() * 3);
var startY = 1200 + Math.random() * 600;
for (var i = 0; i < numCoins; i++) {
var coin = new Coin();
coin.x = 2048 + 100 + i * 120; // Space coins 120 pixels apart
coin.y = startY + (Math.random() * 100 - 50); // Small vertical variation
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
coin.speed = gameSpeed * speedMultiplier;
coins.push(coin);
game.addChild(coin);
}
}
function createWaterEffect() {
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
// Create a subtle water overlay effect
waterEffect = new Container();
var waterOverlay = LK.getAsset('cloud', {
width: 2048,
height: 2732,
scaleX: 10,
scaleY: 10,
alpha: 0.1,
tint: 0x006064
});
waterEffect.addChild(waterOverlay);
game.addChild(waterEffect);
// Add gentle wave animation
function animateWater() {
if (waterEffect) {
tween(waterOverlay, {
alpha: 0.05
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (waterEffect) {
tween(waterOverlay, {
alpha: 0.15
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateWater
});
}
}
});
}
}
animateWater();
}
function changeWorld() {
if (worldTransitioning) {
return;
}
worldTransitioning = true;
currentWorld = (currentWorld + 1) % worlds.length;
// Flash effect
LK.effects.flashScreen(0xFFFFFF, 500);
// Update background
game.setBackgroundColor(worlds[currentWorld].bg);
// Update world text
var worldNames = ['Sky World', 'Space World', 'Underwater World', 'Desert World', 'Ice World', 'Lava World', 'Candy Land', 'Mushroom World', 'City World'];
worldText.setText(worldNames[currentWorld]);
// Show large world title
showWorldTitle(worldNames[currentWorld]);
// Handle world-specific elements
if (currentWorld === 1) {
// Space world: Hide clouds and bubbles, show planets
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = false;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = true;
}
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
} else if (currentWorld === 2) {
// Underwater world: Hide clouds and planets, show bubbles and water effect
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = true;
}
createWaterEffect();
} else if (currentWorld === 3) {
// Desert world: Hide clouds, planets and bubbles, show only static sun
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = false;
}
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
// Create or show static sun
if (!sun) {
sun = new Sun();
sun.x = 1600; // Position sun in upper right area
sun.y = 400;
game.addChild(sun);
} else {
sun.visible = true;
}
} else {
// Other worlds: Show clouds, hide planets, bubbles and sun
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = true;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = false;
}
if (sun) {
sun.visible = false;
}
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
}
// Update existing objects
for (var i = 0; i < groundTiles.length; i++) {
applyWorldTheme(groundTiles[i], currentWorld);
}
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i].children && obstacles[i].children[0]) {
obstacles[i].children[0].tint = worlds[currentWorld].obstacle;
}
}
LK.getSound('portal').play();
LK.setTimeout(function () {
worldTransitioning = false;
}, 500);
}
// Input handling
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if play button area was clicked (around the PLAY text)
var playButtonX = 1024;
var playButtonY = 1400;
var playButtonWidth = 240; // Approximate width of portal asset scaled
var playButtonHeight = 300; // Approximate height of portal asset scaled
if (x >= playButtonX - playButtonWidth / 2 && x <= playButtonX + playButtonWidth / 2 && y >= playButtonY - playButtonHeight / 2 && y <= playButtonY + playButtonHeight / 2) {
startGame();
// Show UI elements
distanceText.visible = true;
worldText.visible = true;
worldCounterText.visible = true;
coinCounterText.visible = true;
return;
}
// Check if shop button was clicked
var shopButtonX = 1024;
var shopButtonY = 2100;
var shopButtonWidth = 120; // Coin asset width scaled by 2
var shopButtonHeight = 120; // Coin asset height scaled by 2
if (x >= shopButtonX - shopButtonWidth / 2 && x <= shopButtonX + shopButtonWidth / 2 && y >= shopButtonY - shopButtonHeight / 2 && y <= shopButtonY + shopButtonHeight / 2) {
openShop();
return;
}
// Check if practice button was clicked
var practiceButtonX = 1024;
var practiceButtonY = 2300;
var practiceButtonWidth = 150; // Escudo asset width scaled by 1.5
var practiceButtonHeight = 150; // Escudo asset height scaled by 1.5
if (x >= practiceButtonX - practiceButtonWidth / 2 && x <= practiceButtonX + practiceButtonWidth / 2 && y >= practiceButtonY - practiceButtonHeight / 2 && y <= practiceButtonY + practiceButtonHeight / 2) {
openPractice();
return;
}
} else if (gameState === 'shop') {
// Check back button
var backButtonX = 1024;
var backButtonY = 2550;
var backButtonWidth = 240; // Portal asset width scaled by 1.5
var backButtonHeight = 240; // Portal asset height scaled by 1.2
if (x >= backButtonX - backButtonWidth / 2 && x <= backButtonX + backButtonWidth / 2 && y >= backButtonY - backButtonHeight / 2 && y <= backButtonY + backButtonHeight / 2) {
closeShop();
return;
}
// Check skin buttons
for (var i = 0; i < shopContainer.skinButtons.length; i++) {
var skinButton = shopContainer.skinButtons[i];
var skinButtonWidth = 280; // Adjusted for new smaller scale and increased spacing
var skinButtonHeight = 280; // Adjusted for new smaller scale and increased spacing
if (x >= skinButton.x - skinButtonWidth / 2 && x <= skinButton.x + skinButtonWidth / 2 && y >= skinButton.y - skinButtonHeight / 2 && y <= skinButton.y + skinButtonHeight / 2) {
handleSkinClick(skinButton.skinName);
return;
}
}
} else if (gameState === 'practice') {
// Check back button
var practiceBackButtonX = 1024;
var practiceBackButtonY = 2400;
var practiceBackButtonWidth = 240; // Portal asset width scaled by 1.5
var practiceBackButtonHeight = 240; // Portal asset height scaled by 1.2
if (x >= practiceBackButtonX - practiceBackButtonWidth / 2 && x <= practiceBackButtonX + practiceBackButtonWidth / 2 && y >= practiceBackButtonY - practiceBackButtonHeight / 2 && y <= practiceBackButtonY + practiceBackButtonHeight / 2) {
closePractice();
return;
}
// Check world buttons
for (var i = 0; i < practiceContainer.worldButtons.length; i++) {
var worldButton = practiceContainer.worldButtons[i];
var worldButtonWidth = 300; // World button width
var worldButtonHeight = 160; // World button height
if (x >= worldButton.x - worldButtonWidth / 2 && x <= worldButton.x + worldButtonWidth / 2 && y >= worldButton.y - worldButtonHeight / 2 && y <= worldButton.y + worldButtonHeight / 2) {
startPracticeMode(worldButton.worldIndex);
return;
}
}
} else if (gameState === 'playing') {
player.jump();
}
};
// Main game loop
game.update = function () {
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
// Update distance
distance += gameSpeed;
distanceText.setText('Distance: ' + Math.floor(distance / 10));
LK.setScore(Math.floor(distance / 10));
// Spawn obstacles
obstacleTimer++;
if (obstacleTimer > 150 + Math.random() * 100) {
spawnObstacle();
obstacleTimer = 0;
}
// Spawn portals - only one at a time
portalTimer++;
if (portalTimer > 300 + Math.random() * 200 && currentPortalCount === 0) {
spawnPortal();
portalTimer = 0;
}
// Spawn sky obstacles only in world 2 (Space World)
if (currentWorld === 1) {
skyObstacleTimer++;
if (skyObstacleTimer > 200 + Math.random() * 150) {
spawnSkyObstacle();
skyObstacleTimer = 0;
}
}
// Spawn power-up items randomly (rare) - alternates between flying and shield
flyingItemTimer++;
if (flyingItemTimer > 800 + Math.random() * 400) {
spawnPowerUpItem();
flyingItemTimer = 0;
}
// Update and remove off-screen coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.x < -100) {
coin.destroy();
coins.splice(i, 1);
continue;
}
// Coin collision
if (player.intersects(coin)) {
if (!isPracticeMode) {
coinCount++;
storage.coinCount = coinCount;
coinCounterText.setText('Coins: ' + coinCount);
}
// Play coin sound
LK.getSound('coin').play();
// Visual effect when collecting coin
tween(coin, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(i, 1);
}
}
// Update ground tiles
coinTimer++;
if (coinTimer > 200 + Math.random() * 150) {
if (Math.random() < 0.3) {
spawnCoinGroup(); // 30% chance for coin group
} else {
spawnCoin(); // 70% chance for single coin
}
coinTimer = 0;
}
// Update flying timer
if (isFlying) {
flyingTimer--;
if (flyingTimer <= 0) {
isFlying = false;
}
}
// Update invincibility timer
if (isInvincible) {
invincibleTimer--;
if (invincibleTimer <= 0) {
isInvincible = false;
}
}
// Update and remove off-screen obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.x < -100) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
if (player.intersects(obstacle) && !isInvulnerable && !isInvincible) {
loseLife();
if (playerLives <= 0) {
return;
}
}
}
// Update and remove off-screen portals
for (var i = portals.length - 1; i >= 0; i--) {
var portal = portals[i];
if (portal.x < -150) {
portal.destroy();
portals.splice(i, 1);
currentPortalCount = 0; // Reset count to allow new portal spawning
continue;
}
// Portal collision
if (player.intersects(portal) && !portal.used) {
portal.used = true;
if (!isPracticeMode) {
worldCounter++;
worldCounterText.setText('Mundos: ' + worldCounter);
}
changeWorld();
LK.setScore(LK.getScore() + 10);
// Remove portal immediately after use
portal.destroy();
portals.splice(i, 1);
currentPortalCount = 0; // Reset count to allow new portal spawning
}
}
// Update and remove off-screen sky obstacles
for (var i = skyObstacles.length - 1; i >= 0; i--) {
var skyObstacle = skyObstacles[i];
if (skyObstacle.x < -100) {
skyObstacle.destroy();
skyObstacles.splice(i, 1);
continue;
}
// Collision detection with sky obstacles
if (player.intersects(skyObstacle) && !isInvulnerable && !isInvincible) {
loseLife();
if (playerLives <= 0) {
return;
}
}
}
// Update and remove off-screen flying items
for (var i = flyingItems.length - 1; i >= 0; i--) {
var flyingItem = flyingItems[i];
if (flyingItem.x < -100) {
flyingItem.destroy();
flyingItems.splice(i, 1);
continue;
}
// Flying item collision
if (player.intersects(flyingItem)) {
isFlying = true;
flyingTimer = flyingDuration;
// Visual effect when collecting flying item
tween(flyingItem, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
flyingItem.destroy();
}
});
flyingItems.splice(i, 1);
}
}
// Update and remove off-screen shield items
for (var i = shieldItems.length - 1; i >= 0; i--) {
var shieldItem = shieldItems[i];
if (shieldItem.x < -100) {
shieldItem.destroy();
shieldItems.splice(i, 1);
continue;
}
// Shield item collision
if (player.intersects(shieldItem)) {
isInvincible = true;
invincibleTimer = invincibleDuration;
// Visual effect when collecting shield item
tween(shieldItem, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
shieldItem.destroy();
}
});
shieldItems.splice(i, 1);
}
}
// Update ground tiles
for (var i = groundTiles.length - 1; i >= 0; i--) {
var tile = groundTiles[i];
if (tile.x < -2048) {
// Move tile to the right
tile.x = tile.x + groundTiles.length * 2048;
applyWorldTheme(tile, currentWorld);
}
}
// Check if player fell off screen
if (player.y > 2732 + 100) {
loseLife();
if (playerLives <= 0) {
return;
}
// Reset player position when falling
player.y = groundY;
player.velocityY = 0;
}
// Update clouds (replaced with ulces in candy land world)
for (var i = clouds.length - 1; i >= 0; i--) {
var cloud = clouds[i];
if (cloud.x < -300) {
cloud.destroy();
clouds.splice(i, 1);
// Spawn new cloud or ulces based on world
var newCloud;
if (currentWorld === 6) {
// In candy land world, spawn ulces instead of clouds
newCloud = new UlcesObstacle();
newCloud.x = 2048 + Math.random() * 1000;
newCloud.y = 200 + Math.random() * 800;
newCloud.speed = 1 + Math.random() * 2;
} else {
newCloud = createCloud();
}
newCloud = game.addChild(newCloud);
newCloud.visible = currentWorld !== 1 && currentWorld !== 2 && currentWorld !== 3; // Hide in space, underwater and desert worlds
clouds.push(newCloud);
}
}
// Update planets
for (var i = planets.length - 1; i >= 0; i--) {
var planet = planets[i];
if (planet.x < -300) {
planet.destroy();
planets.splice(i, 1);
// Spawn new planet
var newPlanet = game.addChild(createPlanet());
newPlanet.visible = currentWorld === 1;
planets.push(newPlanet);
}
}
// Update bubbles
for (var i = bubbles.length - 1; i >= 0; i--) {
var bubble = bubbles[i];
if (bubble.x < -300) {
bubble.destroy();
bubbles.splice(i, 1);
// Spawn new bubble
var newBubble = game.addChild(createBubble());
newBubble.visible = currentWorld === 2;
bubbles.push(newBubble);
}
}
// Gradually increase difficulty and speed based on distance
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
var currentGameSpeed = gameSpeed * speedMultiplier;
if (LK.ticks % 600 === 0) {
gameSpeed += 0.5;
}
// Update speeds for existing objects with current speed multiplier
for (var i = 0; i < obstacles.length; i++) {
// Check if it's an EstepicursorObstacle (world 4)
if (obstacles[i].constructor.name === 'EstepicursorObstacle') {
obstacles[i].speed = currentGameSpeed * 0.5; // Keep slow speed
} else {
obstacles[i].speed = currentGameSpeed;
}
}
for (var i = 0; i < portals.length; i++) {
portals[i].speed = currentGameSpeed;
}
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].speed = currentGameSpeed;
}
for (var i = 0; i < clouds.length; i++) {
clouds[i].speed = (1 + Math.random() * 2) * speedMultiplier;
}
for (var i = 0; i < planets.length; i++) {
planets[i].speed = (1 + Math.random() * 2) * speedMultiplier;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].speed = (1 + Math.random() * 2) * speedMultiplier;
}
for (var i = 0; i < skyObstacles.length; i++) {
skyObstacles[i].speed = currentGameSpeed;
}
for (var i = 0; i < flyingItems.length; i++) {
flyingItems[i].speed = currentGameSpeed;
}
for (var i = 0; i < shieldItems.length; i++) {
shieldItems[i].speed = currentGameSpeed;
}
for (var i = 0; i < coins.length; i++) {
coins[i].speed = currentGameSpeed;
}
// Update and remove volcano projectiles
for (var i = volcanoProjectiles.length - 1; i >= 0; i--) {
var projectile = volcanoProjectiles[i];
if (projectile.y < -100) {
projectile.destroy();
volcanoProjectiles.splice(i, 1);
continue;
}
// Collision detection with player
if (player.intersects(projectile) && !isInvulnerable && !isInvincible) {
loseLife();
projectile.destroy();
volcanoProjectiles.splice(i, 1);
if (playerLives <= 0) {
return;
}
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BolaProjectile = Container.expand(function () {
var self = Container.call(this);
var bolaGraphics = self.attachAsset('bola', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -8; // Move upward
self.update = function () {
self.y += self.speed; // Move upward (negative Y)
};
return self;
});
var Bubble = Container.expand(function () {
var self = Container.call(this);
var bubbleGraphics = self.attachAsset('burbuja', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 2;
bubbleGraphics.alpha = 0.6 + Math.random() * 0.3;
var scale = 0.5 + Math.random() * 1.5;
bubbleGraphics.scaleX = scale;
bubbleGraphics.scaleY = scale;
// Add floating animation for bubbles
self.floatOffset = Math.random() * Math.PI * 2;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.02 + self.floatOffset) * 0.5;
};
return self;
});
var Cloud = Container.expand(function () {
var self = Container.call(this);
var cloudGraphics = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 2;
cloudGraphics.alpha = 0.6 + Math.random() * 0.3;
var scale = 0.5 + Math.random() * 1.5;
cloudGraphics.scaleX = scale;
cloudGraphics.scaleY = scale * 0.6;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
// Add rotation animation
self.rotationSpeed = 0.1;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.05 + self.floatOffset) * 1.0;
// Add rotation
coinGraphics.rotation += self.rotationSpeed;
// Add subtle pulsing effect
var scale = 1.0 + Math.sin(LK.ticks * 0.08) * 0.1;
coinGraphics.scaleX = scale;
coinGraphics.scaleY = scale;
};
return self;
});
var EstepicursorObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('estepicursor', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed * 0.5; // Move slowly to the left
self.update = function () {
self.x -= self.speed;
};
return self;
});
var FlyingItem = Container.expand(function () {
var self = Container.call(this);
var itemGraphics = self.attachAsset('over', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
// Add glow effect
itemGraphics.tint = 0x00FFFF;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.05 + self.floatOffset) * 1.5;
// Add pulsing glow effect
itemGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.1) * 0.3;
};
return self;
});
var Ground = Container.expand(function () {
var self = Container.call(this);
var groundGraphics = self.attachAsset('ground', {
anchorX: 0,
anchorY: 0
});
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var HongoObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('hongo', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var NieveObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('nieve', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
// Start flip animation when obstacle is created
function startFlipAnimation() {
tween(obstacleGraphics, {
scaleX: -1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(obstacleGraphics, {
scaleX: 1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: startFlipAnimation
});
}
});
}
// Start the flip animation
startFlipAnimation();
return self;
});
var Obstacle3 = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle3', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var PaletaObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('paleta', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Planet = Container.expand(function () {
var self = Container.call(this);
var planetGraphics = self.attachAsset('planeta', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 1 + Math.random() * 2;
planetGraphics.alpha = 0.8 + Math.random() * 0.2;
var scale = 0.8 + Math.random() * 1.2;
planetGraphics.scaleX = scale;
planetGraphics.scaleY = scale;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset(currentSkin, {
anchorX: 0.5,
anchorY: 1
});
self.velocityY = 0;
self.isOnGround = false;
self.jumpPower = -40;
self.gravity = 1.2;
self.jumpCount = 0;
self.maxJumps = 2;
self.jump = function () {
if (self.jumpCount < self.maxJumps) {
self.velocityY = self.jumpPower;
self.isOnGround = false;
self.jumpCount++;
LK.getSound('jump').play();
}
};
self.update = function () {
if (isFlying) {
// Flying mode: gentle hovering motion, no gravity
self.y = 800 + Math.sin(LK.ticks * 0.08) * 50;
self.velocityY = 0;
self.isOnGround = false;
self.jumpCount = 0;
// Flying animation
playerGraphics.rotation = Math.sin(LK.ticks * 0.2) * 0.05;
// Add flying glow effect
playerGraphics.tint = 0x00FFFF;
} else if (isInvincible) {
// Invincible mode: normal gravity but golden glow
playerGraphics.tint = 0xFFD700;
self.velocityY += self.gravity;
self.y += self.velocityY;
// Check ground collision
if (self.y >= groundY) {
self.y = groundY;
self.velocityY = 0;
self.isOnGround = true;
self.jumpCount = 0;
}
// Animate running with glow pulse
playerGraphics.rotation = Math.sin(LK.ticks * 0.3) * 0.1;
playerGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.2) * 0.3;
} else {
// Normal mode: apply gravity
playerGraphics.tint = 0xFFFFFF;
playerGraphics.alpha = 1.0;
self.velocityY += self.gravity;
self.y += self.velocityY;
// Check ground collision
if (self.y >= groundY) {
self.y = groundY;
self.velocityY = 0;
self.isOnGround = true;
self.jumpCount = 0;
}
// Animate running
playerGraphics.rotation = Math.sin(LK.ticks * 0.3) * 0.1;
}
};
return self;
});
var Portal = Container.expand(function () {
var self = Container.call(this);
var portalGraphics = self.attachAsset('portal', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
self.used = false;
self.update = function () {
self.x -= self.speed;
// Portal animation
portalGraphics.rotation += 0.1;
portalGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.15) * 0.3;
};
return self;
});
var PosteObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('poste', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var ShieldItem = Container.expand(function () {
var self = Container.call(this);
var itemGraphics = self.attachAsset('escudo', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = gameSpeed;
// Add floating animation
self.floatOffset = Math.random() * Math.PI * 2;
// Add glow effect
itemGraphics.tint = 0xFFD700;
self.update = function () {
self.x -= self.speed;
// Add gentle floating motion
self.y += Math.sin(LK.ticks * 0.05 + self.floatOffset) * 1.5;
// Add pulsing glow effect
itemGraphics.alpha = 0.7 + Math.sin(LK.ticks * 0.1) * 0.3;
};
return self;
});
var SkyObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('obstacle2', {
anchorX: 0.5,
anchorY: 0.5
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height * 0.5; // Center the hitbox
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var Sun = Container.expand(function () {
var self = Container.call(this);
var sunGraphics = self.attachAsset('sol', {
anchorX: 0.5,
anchorY: 0.5
});
// Sun doesn't move, it's static
sunGraphics.alpha = 0.9;
var scale = 2.0; // Make sun larger
sunGraphics.scaleX = scale;
sunGraphics.scaleY = scale;
// Add gentle glow animation
self.glowOffset = 0;
self.update = function () {
// Add gentle pulsing glow effect
sunGraphics.alpha = 0.8 + Math.sin(LK.ticks * 0.03 + self.glowOffset) * 0.1;
};
return self;
});
var UlcesObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('ulces', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.update = function () {
self.x -= self.speed;
};
return self;
});
var VolcanObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('volcan', {
anchorX: 0.5,
anchorY: 1
});
// Create smaller hitbox for collision detection (30% of original size)
self.hitbox = new Container();
self.hitbox.width = obstacleGraphics.width * 0.3;
self.hitbox.height = obstacleGraphics.height * 0.3;
self.hitbox.x = -self.hitbox.width * 0.5; // Center the hitbox
self.hitbox.y = -self.hitbox.height; // Align to bottom like the visual
self.addChild(self.hitbox);
// Override intersects method to use hitbox instead of full visual
self.intersects = function (other) {
return self.hitbox.intersects ? self.hitbox.intersects(other) : Container.prototype.intersects.call(self.hitbox, other);
};
self.speed = gameSpeed;
self.shootTimer = 0;
self.update = function () {
self.x -= self.speed;
// Shoot bola projectiles upward
self.shootTimer++;
if (self.shootTimer > 60) {
// Shoot every second (60 frames)
var bola = new BolaProjectile();
bola.x = self.x;
bola.y = self.y - 50; // Start above the volcano
volcanoProjectiles.push(bola);
game.addChild(bola);
self.shootTimer = 0;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var gameSpeed = 8;
var groundY = 2732 - 200;
var player;
var obstacles = [];
var portals = [];
var groundTiles = [];
var distance = 0;
var currentWorld = 0;
var worldTransitioning = false;
var clouds = [];
var planets = [];
var skyObstacles = [];
var bubbles = [];
var volcanoProjectiles = [];
var coins = [];
var coinCount = storage.coinCount || 0;
var flyingItems = [];
var isFlying = false;
var flyingTimer = 0;
var flyingDuration = 600; // 10 seconds at 60 FPS
var shieldItems = [];
var isInvincible = false;
var invincibleTimer = 0;
var invincibleDuration = 600; // 10 seconds at 60 FPS
var lastItemType = 'flying'; // Track last spawned item type for alternating
var currentPortalCount = 0; // Track current portal count
var sun = null; // Static sun for world 4
var worldCounter = 0;
var waterEffect = null;
var playerLives = 3;
var maxLives = 3;
var hearts = [];
var isInvulnerable = false;
var invulnerabilityTime = 2000; // 2 seconds
// Game state variables
var gameState = 'menu'; // 'menu', 'playing', 'shop', or 'practice'
var menuContainer = null;
var shopContainer = null;
var practiceContainer = null;
var isPracticeMode = false;
var practiceStartWorld = 0;
// Shop and skin variables
var currentSkin = storage.currentSkin || 'player';
var ownedSkins = storage.ownedSkins || ['player']; // Player skin is free by default
var skinPrices = {
'player': 0,
'skin': 50,
'skin2': 50,
'skin3': 50,
'skin4': 50,
'skin5': 50,
'skin6': 50,
'skin8': 75,
'skin9': 75,
'skin10': 100,
'skin11': 100,
'skin12': 125
};
// Record tracking
var bestDistance = storage.bestDistance || 0;
var bestWorlds = storage.bestWorlds || 0;
// World configurations
var worlds = [{
bg: 0x87CEEB,
ground: 0x4A90E2,
obstacle: 0xFF5722
},
// Sky world
{
bg: 0x1A237E,
ground: 0x424242,
obstacle: 0xFF9800
},
// Space world
{
bg: 0x006064,
ground: 0x00838F,
obstacle: 0x4CAF50
},
// Underwater world
{
bg: 0xFF6F00,
ground: 0xD84315,
obstacle: 0x795548
},
// Desert world
{
bg: 0xE3F2FD,
ground: 0x90A4AE,
obstacle: 0x607D8B
},
// Ice world
{
bg: 0x3E2723,
ground: 0xFF5722,
obstacle: 0x212121
},
// Lava world
{
bg: 0xFFB6C1,
ground: 0xFF69B4,
obstacle: 0xFF1493
},
// Candy land world
{
bg: 0x228B22,
ground: 0x8B4513,
obstacle: 0x654321
},
// Mushroom world
{
bg: 0x696969,
ground: 0x2F4F4F,
obstacle: 0x778899
} // City world
];
// UI
var distanceText = new Text2('Distance: 0', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
distanceText.anchor.set(0, 0);
distanceText.x = 50;
distanceText.y = 50;
distanceText.visible = false; // Hide initially
LK.gui.topLeft.addChild(distanceText);
var worldText = new Text2('Sky World', {
size: 50,
fill: 0xFFFFFF
});
worldText.anchor.set(0.5, 0);
worldText.visible = false; // Hide initially
LK.gui.top.addChild(worldText);
var worldTitleText = new Text2('', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
worldTitleText.anchor.set(0.5, 0.5);
worldTitleText.x = 1024;
worldTitleText.y = 1366;
worldTitleText.visible = false;
LK.gui.center.addChild(worldTitleText);
function showWorldTitle(worldName) {
worldTitleText.setText(worldName);
worldTitleText.visible = true;
worldTitleText.alpha = 0;
worldTitleText.scaleX = 0.5;
worldTitleText.scaleY = 0.5;
tween(worldTitleText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(worldTitleText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
worldTitleText.visible = false;
}
});
}, 2000);
}
});
}
var worldCounterText = new Text2('Mundos: 0', {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 3,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
worldCounterText.anchor.set(1, 0);
worldCounterText.x = -50;
worldCounterText.y = 50;
worldCounterText.visible = false; // Hide initially
LK.gui.topRight.addChild(worldCounterText);
var coinCounterText = new Text2('Coins: ' + coinCount, {
size: 50,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 3,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
coinCounterText.anchor.set(1, 0);
coinCounterText.x = -50;
coinCounterText.y = 120;
coinCounterText.visible = false; // Hide initially
LK.gui.topRight.addChild(coinCounterText);
// Initialize player
player = game.addChild(new Player());
player.x = 300;
player.y = groundY;
player.visible = false; // Hide player initially
// Create hearts UI
function createHeartsUI() {
// Clear existing hearts
for (var i = 0; i < hearts.length; i++) {
hearts[i].destroy();
}
hearts = [];
// Create heart containers
for (var i = 0; i < maxLives; i++) {
var heartContainer = new Container();
var heart = heartContainer.attachAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
// Position hearts horizontally
heartContainer.x = 50 + i * 100;
heartContainer.y = 150;
// Show/hide based on current lives
if (i < playerLives) {
heart.alpha = 1.0;
heart.tint = 0xFF0000; // Red for active hearts
} else {
heart.alpha = 0.3;
heart.tint = 0x666666; // Gray for lost hearts
}
LK.gui.topLeft.addChild(heartContainer);
hearts.push(heartContainer);
}
}
// Update hearts display
function updateHeartsUI() {
for (var i = 0; i < hearts.length; i++) {
var heart = hearts[i].children[0];
if (i < playerLives) {
heart.alpha = 1.0;
heart.tint = 0xFF0000; // Red for active hearts
} else {
heart.alpha = 0.3;
heart.tint = 0x666666; // Gray for lost hearts
}
}
}
// Lose a life function
function loseLife() {
if (isInvulnerable || playerLives <= 0) return;
playerLives--;
updateHeartsUI();
// Make player invulnerable temporarily
isInvulnerable = true;
// Play pain sound
LK.getSound('pain').play();
// Flash player red to indicate damage
tween(player.children[0], {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(player.children[0], {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
// Remove invulnerability after set time
LK.setTimeout(function () {
isInvulnerable = false;
}, invulnerabilityTime);
// Check if game over
if (playerLives <= 0) {
// Update records before game over (only if not in practice mode)
if (!isPracticeMode) {
var currentDistance = Math.floor(distance / 10);
if (currentDistance > bestDistance) {
bestDistance = currentDistance;
storage.bestDistance = bestDistance;
}
if (worldCounter > bestWorlds) {
bestWorlds = worldCounter;
storage.bestWorlds = bestWorlds;
}
}
LK.getSound('crash').play();
LK.effects.flashScreen(0xFF0000, 1000);
LK.showGameOver();
}
}
// Create start menu
function createStartMenu() {
menuContainer = new Container();
// Menu background
var menuBg = LK.getAsset('cloud', {
scaleX: 15,
scaleY: 20,
alpha: 0.3,
tint: 0x000080
});
menuBg.x = 1024;
menuBg.y = 1366;
menuContainer.addChild(menuBg);
// Game title
var titleText = new Text2('WORD RUNNER', {
size: 120,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
menuContainer.addChild(titleText);
// Subtitle
var subtitleText = new Text2('Tap to jump and avoid obstacles!', {
size: 80,
fill: 0xCCCCCC,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 950;
menuContainer.addChild(subtitleText);
// Play button
var playButton = LK.getAsset('portal', {
scaleX: 2,
scaleY: 1.5
});
playButton.x = 1024;
playButton.y = 1400;
playButton.anchor.set(0.5, 0.5);
menuContainer.addChild(playButton);
// Play button text
var playText = new Text2('PLAY', {
size: 100,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
playText.anchor.set(0.5, 0.5);
playText.x = 1024;
playText.y = 1400;
menuContainer.addChild(playText);
// Shop button
var shopButton = LK.getAsset('coin', {
scaleX: 2,
scaleY: 2
});
shopButton.x = 1024;
shopButton.y = 2100;
shopButton.anchor.set(0.5, 0.5);
menuContainer.addChild(shopButton);
// Shop button text
var shopText = new Text2('SHOP', {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
shopText.anchor.set(0.5, 0.5);
shopText.x = 1024;
shopText.y = 2100;
menuContainer.addChild(shopText);
// Store shop button reference for click detection
menuContainer.shopButton = shopButton;
// Instructions
var instructText = new Text2('Collect portals to travel between worlds!', {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
instructText.anchor.set(0.5, 0.5);
instructText.x = 1024;
instructText.y = 1600;
menuContainer.addChild(instructText);
// Records display
var recordsText = new Text2('RECORDS', {
size: 90,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
recordsText.anchor.set(0.5, 0.5);
recordsText.x = 1024;
recordsText.y = 1800;
menuContainer.addChild(recordsText);
var bestDistanceText = new Text2('Best Distance: ' + bestDistance, {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
bestDistanceText.anchor.set(0.5, 0.5);
bestDistanceText.x = 1024;
bestDistanceText.y = 1900;
menuContainer.addChild(bestDistanceText);
var bestWorldsText = new Text2('Best Worlds: ' + bestWorlds, {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
bestWorldsText.anchor.set(0.5, 0.5);
bestWorldsText.x = 1024;
bestWorldsText.y = 1980;
menuContainer.addChild(bestWorldsText);
// Practice button
var practiceButton = LK.getAsset('escudo', {
scaleX: 1.5,
scaleY: 1.5
});
practiceButton.x = 1024;
practiceButton.y = 2300;
practiceButton.anchor.set(0.5, 0.5);
menuContainer.addChild(practiceButton);
// Practice button text
var practiceText = new Text2('PRACTICE', {
size: 70,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
practiceText.anchor.set(0.5, 0.5);
practiceText.x = 1024;
practiceText.y = 2300;
menuContainer.addChild(practiceText);
// Store practice button reference for click detection
menuContainer.practiceButton = practiceButton;
game.addChild(menuContainer);
}
// Create shop menu
function createShopMenu() {
shopContainer = new Container();
// Shop background
var shopBg = LK.getAsset('cloud', {
scaleX: 15,
scaleY: 20,
alpha: 0.3,
tint: 0x000080
});
shopBg.x = 1024;
shopBg.y = 1366;
shopContainer.addChild(shopBg);
// Shop title
var shopTitleText = new Text2('SHOP', {
size: 120,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
shopTitleText.anchor.set(0.5, 0.5);
shopTitleText.x = 1024;
shopTitleText.y = 400;
shopContainer.addChild(shopTitleText);
// Coin counter in shop
var shopCoinText = new Text2('Coins: ' + coinCount, {
size: 80,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
shopCoinText.anchor.set(0.5, 0.5);
shopCoinText.x = 1024;
shopCoinText.y = 550;
shopContainer.addChild(shopCoinText);
// Skin grid
var skinNames = ['player', 'skin', 'skin2', 'skin3', 'skin4', 'skin5', 'skin6', 'skin8', 'skin9', 'skin10', 'skin11', 'skin12'];
var skinButtons = [];
var col = 0;
var row = 0;
for (var i = 0; i < skinNames.length; i++) {
var skinName = skinNames[i];
var skinButton = new Container();
skinButton.skinName = skinName;
// Skin preview
var skinPreview = skinButton.attachAsset(skinName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Position in grid with increased spacing
var startX = 1024 - 700;
var startY = 650;
skinButton.x = startX + col * 500;
skinButton.y = startY + row * 350;
// Price or status text
var priceText;
var isOwned = ownedSkins.indexOf(skinName) !== -1;
var isCurrent = skinName === currentSkin;
if (isCurrent) {
priceText = new Text2('CURRENT', {
size: 40,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 2
});
} else if (isOwned) {
priceText = new Text2('OWNED', {
size: 40,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 2
});
} else {
priceText = new Text2(skinPrices[skinName] + ' COINS', {
size: 45,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
}
priceText.anchor.set(0.5, 0.5);
priceText.y = 100;
skinButton.addChild(priceText);
skinButton.priceText = priceText;
// Add selection border for current skin
if (isCurrent) {
var border = LK.getAsset('coin', {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0.5,
tint: 0x00FF00
});
border.anchor.set(0.5, 0.5);
skinButton.addChild(border);
}
shopContainer.addChild(skinButton);
skinButtons.push(skinButton);
col++;
if (col >= 3) {
col = 0;
row++;
}
}
// Back button
var backButton = LK.getAsset('portal', {
scaleX: 1.5,
scaleY: 1.2
});
backButton.x = 1024;
backButton.y = 2550;
backButton.anchor.set(0.5, 0.5);
shopContainer.addChild(backButton);
var backText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2550;
shopContainer.addChild(backText);
// Store references for click detection
shopContainer.skinButtons = skinButtons;
shopContainer.backButton = backButton;
shopContainer.shopCoinText = shopCoinText;
game.addChild(shopContainer);
}
// Create practice menu
function createPracticeMenu() {
practiceContainer = new Container();
// Practice background
var practiceBg = LK.getAsset('cloud', {
scaleX: 15,
scaleY: 20,
alpha: 0.3,
tint: 0x000080
});
practiceBg.x = 1024;
practiceBg.y = 1366;
practiceContainer.addChild(practiceBg);
// Practice title
var practiceTitleText = new Text2('PRACTICE MODE', {
size: 120,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 5,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 6,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 4
});
practiceTitleText.anchor.set(0.5, 0.5);
practiceTitleText.x = 1024;
practiceTitleText.y = 400;
practiceContainer.addChild(practiceTitleText);
// Instructions
var practiceInstructText = new Text2('Choose starting world', {
size: 70,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 2
});
practiceInstructText.anchor.set(0.5, 0.5);
practiceInstructText.x = 1024;
practiceInstructText.y = 550;
practiceContainer.addChild(practiceInstructText);
// World selection buttons
var worldNames = ['Sky World', 'Space World', 'Underwater World', 'Desert World', 'Ice World', 'Lava World', 'Candy Land', 'Mushroom World', 'City World'];
var worldButtons = [];
var col = 0;
var row = 0;
for (var i = 0; i < worldNames.length; i++) {
var worldButton = new Container();
worldButton.worldIndex = i;
// World button background
var buttonBg = LK.getAsset('coin', {
scaleX: 3,
scaleY: 2,
alpha: 0.7,
tint: worlds[i].bg
});
buttonBg.anchor.set(0.5, 0.5);
worldButton.addChild(buttonBg);
// World name text
var worldNameText = new Text2(worldNames[i], {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 2
});
worldNameText.anchor.set(0.5, 0.5);
worldButton.addChild(worldNameText);
// Position in grid
var startX = 1024 - 400;
var startY = 750;
worldButton.x = startX + col * 300;
worldButton.y = startY + row * 200;
practiceContainer.addChild(worldButton);
worldButtons.push(worldButton);
col++;
if (col >= 3) {
col = 0;
row++;
}
}
// Back button
var practiceBackButton = LK.getAsset('portal', {
scaleX: 1.5,
scaleY: 1.2
});
practiceBackButton.x = 1024;
practiceBackButton.y = 2400;
practiceBackButton.anchor.set(0.5, 0.5);
practiceContainer.addChild(practiceBackButton);
var practiceBackText = new Text2('BACK', {
size: 80,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4,
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 5,
dropShadowAngle: Math.PI / 4,
dropShadowDistance: 3
});
practiceBackText.anchor.set(0.5, 0.5);
practiceBackText.x = 1024;
practiceBackText.y = 2400;
practiceContainer.addChild(practiceBackText);
// Store references for click detection
practiceContainer.worldButtons = worldButtons;
practiceContainer.backButton = practiceBackButton;
game.addChild(practiceContainer);
}
// Start the game
function startGame() {
if (gameState !== 'menu') return;
isPracticeMode = false; // Reset practice mode flag
gameState = 'playing';
// Hide menu
if (menuContainer) {
menuContainer.destroy();
menuContainer = null;
}
// Reset lives
playerLives = maxLives;
isInvulnerable = false;
// Update player skin
player.destroy();
player = game.addChild(new Player());
player.x = 300;
player.y = groundY;
// Show player
player.visible = true;
// Create and show hearts UI
createHeartsUI();
// Start music
LK.playMusic('electro');
}
// Open shop
function openShop() {
if (gameState !== 'menu') return;
gameState = 'shop';
// Hide menu
if (menuContainer) {
menuContainer.visible = false;
}
createShopMenu();
}
// Close shop and return to menu
function closeShop() {
if (gameState !== 'shop') return;
gameState = 'menu';
// Hide shop
if (shopContainer) {
shopContainer.destroy();
shopContainer = null;
}
// Show menu
if (menuContainer) {
menuContainer.visible = true;
}
}
// Open practice menu
function openPractice() {
if (gameState !== 'menu') return;
gameState = 'practice';
// Hide menu
if (menuContainer) {
menuContainer.visible = false;
}
createPracticeMenu();
}
// Close practice and return to menu
function closePractice() {
if (gameState !== 'practice') return;
gameState = 'menu';
// Hide practice
if (practiceContainer) {
practiceContainer.destroy();
practiceContainer = null;
}
// Show menu
if (menuContainer) {
menuContainer.visible = true;
}
}
// Start practice mode
function startPracticeMode(worldIndex) {
if (gameState !== 'practice') return;
isPracticeMode = true;
practiceStartWorld = worldIndex;
gameState = 'playing';
// Hide practice menu
if (practiceContainer) {
practiceContainer.destroy();
practiceContainer = null;
}
// Reset game state
currentWorld = worldIndex;
worldCounter = 0; // Don't count worlds in practice
distance = 0;
coinCount = storage.coinCount || 0; // Don't modify coins in practice
// Reset lives
playerLives = maxLives;
isInvulnerable = false;
// Update player skin
player.destroy();
player = game.addChild(new Player());
player.x = 300;
player.y = groundY;
// Show player
player.visible = true;
// Create and show hearts UI
createHeartsUI();
// Set world appearance
game.setBackgroundColor(worlds[currentWorld].bg);
var worldNames = ['Sky World', 'Space World', 'Underwater World', 'Desert World', 'Ice World', 'Lava World', 'Candy Land', 'Mushroom World', 'City World'];
worldText.setText(worldNames[currentWorld]);
showWorldTitle(worldNames[currentWorld] + ' (Practice)');
// Show UI elements
distanceText.visible = true;
worldText.visible = true;
worldCounterText.visible = false; // Hide world counter in practice
coinCounterText.visible = false; // Hide coin counter in practice
// Start music
LK.playMusic('electro');
}
// Buy or select skin
function handleSkinClick(skinName) {
var isOwned = ownedSkins.indexOf(skinName) !== -1;
var price = skinPrices[skinName];
if (isOwned) {
// Switch to this skin
currentSkin = skinName;
storage.currentSkin = currentSkin;
// Refresh shop to update UI
closeShop();
openShop();
} else if (coinCount >= price) {
// Buy the skin
coinCount -= price;
storage.coinCount = coinCount;
ownedSkins.push(skinName);
storage.ownedSkins = ownedSkins;
currentSkin = skinName;
storage.currentSkin = currentSkin;
// Play coin sound
LK.getSound('coin').play();
// Refresh shop to update UI
closeShop();
openShop();
}
}
// Initialize start menu
createStartMenu();
// Initialize clouds
function createCloud() {
var cloud = new Cloud();
cloud.x = 2048 + Math.random() * 1000;
cloud.y = 200 + Math.random() * 800;
return cloud;
}
// Initialize planets
function createPlanet() {
var planet = new Planet();
planet.x = 2048 + Math.random() * 1000;
planet.y = 200 + Math.random() * 800;
return planet;
}
// Initialize bubbles
function createBubble() {
var bubble = new Bubble();
bubble.x = 2048 + Math.random() * 1000;
bubble.y = 200 + Math.random() * 800;
return bubble;
}
// Create initial clouds
for (var i = 0; i < 5; i++) {
var cloud = game.addChild(createCloud());
clouds.push(cloud);
}
// Create initial planets (hidden initially)
for (var i = 0; i < 5; i++) {
var planet = game.addChild(createPlanet());
planet.visible = false;
planets.push(planet);
}
// Create initial bubbles (hidden initially)
for (var i = 0; i < 5; i++) {
var bubble = game.addChild(createBubble());
bubble.visible = false;
bubbles.push(bubble);
}
// Initialize ground tiles
function createGroundTile(x) {
var tile = new Ground();
tile.x = x;
tile.y = groundY;
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
tile.speed = gameSpeed * speedMultiplier;
applyWorldTheme(tile, currentWorld);
return tile;
}
function applyWorldTheme(object, worldIndex) {
var world = worlds[worldIndex];
if (object.children && object.children[0]) {
object.children[0].tint = world.ground;
}
}
// Create initial ground
for (var i = 0; i < 4; i++) {
var tile = game.addChild(createGroundTile(i * 2048));
groundTiles.push(tile);
}
// Obstacle spawning
var obstacleTimer = 0;
var portalTimer = 0;
var skyObstacleTimer = 0;
var flyingItemTimer = 0;
var coinTimer = 0;
function spawnObstacle() {
var obstacle;
// In world 3, spawn Obstacle3 instead of regular Obstacle
if (currentWorld === 2) {
obstacle = new Obstacle3();
} else if (currentWorld === 3) {
// In world 4 (desert world), spawn EstepicursorObstacle (uses estepicursor asset)
obstacle = new EstepicursorObstacle();
} else if (currentWorld === 4) {
// In world 5 (ice world), spawn NieveObstacle (uses nieve asset)
obstacle = new NieveObstacle();
} else if (currentWorld === 5) {
// In world 6 (lava world), spawn VolcanObstacle (uses volcan asset)
obstacle = new VolcanObstacle();
} else if (currentWorld === 6) {
// In world 7 (candy land), spawn PaletaObstacle (uses paleta asset)
obstacle = new PaletaObstacle();
} else if (currentWorld === 7) {
// In world 8 (mushroom world), spawn HongoObstacle (uses hongo asset)
obstacle = new HongoObstacle();
} else if (currentWorld === 8) {
// In world 9 (city world), spawn PosteObstacle (uses poste asset)
obstacle = new PosteObstacle();
} else {
obstacle = new Obstacle();
}
obstacle.x = 2048 + 100;
obstacle.y = groundY;
// Apply world theme only for regular obstacles (not Obstacle3, EstepicursorObstacle, NieveObstacle, VolcanObstacle, PaletaObstacle, HongoObstacle, PosteObstacle or UlcesObstacle)
if (currentWorld !== 2 && currentWorld !== 3 && currentWorld !== 4 && currentWorld !== 5 && currentWorld !== 6 && currentWorld !== 7 && currentWorld !== 8 && obstacle.children && obstacle.children[0]) {
obstacle.children[0].tint = worlds[currentWorld].obstacle;
}
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
if (currentWorld === 3) {
// EstepicursorObstacle moves slowly (now in desert world)
obstacle.speed = gameSpeed * 0.5 * speedMultiplier;
} else {
obstacle.speed = gameSpeed * speedMultiplier;
}
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnPortal() {
var portal = new Portal();
portal.x = 2048 + 200;
portal.y = groundY - 100;
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
portal.speed = gameSpeed * speedMultiplier;
portals.push(portal);
game.addChild(portal);
currentPortalCount++;
}
function spawnSkyObstacle() {
var skyObstacle = new SkyObstacle();
skyObstacle.x = 2048 + 100;
skyObstacle.y = 400 + Math.random() * 800; // Random height in the sky
// Set speed based on current game progress
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
skyObstacle.speed = gameSpeed * speedMultiplier;
skyObstacles.push(skyObstacle);
game.addChild(skyObstacle);
}
function spawnFlyingItem() {
var flyingItem = new FlyingItem();
flyingItem.x = 2048 + 200;
flyingItem.y = 1000 + Math.random() * 400; // Random height in lower middle area
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
flyingItem.speed = gameSpeed * speedMultiplier;
flyingItems.push(flyingItem);
game.addChild(flyingItem);
}
function spawnShieldItem() {
var shieldItem = new ShieldItem();
shieldItem.x = 2048 + 200;
shieldItem.y = 1000 + Math.random() * 400; // Random height in lower middle area
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
shieldItem.speed = gameSpeed * speedMultiplier;
shieldItems.push(shieldItem);
game.addChild(shieldItem);
}
function spawnPowerUpItem() {
// Cycle between flying and shield items
if (lastItemType === 'flying') {
spawnShieldItem();
lastItemType = 'shield';
} else {
spawnFlyingItem();
lastItemType = 'flying';
}
}
function spawnCoin() {
var coin = new Coin();
coin.x = 2048 + 100;
coin.y = 1200 + Math.random() * 800; // Random height in lower area
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
coin.speed = gameSpeed * speedMultiplier;
coins.push(coin);
game.addChild(coin);
}
function spawnCoinGroup() {
// Spawn 3-5 coins in a line with some spacing
var numCoins = 3 + Math.floor(Math.random() * 3);
var startY = 1200 + Math.random() * 600;
for (var i = 0; i < numCoins; i++) {
var coin = new Coin();
coin.x = 2048 + 100 + i * 120; // Space coins 120 pixels apart
coin.y = startY + (Math.random() * 100 - 50); // Small vertical variation
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
coin.speed = gameSpeed * speedMultiplier;
coins.push(coin);
game.addChild(coin);
}
}
function createWaterEffect() {
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
// Create a subtle water overlay effect
waterEffect = new Container();
var waterOverlay = LK.getAsset('cloud', {
width: 2048,
height: 2732,
scaleX: 10,
scaleY: 10,
alpha: 0.1,
tint: 0x006064
});
waterEffect.addChild(waterOverlay);
game.addChild(waterEffect);
// Add gentle wave animation
function animateWater() {
if (waterEffect) {
tween(waterOverlay, {
alpha: 0.05
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (waterEffect) {
tween(waterOverlay, {
alpha: 0.15
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateWater
});
}
}
});
}
}
animateWater();
}
function changeWorld() {
if (worldTransitioning) {
return;
}
worldTransitioning = true;
currentWorld = (currentWorld + 1) % worlds.length;
// Flash effect
LK.effects.flashScreen(0xFFFFFF, 500);
// Update background
game.setBackgroundColor(worlds[currentWorld].bg);
// Update world text
var worldNames = ['Sky World', 'Space World', 'Underwater World', 'Desert World', 'Ice World', 'Lava World', 'Candy Land', 'Mushroom World', 'City World'];
worldText.setText(worldNames[currentWorld]);
// Show large world title
showWorldTitle(worldNames[currentWorld]);
// Handle world-specific elements
if (currentWorld === 1) {
// Space world: Hide clouds and bubbles, show planets
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = false;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = true;
}
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
} else if (currentWorld === 2) {
// Underwater world: Hide clouds and planets, show bubbles and water effect
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = true;
}
createWaterEffect();
} else if (currentWorld === 3) {
// Desert world: Hide clouds, planets and bubbles, show only static sun
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = false;
}
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
// Create or show static sun
if (!sun) {
sun = new Sun();
sun.x = 1600; // Position sun in upper right area
sun.y = 400;
game.addChild(sun);
} else {
sun.visible = true;
}
} else {
// Other worlds: Show clouds, hide planets, bubbles and sun
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = true;
}
for (var i = 0; i < planets.length; i++) {
planets[i].visible = false;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].visible = false;
}
if (sun) {
sun.visible = false;
}
if (waterEffect) {
waterEffect.destroy();
waterEffect = null;
}
}
// Update existing objects
for (var i = 0; i < groundTiles.length; i++) {
applyWorldTheme(groundTiles[i], currentWorld);
}
for (var i = 0; i < obstacles.length; i++) {
if (obstacles[i].children && obstacles[i].children[0]) {
obstacles[i].children[0].tint = worlds[currentWorld].obstacle;
}
}
LK.getSound('portal').play();
LK.setTimeout(function () {
worldTransitioning = false;
}, 500);
}
// Input handling
game.down = function (x, y, obj) {
if (gameState === 'menu') {
// Check if play button area was clicked (around the PLAY text)
var playButtonX = 1024;
var playButtonY = 1400;
var playButtonWidth = 240; // Approximate width of portal asset scaled
var playButtonHeight = 300; // Approximate height of portal asset scaled
if (x >= playButtonX - playButtonWidth / 2 && x <= playButtonX + playButtonWidth / 2 && y >= playButtonY - playButtonHeight / 2 && y <= playButtonY + playButtonHeight / 2) {
startGame();
// Show UI elements
distanceText.visible = true;
worldText.visible = true;
worldCounterText.visible = true;
coinCounterText.visible = true;
return;
}
// Check if shop button was clicked
var shopButtonX = 1024;
var shopButtonY = 2100;
var shopButtonWidth = 120; // Coin asset width scaled by 2
var shopButtonHeight = 120; // Coin asset height scaled by 2
if (x >= shopButtonX - shopButtonWidth / 2 && x <= shopButtonX + shopButtonWidth / 2 && y >= shopButtonY - shopButtonHeight / 2 && y <= shopButtonY + shopButtonHeight / 2) {
openShop();
return;
}
// Check if practice button was clicked
var practiceButtonX = 1024;
var practiceButtonY = 2300;
var practiceButtonWidth = 150; // Escudo asset width scaled by 1.5
var practiceButtonHeight = 150; // Escudo asset height scaled by 1.5
if (x >= practiceButtonX - practiceButtonWidth / 2 && x <= practiceButtonX + practiceButtonWidth / 2 && y >= practiceButtonY - practiceButtonHeight / 2 && y <= practiceButtonY + practiceButtonHeight / 2) {
openPractice();
return;
}
} else if (gameState === 'shop') {
// Check back button
var backButtonX = 1024;
var backButtonY = 2550;
var backButtonWidth = 240; // Portal asset width scaled by 1.5
var backButtonHeight = 240; // Portal asset height scaled by 1.2
if (x >= backButtonX - backButtonWidth / 2 && x <= backButtonX + backButtonWidth / 2 && y >= backButtonY - backButtonHeight / 2 && y <= backButtonY + backButtonHeight / 2) {
closeShop();
return;
}
// Check skin buttons
for (var i = 0; i < shopContainer.skinButtons.length; i++) {
var skinButton = shopContainer.skinButtons[i];
var skinButtonWidth = 280; // Adjusted for new smaller scale and increased spacing
var skinButtonHeight = 280; // Adjusted for new smaller scale and increased spacing
if (x >= skinButton.x - skinButtonWidth / 2 && x <= skinButton.x + skinButtonWidth / 2 && y >= skinButton.y - skinButtonHeight / 2 && y <= skinButton.y + skinButtonHeight / 2) {
handleSkinClick(skinButton.skinName);
return;
}
}
} else if (gameState === 'practice') {
// Check back button
var practiceBackButtonX = 1024;
var practiceBackButtonY = 2400;
var practiceBackButtonWidth = 240; // Portal asset width scaled by 1.5
var practiceBackButtonHeight = 240; // Portal asset height scaled by 1.2
if (x >= practiceBackButtonX - practiceBackButtonWidth / 2 && x <= practiceBackButtonX + practiceBackButtonWidth / 2 && y >= practiceBackButtonY - practiceBackButtonHeight / 2 && y <= practiceBackButtonY + practiceBackButtonHeight / 2) {
closePractice();
return;
}
// Check world buttons
for (var i = 0; i < practiceContainer.worldButtons.length; i++) {
var worldButton = practiceContainer.worldButtons[i];
var worldButtonWidth = 300; // World button width
var worldButtonHeight = 160; // World button height
if (x >= worldButton.x - worldButtonWidth / 2 && x <= worldButton.x + worldButtonWidth / 2 && y >= worldButton.y - worldButtonHeight / 2 && y <= worldButton.y + worldButtonHeight / 2) {
startPracticeMode(worldButton.worldIndex);
return;
}
}
} else if (gameState === 'playing') {
player.jump();
}
};
// Main game loop
game.update = function () {
// Only update game logic when playing
if (gameState !== 'playing') {
return;
}
// Update distance
distance += gameSpeed;
distanceText.setText('Distance: ' + Math.floor(distance / 10));
LK.setScore(Math.floor(distance / 10));
// Spawn obstacles
obstacleTimer++;
if (obstacleTimer > 150 + Math.random() * 100) {
spawnObstacle();
obstacleTimer = 0;
}
// Spawn portals - only one at a time
portalTimer++;
if (portalTimer > 300 + Math.random() * 200 && currentPortalCount === 0) {
spawnPortal();
portalTimer = 0;
}
// Spawn sky obstacles only in world 2 (Space World)
if (currentWorld === 1) {
skyObstacleTimer++;
if (skyObstacleTimer > 200 + Math.random() * 150) {
spawnSkyObstacle();
skyObstacleTimer = 0;
}
}
// Spawn power-up items randomly (rare) - alternates between flying and shield
flyingItemTimer++;
if (flyingItemTimer > 800 + Math.random() * 400) {
spawnPowerUpItem();
flyingItemTimer = 0;
}
// Update and remove off-screen coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.x < -100) {
coin.destroy();
coins.splice(i, 1);
continue;
}
// Coin collision
if (player.intersects(coin)) {
if (!isPracticeMode) {
coinCount++;
storage.coinCount = coinCount;
coinCounterText.setText('Coins: ' + coinCount);
}
// Play coin sound
LK.getSound('coin').play();
// Visual effect when collecting coin
tween(coin, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
coin.destroy();
}
});
coins.splice(i, 1);
}
}
// Update ground tiles
coinTimer++;
if (coinTimer > 200 + Math.random() * 150) {
if (Math.random() < 0.3) {
spawnCoinGroup(); // 30% chance for coin group
} else {
spawnCoin(); // 70% chance for single coin
}
coinTimer = 0;
}
// Update flying timer
if (isFlying) {
flyingTimer--;
if (flyingTimer <= 0) {
isFlying = false;
}
}
// Update invincibility timer
if (isInvincible) {
invincibleTimer--;
if (invincibleTimer <= 0) {
isInvincible = false;
}
}
// Update and remove off-screen obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.x < -100) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
if (player.intersects(obstacle) && !isInvulnerable && !isInvincible) {
loseLife();
if (playerLives <= 0) {
return;
}
}
}
// Update and remove off-screen portals
for (var i = portals.length - 1; i >= 0; i--) {
var portal = portals[i];
if (portal.x < -150) {
portal.destroy();
portals.splice(i, 1);
currentPortalCount = 0; // Reset count to allow new portal spawning
continue;
}
// Portal collision
if (player.intersects(portal) && !portal.used) {
portal.used = true;
if (!isPracticeMode) {
worldCounter++;
worldCounterText.setText('Mundos: ' + worldCounter);
}
changeWorld();
LK.setScore(LK.getScore() + 10);
// Remove portal immediately after use
portal.destroy();
portals.splice(i, 1);
currentPortalCount = 0; // Reset count to allow new portal spawning
}
}
// Update and remove off-screen sky obstacles
for (var i = skyObstacles.length - 1; i >= 0; i--) {
var skyObstacle = skyObstacles[i];
if (skyObstacle.x < -100) {
skyObstacle.destroy();
skyObstacles.splice(i, 1);
continue;
}
// Collision detection with sky obstacles
if (player.intersects(skyObstacle) && !isInvulnerable && !isInvincible) {
loseLife();
if (playerLives <= 0) {
return;
}
}
}
// Update and remove off-screen flying items
for (var i = flyingItems.length - 1; i >= 0; i--) {
var flyingItem = flyingItems[i];
if (flyingItem.x < -100) {
flyingItem.destroy();
flyingItems.splice(i, 1);
continue;
}
// Flying item collision
if (player.intersects(flyingItem)) {
isFlying = true;
flyingTimer = flyingDuration;
// Visual effect when collecting flying item
tween(flyingItem, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
flyingItem.destroy();
}
});
flyingItems.splice(i, 1);
}
}
// Update and remove off-screen shield items
for (var i = shieldItems.length - 1; i >= 0; i--) {
var shieldItem = shieldItems[i];
if (shieldItem.x < -100) {
shieldItem.destroy();
shieldItems.splice(i, 1);
continue;
}
// Shield item collision
if (player.intersects(shieldItem)) {
isInvincible = true;
invincibleTimer = invincibleDuration;
// Visual effect when collecting shield item
tween(shieldItem, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
shieldItem.destroy();
}
});
shieldItems.splice(i, 1);
}
}
// Update ground tiles
for (var i = groundTiles.length - 1; i >= 0; i--) {
var tile = groundTiles[i];
if (tile.x < -2048) {
// Move tile to the right
tile.x = tile.x + groundTiles.length * 2048;
applyWorldTheme(tile, currentWorld);
}
}
// Check if player fell off screen
if (player.y > 2732 + 100) {
loseLife();
if (playerLives <= 0) {
return;
}
// Reset player position when falling
player.y = groundY;
player.velocityY = 0;
}
// Update clouds (replaced with ulces in candy land world)
for (var i = clouds.length - 1; i >= 0; i--) {
var cloud = clouds[i];
if (cloud.x < -300) {
cloud.destroy();
clouds.splice(i, 1);
// Spawn new cloud or ulces based on world
var newCloud;
if (currentWorld === 6) {
// In candy land world, spawn ulces instead of clouds
newCloud = new UlcesObstacle();
newCloud.x = 2048 + Math.random() * 1000;
newCloud.y = 200 + Math.random() * 800;
newCloud.speed = 1 + Math.random() * 2;
} else {
newCloud = createCloud();
}
newCloud = game.addChild(newCloud);
newCloud.visible = currentWorld !== 1 && currentWorld !== 2 && currentWorld !== 3; // Hide in space, underwater and desert worlds
clouds.push(newCloud);
}
}
// Update planets
for (var i = planets.length - 1; i >= 0; i--) {
var planet = planets[i];
if (planet.x < -300) {
planet.destroy();
planets.splice(i, 1);
// Spawn new planet
var newPlanet = game.addChild(createPlanet());
newPlanet.visible = currentWorld === 1;
planets.push(newPlanet);
}
}
// Update bubbles
for (var i = bubbles.length - 1; i >= 0; i--) {
var bubble = bubbles[i];
if (bubble.x < -300) {
bubble.destroy();
bubbles.splice(i, 1);
// Spawn new bubble
var newBubble = game.addChild(createBubble());
newBubble.visible = currentWorld === 2;
bubbles.push(newBubble);
}
}
// Gradually increase difficulty and speed based on distance
var speedMultiplier = 1 + Math.floor(distance / 1000) * 0.3;
var currentGameSpeed = gameSpeed * speedMultiplier;
if (LK.ticks % 600 === 0) {
gameSpeed += 0.5;
}
// Update speeds for existing objects with current speed multiplier
for (var i = 0; i < obstacles.length; i++) {
// Check if it's an EstepicursorObstacle (world 4)
if (obstacles[i].constructor.name === 'EstepicursorObstacle') {
obstacles[i].speed = currentGameSpeed * 0.5; // Keep slow speed
} else {
obstacles[i].speed = currentGameSpeed;
}
}
for (var i = 0; i < portals.length; i++) {
portals[i].speed = currentGameSpeed;
}
for (var i = 0; i < groundTiles.length; i++) {
groundTiles[i].speed = currentGameSpeed;
}
for (var i = 0; i < clouds.length; i++) {
clouds[i].speed = (1 + Math.random() * 2) * speedMultiplier;
}
for (var i = 0; i < planets.length; i++) {
planets[i].speed = (1 + Math.random() * 2) * speedMultiplier;
}
for (var i = 0; i < bubbles.length; i++) {
bubbles[i].speed = (1 + Math.random() * 2) * speedMultiplier;
}
for (var i = 0; i < skyObstacles.length; i++) {
skyObstacles[i].speed = currentGameSpeed;
}
for (var i = 0; i < flyingItems.length; i++) {
flyingItems[i].speed = currentGameSpeed;
}
for (var i = 0; i < shieldItems.length; i++) {
shieldItems[i].speed = currentGameSpeed;
}
for (var i = 0; i < coins.length; i++) {
coins[i].speed = currentGameSpeed;
}
// Update and remove volcano projectiles
for (var i = volcanoProjectiles.length - 1; i >= 0; i--) {
var projectile = volcanoProjectiles[i];
if (projectile.y < -100) {
projectile.destroy();
volcanoProjectiles.splice(i, 1);
continue;
}
// Collision detection with player
if (player.intersects(projectile) && !isInvulnerable && !isInvincible) {
loseLife();
projectile.destroy();
volcanoProjectiles.splice(i, 1);
if (playerLives <= 0) {
return;
}
}
}
};
portal con manos saliendo de el. In-Game asset. 2d. High contrast. No shadows
piso futurista. In-Game asset. 2d. High contrast. No shadows
modern player of app store icon,only player,in skaetborg. In-Game asset. 2d. High contrast. No shadows
only portal colorido. In-Game asset. 2d. High contrast. No shadows
nube. In-Game asset. 2d. High contrast. No shadows
planeta. In-Game asset. 2d. High contrast. No shadows
pez. In-Game asset. 2d. High contrast. No shadows
burbuja. In-Game asset. 2d. High contrast. No shadows
cohete con ojos y dientes afilados. In-Game asset. 2d. High contrast. No shadows
corazon. In-Game asset. 2d. High contrast. No shadows
sol. In-Game asset. 2d. High contrast. No shadows
bola rodante del desierto. In-Game asset. 2d. High contrast. No shadows
muñeco de nieve. In-Game asset. 2d. High contrast. No shadows
volcan. In-Game asset. 2d. High contrast. No shadows
bola de fuego. In-Game asset. 2d. High contrast. No shadows
icon modern alas. In-Game asset. 2d. High contrast. No shadows
esfera verde. In-Game asset. 2d. High contrast. No shadows
coin. In-Game asset. 2d. High contrast. No shadows
retro pixel ar style 80s
con ropa y pelo de mujer
dulces. In-Game asset. 2d. High contrast. No shadows
paleta. In-Game asset. 2d. High contrast. No shadows
hongo. In-Game asset. 2d. High contrast. No shadows
bus stop. In-Game asset. 2d. High contrast. No shadows
con una medusa en la cabeza y traje de buzo