User prompt
separa un poco los obstaculos y mientras mas avances mas rapido valla el personaje ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
manten el tamaño pero reduce la hit box del obstaculo
User prompt
haz que el personaje pueda hacer un doble salto al clickear dos veces
Code edit (1 edits merged)
Please save this source code
User prompt
haz que el salto sea mas alto y el fondo sea un cielo con nubes que se mueve y el suelo sea futurista ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Portal Worlds
Initial prompt
hola,un juego donde avanzas y saltas a travez de portales y al pasar por el portal cambia a otro mundo
/****
* 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