/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0
});
self.speed = 2;
self.direction = 1;
self.patrolDistance = 200;
self.startX = 0;
self.setPatrol = function (startX, distance) {
self.startX = startX;
self.patrolDistance = distance;
};
self.update = function () {
self.x += self.speed * self.direction;
if (self.x > self.startX + self.patrolDistance || self.x < self.startX) {
self.direction *= -1;
enemyGraphics.scaleX = self.direction;
}
};
return self;
});
var FallingObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
// Thwomp-like behavior states
self.state = 'waiting'; // waiting, detecting, falling, rising
self.originalY = 0;
self.fallY = 0;
self.triggerDistance = 800; // Increased detection range
self.detectionTimer = 0;
self.detectionDelay = 5; // Much smaller delay to make obstacles fall much before player passes underneath
self.lastPlayerInRange = false;
self.update = function () {
if (!player) return;
var distanceToPlayer = Math.abs(player.x - self.x);
// Player is below obstacle when they are close horizontally and below vertically
var playerInRange = distanceToPlayer < 300 && player.y > self.originalY; // Increased horizontal detection range
// State machine for Thwomp behavior
if (self.state === 'waiting') {
// Check if player enters detection range (directly below)
if (!self.lastPlayerInRange && playerInRange) {
self.state = 'detecting';
self.detectionTimer = 0;
// No animation before falling
}
} else if (self.state === 'detecting') {
self.detectionTimer += 1000 / 60; // Add frame time
if (self.detectionTimer >= self.detectionDelay) {
self.state = 'falling';
// Play axe falling sound
LK.getSound('axe_fall').play();
// Fall very fast like a Thwomp
tween(self, {
y: self.fallY
}, {
duration: 50,
easing: tween.easeIn,
onFinish: function onFinish() {
// Stay down briefly, then rise slowly
LK.setTimeout(function () {
self.state = 'rising';
tween(self, {
y: self.originalY
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.state = 'waiting';
// Reset last player range to allow re-triggering
self.lastPlayerInRange = false;
}
});
}, 800);
}
});
}
} else if (self.state === 'rising') {
// Don't detect player while rising
self.lastPlayerInRange = true;
}
self.lastPlayerInRange = playerInRange;
};
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 1.0
});
// Animate goal with gentle pulsing
tween(goalGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(goalGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart animation
tween(goalGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
}
});
return self;
});
var Platform = Container.expand(function (width, height) {
var self = Container.call(this);
var platformGraphics = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: width / 200,
scaleY: height / 40
});
self.width = width;
self.height = height;
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 8;
self.facing = 1; // 1 for right, -1 for left
self.moveLeft = function () {
self.velocityX = -self.speed;
self.facing = -1;
playerGraphics.scaleX = -1;
};
self.moveRight = function () {
self.velocityX = self.speed;
self.facing = 1;
playerGraphics.scaleX = 1;
};
self.stop = function () {
self.velocityX = 0;
};
self.update = function () {
// Apply velocity
self.x += self.velocityX;
// Apply friction
self.velocityX *= 0.8;
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xD3D3D3
});
/****
* Game Code
****/
// Add background image
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 2048 / 2048,
scaleY: 2732 / 2732
}));
var player;
var platforms = [];
var enemies = [];
var spikes = [];
var goal;
var gameStarted = false;
var levelStartTime = 0;
var currentLevel = 1;
var maxLevel = storage.maxLevel || 1;
var musicPlaying = false;
var musicPosition = 0;
var normalMusicVolume = 1.0;
var slowMusicVolume = 0.3;
var currentMusicSpeed = 1.0;
var normalMusicSpeed = 1.0;
var slowMusicSpeed = 0.5;
// UI Elements
var levelText = new Text2('Level 1', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
levelText.y = 50;
var instructionText = new Text2('manten pulsado para avanzar', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
function createLevel(levelNum) {
// Clear existing level
for (var i = platforms.length - 1; i >= 0; i--) {
platforms[i].destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
for (var i = spikes.length - 1; i >= 0; i--) {
spikes[i].destroy();
}
if (goal) {
goal.destroy();
}
platforms = [];
enemies = [];
spikes = [];
// Create one very long level
// Ground platform - very long continuous ground that covers the entire bottom
var platform1 = game.addChild(new Platform(16000, 432));
platform1.x = 4000;
platform1.y = 2516;
platforms.push(platform1);
// Create Thwomp-like falling obstacles with more spacing and random positions
var basePositions = [800, 1600, 2400, 3200, 4000, 4800, 5600, 6400];
for (var i = 0; i < basePositions.length; i++) {
var obstacle = game.addChild(new FallingObstacle());
// Add random offset to make positions less predictable
var randomOffset = (Math.random() - 0.5) * 200; // Random offset between -100 and +100
obstacle.x = basePositions[i] + randomOffset;
obstacle.y = 2100; // Start position in air
obstacle.originalY = 2100;
obstacle.fallY = 2420; // Where it falls to (just above ground)
obstacle.triggerDistance = 400;
spikes.push(obstacle); // Add to spikes array since they kill the player
}
// Goal at the end
goal = game.addChild(new Goal());
goal.x = 7500;
goal.y = 2500;
}
function startGame() {
if (gameStarted) return;
gameStarted = true;
levelStartTime = Date.now();
instructionText.visible = false;
// Create level first
createLevel(currentLevel);
// Create player after level (so player renders on top)
player = game.addChild(new Player());
player.x = 200;
player.y = 2500;
levelText.setText('emanuel el caballero');
}
function restartLevel() {
LK.getSound('damage').play();
LK.effects.flashScreen(0xFF0000, 500);
if (player) {
player.destroy();
}
// Reset player (create after existing platforms so player renders on top)
player = game.addChild(new Player());
player.x = 200;
player.y = 2500;
levelStartTime = Date.now();
}
function nextLevel() {
// Game completed - single long level
LK.showYouWin();
}
// Touch controls
var rightPressed = false;
var walkSoundInterval = null;
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
return;
}
// Start music if not already playing or restore normal speed/volume
if (!musicPlaying) {
LK.playMusic('Musiquita', {
volume: normalMusicVolume
});
musicPlaying = true;
}
// Tween music back to normal speed and volume
tween({
speed: currentMusicSpeed,
volume: slowMusicVolume
}, {
speed: normalMusicSpeed,
volume: normalMusicVolume
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
currentMusicSpeed = normalMusicSpeed;
}
});
// Only allow right movement
rightPressed = true;
// Start walking sound loop
if (walkSoundInterval) {
LK.clearInterval(walkSoundInterval);
}
walkSoundInterval = LK.setInterval(function () {
LK.getSound('walk').play();
}, 200); // Play every 200ms for continuous walking sound
};
game.up = function (x, y, obj) {
rightPressed = false;
if (player) {
player.stop();
}
// Slow down music and reduce volume instead of stopping
if (musicPlaying) {
tween({
speed: currentMusicSpeed,
volume: normalMusicVolume
}, {
speed: slowMusicSpeed,
volume: slowMusicVolume
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
currentMusicSpeed = slowMusicSpeed;
}
});
}
// Stop walking sound loop
if (walkSoundInterval) {
LK.clearInterval(walkSoundInterval);
walkSoundInterval = null;
}
};
game.update = function () {
if (!gameStarted || !player) return;
// Handle continuous movement - only right
if (rightPressed) {
player.moveRight();
} else {
player.stop();
}
// Thwomp obstacles handle their own behavior in their update methods
// Check enemy collisions
for (var i = 0; i < enemies.length; i++) {
if (player.intersects(enemies[i])) {
restartLevel();
return;
}
}
// Check spike collisions
for (var i = 0; i < spikes.length; i++) {
if (player.intersects(spikes[i])) {
restartLevel();
return;
}
}
// Check goal collision
if (goal && player.intersects(goal)) {
LK.getSound('collect').play();
var completionTime = Math.floor((Date.now() - levelStartTime) / 1000);
LK.setScore(LK.getScore() + Math.max(100 - completionTime, 10));
nextLevel();
}
// Camera follow player with smooth interpolation
var targetX = -(player.x - 1024); // Center player on screen
var targetY = -(player.y - 1366); // Center player vertically
game.x += (targetX - game.x) * 0.1;
game.y += (targetY - game.y) * 0.1;
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 1.0
});
self.speed = 2;
self.direction = 1;
self.patrolDistance = 200;
self.startX = 0;
self.setPatrol = function (startX, distance) {
self.startX = startX;
self.patrolDistance = distance;
};
self.update = function () {
self.x += self.speed * self.direction;
if (self.x > self.startX + self.patrolDistance || self.x < self.startX) {
self.direction *= -1;
enemyGraphics.scaleX = self.direction;
}
};
return self;
});
var FallingObstacle = Container.expand(function () {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
// Thwomp-like behavior states
self.state = 'waiting'; // waiting, detecting, falling, rising
self.originalY = 0;
self.fallY = 0;
self.triggerDistance = 800; // Increased detection range
self.detectionTimer = 0;
self.detectionDelay = 5; // Much smaller delay to make obstacles fall much before player passes underneath
self.lastPlayerInRange = false;
self.update = function () {
if (!player) return;
var distanceToPlayer = Math.abs(player.x - self.x);
// Player is below obstacle when they are close horizontally and below vertically
var playerInRange = distanceToPlayer < 300 && player.y > self.originalY; // Increased horizontal detection range
// State machine for Thwomp behavior
if (self.state === 'waiting') {
// Check if player enters detection range (directly below)
if (!self.lastPlayerInRange && playerInRange) {
self.state = 'detecting';
self.detectionTimer = 0;
// No animation before falling
}
} else if (self.state === 'detecting') {
self.detectionTimer += 1000 / 60; // Add frame time
if (self.detectionTimer >= self.detectionDelay) {
self.state = 'falling';
// Play axe falling sound
LK.getSound('axe_fall').play();
// Fall very fast like a Thwomp
tween(self, {
y: self.fallY
}, {
duration: 50,
easing: tween.easeIn,
onFinish: function onFinish() {
// Stay down briefly, then rise slowly
LK.setTimeout(function () {
self.state = 'rising';
tween(self, {
y: self.originalY
}, {
duration: 3000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.state = 'waiting';
// Reset last player range to allow re-triggering
self.lastPlayerInRange = false;
}
});
}, 800);
}
});
}
} else if (self.state === 'rising') {
// Don't detect player while rising
self.lastPlayerInRange = true;
}
self.lastPlayerInRange = playerInRange;
};
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 1.0
});
// Animate goal with gentle pulsing
tween(goalGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(goalGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart animation
tween(goalGraphics, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
}
});
return self;
});
var Platform = Container.expand(function (width, height) {
var self = Container.call(this);
var platformGraphics = self.attachAsset('ground', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: width / 200,
scaleY: height / 40
});
self.width = width;
self.height = height;
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 1.0
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 8;
self.facing = 1; // 1 for right, -1 for left
self.moveLeft = function () {
self.velocityX = -self.speed;
self.facing = -1;
playerGraphics.scaleX = -1;
};
self.moveRight = function () {
self.velocityX = self.speed;
self.facing = 1;
playerGraphics.scaleX = 1;
};
self.stop = function () {
self.velocityX = 0;
};
self.update = function () {
// Apply velocity
self.x += self.velocityX;
// Apply friction
self.velocityX *= 0.8;
};
return self;
});
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xD3D3D3
});
/****
* Game Code
****/
// Add background image
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 2048 / 2048,
scaleY: 2732 / 2732
}));
var player;
var platforms = [];
var enemies = [];
var spikes = [];
var goal;
var gameStarted = false;
var levelStartTime = 0;
var currentLevel = 1;
var maxLevel = storage.maxLevel || 1;
var musicPlaying = false;
var musicPosition = 0;
var normalMusicVolume = 1.0;
var slowMusicVolume = 0.3;
var currentMusicSpeed = 1.0;
var normalMusicSpeed = 1.0;
var slowMusicSpeed = 0.5;
// UI Elements
var levelText = new Text2('Level 1', {
size: 80,
fill: 0xFFFFFF
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
levelText.y = 50;
var instructionText = new Text2('manten pulsado para avanzar', {
size: 60,
fill: 0xFFFFFF
});
instructionText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(instructionText);
function createLevel(levelNum) {
// Clear existing level
for (var i = platforms.length - 1; i >= 0; i--) {
platforms[i].destroy();
}
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
}
for (var i = spikes.length - 1; i >= 0; i--) {
spikes[i].destroy();
}
if (goal) {
goal.destroy();
}
platforms = [];
enemies = [];
spikes = [];
// Create one very long level
// Ground platform - very long continuous ground that covers the entire bottom
var platform1 = game.addChild(new Platform(16000, 432));
platform1.x = 4000;
platform1.y = 2516;
platforms.push(platform1);
// Create Thwomp-like falling obstacles with more spacing and random positions
var basePositions = [800, 1600, 2400, 3200, 4000, 4800, 5600, 6400];
for (var i = 0; i < basePositions.length; i++) {
var obstacle = game.addChild(new FallingObstacle());
// Add random offset to make positions less predictable
var randomOffset = (Math.random() - 0.5) * 200; // Random offset between -100 and +100
obstacle.x = basePositions[i] + randomOffset;
obstacle.y = 2100; // Start position in air
obstacle.originalY = 2100;
obstacle.fallY = 2420; // Where it falls to (just above ground)
obstacle.triggerDistance = 400;
spikes.push(obstacle); // Add to spikes array since they kill the player
}
// Goal at the end
goal = game.addChild(new Goal());
goal.x = 7500;
goal.y = 2500;
}
function startGame() {
if (gameStarted) return;
gameStarted = true;
levelStartTime = Date.now();
instructionText.visible = false;
// Create level first
createLevel(currentLevel);
// Create player after level (so player renders on top)
player = game.addChild(new Player());
player.x = 200;
player.y = 2500;
levelText.setText('emanuel el caballero');
}
function restartLevel() {
LK.getSound('damage').play();
LK.effects.flashScreen(0xFF0000, 500);
if (player) {
player.destroy();
}
// Reset player (create after existing platforms so player renders on top)
player = game.addChild(new Player());
player.x = 200;
player.y = 2500;
levelStartTime = Date.now();
}
function nextLevel() {
// Game completed - single long level
LK.showYouWin();
}
// Touch controls
var rightPressed = false;
var walkSoundInterval = null;
game.down = function (x, y, obj) {
if (!gameStarted) {
startGame();
return;
}
// Start music if not already playing or restore normal speed/volume
if (!musicPlaying) {
LK.playMusic('Musiquita', {
volume: normalMusicVolume
});
musicPlaying = true;
}
// Tween music back to normal speed and volume
tween({
speed: currentMusicSpeed,
volume: slowMusicVolume
}, {
speed: normalMusicSpeed,
volume: normalMusicVolume
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
currentMusicSpeed = normalMusicSpeed;
}
});
// Only allow right movement
rightPressed = true;
// Start walking sound loop
if (walkSoundInterval) {
LK.clearInterval(walkSoundInterval);
}
walkSoundInterval = LK.setInterval(function () {
LK.getSound('walk').play();
}, 200); // Play every 200ms for continuous walking sound
};
game.up = function (x, y, obj) {
rightPressed = false;
if (player) {
player.stop();
}
// Slow down music and reduce volume instead of stopping
if (musicPlaying) {
tween({
speed: currentMusicSpeed,
volume: normalMusicVolume
}, {
speed: slowMusicSpeed,
volume: slowMusicVolume
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
currentMusicSpeed = slowMusicSpeed;
}
});
}
// Stop walking sound loop
if (walkSoundInterval) {
LK.clearInterval(walkSoundInterval);
walkSoundInterval = null;
}
};
game.update = function () {
if (!gameStarted || !player) return;
// Handle continuous movement - only right
if (rightPressed) {
player.moveRight();
} else {
player.stop();
}
// Thwomp obstacles handle their own behavior in their update methods
// Check enemy collisions
for (var i = 0; i < enemies.length; i++) {
if (player.intersects(enemies[i])) {
restartLevel();
return;
}
}
// Check spike collisions
for (var i = 0; i < spikes.length; i++) {
if (player.intersects(spikes[i])) {
restartLevel();
return;
}
}
// Check goal collision
if (goal && player.intersects(goal)) {
LK.getSound('collect').play();
var completionTime = Math.floor((Date.now() - levelStartTime) / 1000);
LK.setScore(LK.getScore() + Math.max(100 - completionTime, 10));
nextLevel();
}
// Camera follow player with smooth interpolation
var targetX = -(player.x - 1024); // Center player on screen
var targetY = -(player.y - 1366); // Center player vertically
game.x += (targetX - game.x) * 0.1;
game.y += (targetY - game.y) * 0.1;
};