/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Cloud (background)
var Cloud = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 3 + Math.random() * 2;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Crocodile (underground obstacle)
var Croc = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('croc', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 24;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Pipe (portal)
var Pipe = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 22;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Plumber (player) class
var Plumber = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('plumber', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.isJumping = false;
self.jumpVY = 0;
self.gravity = 3.5;
self.jumpPower = -55;
self.groundY = 0; // Set by game on spawn
self.state = 'surface'; // 'surface' or 'lahim'
self.update = function () {
// Gravity/jump
if (self.isJumping) {
self.y += self.jumpVY;
self.jumpVY += self.gravity;
if (self.y >= self.groundY) {
self.y = self.groundY;
self.isJumping = false;
self.jumpVY = 0;
}
}
};
// Jump method
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpVY = self.jumpPower;
LK.getSound('jump').play();
}
};
// For pipe transition
self.setState = function (state, groundY) {
self.state = state;
self.groundY = groundY;
if (self.y > groundY) self.y = groundY;
self.isJumping = false;
self.jumpVY = 0;
};
return self;
});
// Thorn (surface obstacle)
var Thorn = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('thorn', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 22;
self.update = function () {
self.x -= self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x7ecbff // blue sky
});
/****
* Game Code
****/
// Add a background for lahim section (gray bricks, pixel art style)
// darker gray for pixel art
// Sound for fail
// Sound for pipe
// Sound for jump
// Cloud - white ellipse
// Sky background - blue box
// Underground floor - dark gray box
// Ground (surface) - brown box
// Pipe (portal between worlds) - green ellipse
// Crocodile (underground obstacle) - green box
// Thorn (surface obstacle) - purple ellipse
// Plumber (player) - red box
// Constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var GROUND_HEIGHT = 120;
var LAHIM_HEIGHT = 120;
var PLUMBER_X = 420;
var SURFACE_GROUND_Y = GAME_HEIGHT - GROUND_HEIGHT;
var LAHIM_GROUND_Y = GAME_HEIGHT - LAHIM_HEIGHT;
var OBSTACLE_SPAWN_X = GAME_WIDTH + 200;
// State
var plumber;
var ground;
var lahimfloor;
var sky;
var clouds = [];
var obstacles = [];
var pipe = null;
var inLahim = false;
var score = 0;
var scoreTxt;
var lastObstacleTick = 0;
var lastCloudTick = 0;
var pipeCooldown = 0;
var dragNode = null;
var gameOver = false;
var transitionAnim = false;
// --- Setup background ---
sky = LK.getAsset('sky', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(sky);
// Clouds
function spawnCloud() {
var c = new Cloud();
c.x = GAME_WIDTH + 200 + Math.random() * 400;
c.y = 200 + Math.random() * 600;
c.speed = 2 + Math.random() * 2;
clouds.push(c);
game.addChild(c);
}
// --- Setup ground ---
ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: SURFACE_GROUND_Y
});
game.addChild(ground);
// Lahim background (hidden at start)
var lahim_bg = LK.getAsset('lahim_bg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
lahim_bg.visible = false;
game.addChild(lahim_bg);
// Underground floor (hidden at start)
lahimfloor = LK.getAsset('lahimfloor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: LAHIM_GROUND_Y
});
lahimfloor.visible = false;
game.addChild(lahimfloor);
// --- Setup plumber ---
plumber = new Plumber();
plumber.x = PLUMBER_X;
plumber.groundY = SURFACE_GROUND_Y;
plumber.y = SURFACE_GROUND_Y;
plumber.state = 'surface';
game.addChild(plumber);
// --- Setup score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Game tap/jump handler ---
game.down = function (x, y, obj) {
if (transitionAnim || gameOver) return;
plumber.jump();
};
// --- Main update loop ---
game.update = function () {
if (gameOver) return;
// Clouds
if (!inLahim && LK.ticks - lastCloudTick > 60) {
spawnCloud();
lastCloudTick = LK.ticks;
}
for (var i = clouds.length - 1; i >= 0; i--) {
var c = clouds[i];
c.update();
if (c.x < -c.width) {
c.destroy();
clouds.splice(i, 1);
}
}
// Pipe cooldown
if (pipeCooldown > 0) pipeCooldown--;
// Obstacles and pipe
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
if (obs.x < -200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with plumber
if (!transitionAnim && obs.intersects(plumber)) {
if (inLahim) {
// If it's a Croc, fail in lahim: now game over
if (obs instanceof Croc) {
failLahim();
return;
}
} else {
// Fail on surface: game over
failSurface();
return;
}
}
}
// Pipe logic
if (pipe) {
pipe.update();
if (pipe.x < -200) {
pipe.destroy();
pipe = null;
} else if (!transitionAnim && pipe.intersects(plumber)) {
enterLahim();
}
}
// Plumber update
plumber.update();
// Spawn obstacles
if (!transitionAnim && LK.ticks - lastObstacleTick > (inLahim ? 60 : 50)) {
lastObstacleTick = LK.ticks;
if (!inLahim) {
// Surface: spawn thorn or pipe
if (!pipe && Math.random() < 0.18 && pipeCooldown === 0) {
pipe = new Pipe();
pipe.x = OBSTACLE_SPAWN_X;
pipe.y = SURFACE_GROUND_Y;
game.addChild(pipe);
pipeCooldown = 180;
} else {
var thorn = new Thorn();
thorn.x = OBSTACLE_SPAWN_X;
thorn.y = SURFACE_GROUND_Y;
obstacles.push(thorn);
game.addChild(thorn);
}
} else {
// Lahim: spawn croc (no pipes or clouds in lahim)
var croc = new Croc();
croc.x = OBSTACLE_SPAWN_X;
croc.y = LAHIM_GROUND_Y;
obstacles.push(croc);
game.addChild(croc);
}
}
// Score: every tick plumber passes an obstacle, +1
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
if (!obs.passed && obs.x + obs.width / 2 < plumber.x - plumber.width / 2) {
obs.passed = true;
score++;
scoreTxt.setText(score);
}
}
};
// --- Enter lahim (underground) ---
function enterLahim() {
transitionAnim = true;
LK.getSound('pipe').play();
// Animate plumber into pipe (down)
tween(plumber, {
y: plumber.y + 200
}, {
duration: 220,
easing: tween.easeIn,
onFinish: function onFinish() {
// Switch to lahim
inLahim = true;
plumber.setState('lahim', LAHIM_GROUND_Y);
ground.visible = false;
lahimfloor.visible = true;
lahim_bg.visible = true;
sky.visible = false;
// Remove all surface obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Hide all clouds in lahim
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
if (pipe) {
pipe.destroy();
pipe = null;
}
// Animate plumber up from below
plumber.y = LAHIM_GROUND_Y + 200;
tween(plumber, {
y: LAHIM_GROUND_Y
}, {
duration: 220,
easing: tween.easeOut,
onFinish: function onFinish() {
transitionAnim = false;
}
});
}
});
}
// --- Fail in lahim: game over ---
function failLahim() {
gameOver = true;
LK.getSound('fail').play();
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// --- Fail on surface: game over ---
function failSurface() {
gameOver = true;
LK.getSound('fail').play();
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// --- Reset state on game restart ---
game.on('reset', function () {
// Remove all obstacles, clouds, pipe
for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy();
obstacles = [];
for (var i = 0; i < clouds.length; i++) {
clouds[i].destroy();
clouds[i].visible = true;
}
clouds = [];
if (pipe) {
pipe.destroy();
pipe = null;
}
inLahim = false;
plumber.setState('surface', SURFACE_GROUND_Y);
plumber.x = PLUMBER_X;
plumber.y = SURFACE_GROUND_Y;
ground.visible = true;
lahimfloor.visible = false;
lahim_bg.visible = false;
sky.visible = true;
score = 0;
scoreTxt.setText(score);
lastObstacleTick = LK.ticks;
lastCloudTick = LK.ticks;
pipeCooldown = 0;
gameOver = false;
transitionAnim = false;
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Cloud (background)
var Cloud = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 3 + Math.random() * 2;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Crocodile (underground obstacle)
var Croc = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('croc', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 24;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Pipe (portal)
var Pipe = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('pipe', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 22;
self.update = function () {
self.x -= self.speed;
};
return self;
});
// Plumber (player) class
var Plumber = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('plumber', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.isJumping = false;
self.jumpVY = 0;
self.gravity = 3.5;
self.jumpPower = -55;
self.groundY = 0; // Set by game on spawn
self.state = 'surface'; // 'surface' or 'lahim'
self.update = function () {
// Gravity/jump
if (self.isJumping) {
self.y += self.jumpVY;
self.jumpVY += self.gravity;
if (self.y >= self.groundY) {
self.y = self.groundY;
self.isJumping = false;
self.jumpVY = 0;
}
}
};
// Jump method
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpVY = self.jumpPower;
LK.getSound('jump').play();
}
};
// For pipe transition
self.setState = function (state, groundY) {
self.state = state;
self.groundY = groundY;
if (self.y > groundY) self.y = groundY;
self.isJumping = false;
self.jumpVY = 0;
};
return self;
});
// Thorn (surface obstacle)
var Thorn = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('thorn', {
anchorX: 0.5,
anchorY: 1
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 22;
self.update = function () {
self.x -= self.speed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x7ecbff // blue sky
});
/****
* Game Code
****/
// Add a background for lahim section (gray bricks, pixel art style)
// darker gray for pixel art
// Sound for fail
// Sound for pipe
// Sound for jump
// Cloud - white ellipse
// Sky background - blue box
// Underground floor - dark gray box
// Ground (surface) - brown box
// Pipe (portal between worlds) - green ellipse
// Crocodile (underground obstacle) - green box
// Thorn (surface obstacle) - purple ellipse
// Plumber (player) - red box
// Constants
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
var GROUND_HEIGHT = 120;
var LAHIM_HEIGHT = 120;
var PLUMBER_X = 420;
var SURFACE_GROUND_Y = GAME_HEIGHT - GROUND_HEIGHT;
var LAHIM_GROUND_Y = GAME_HEIGHT - LAHIM_HEIGHT;
var OBSTACLE_SPAWN_X = GAME_WIDTH + 200;
// State
var plumber;
var ground;
var lahimfloor;
var sky;
var clouds = [];
var obstacles = [];
var pipe = null;
var inLahim = false;
var score = 0;
var scoreTxt;
var lastObstacleTick = 0;
var lastCloudTick = 0;
var pipeCooldown = 0;
var dragNode = null;
var gameOver = false;
var transitionAnim = false;
// --- Setup background ---
sky = LK.getAsset('sky', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(sky);
// Clouds
function spawnCloud() {
var c = new Cloud();
c.x = GAME_WIDTH + 200 + Math.random() * 400;
c.y = 200 + Math.random() * 600;
c.speed = 2 + Math.random() * 2;
clouds.push(c);
game.addChild(c);
}
// --- Setup ground ---
ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: SURFACE_GROUND_Y
});
game.addChild(ground);
// Lahim background (hidden at start)
var lahim_bg = LK.getAsset('lahim_bg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
lahim_bg.visible = false;
game.addChild(lahim_bg);
// Underground floor (hidden at start)
lahimfloor = LK.getAsset('lahimfloor', {
anchorX: 0,
anchorY: 0,
x: 0,
y: LAHIM_GROUND_Y
});
lahimfloor.visible = false;
game.addChild(lahimfloor);
// --- Setup plumber ---
plumber = new Plumber();
plumber.x = PLUMBER_X;
plumber.groundY = SURFACE_GROUND_Y;
plumber.y = SURFACE_GROUND_Y;
plumber.state = 'surface';
game.addChild(plumber);
// --- Setup score text ---
scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Game tap/jump handler ---
game.down = function (x, y, obj) {
if (transitionAnim || gameOver) return;
plumber.jump();
};
// --- Main update loop ---
game.update = function () {
if (gameOver) return;
// Clouds
if (!inLahim && LK.ticks - lastCloudTick > 60) {
spawnCloud();
lastCloudTick = LK.ticks;
}
for (var i = clouds.length - 1; i >= 0; i--) {
var c = clouds[i];
c.update();
if (c.x < -c.width) {
c.destroy();
clouds.splice(i, 1);
}
}
// Pipe cooldown
if (pipeCooldown > 0) pipeCooldown--;
// Obstacles and pipe
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
obs.update();
if (obs.x < -200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision with plumber
if (!transitionAnim && obs.intersects(plumber)) {
if (inLahim) {
// If it's a Croc, fail in lahim: now game over
if (obs instanceof Croc) {
failLahim();
return;
}
} else {
// Fail on surface: game over
failSurface();
return;
}
}
}
// Pipe logic
if (pipe) {
pipe.update();
if (pipe.x < -200) {
pipe.destroy();
pipe = null;
} else if (!transitionAnim && pipe.intersects(plumber)) {
enterLahim();
}
}
// Plumber update
plumber.update();
// Spawn obstacles
if (!transitionAnim && LK.ticks - lastObstacleTick > (inLahim ? 60 : 50)) {
lastObstacleTick = LK.ticks;
if (!inLahim) {
// Surface: spawn thorn or pipe
if (!pipe && Math.random() < 0.18 && pipeCooldown === 0) {
pipe = new Pipe();
pipe.x = OBSTACLE_SPAWN_X;
pipe.y = SURFACE_GROUND_Y;
game.addChild(pipe);
pipeCooldown = 180;
} else {
var thorn = new Thorn();
thorn.x = OBSTACLE_SPAWN_X;
thorn.y = SURFACE_GROUND_Y;
obstacles.push(thorn);
game.addChild(thorn);
}
} else {
// Lahim: spawn croc (no pipes or clouds in lahim)
var croc = new Croc();
croc.x = OBSTACLE_SPAWN_X;
croc.y = LAHIM_GROUND_Y;
obstacles.push(croc);
game.addChild(croc);
}
}
// Score: every tick plumber passes an obstacle, +1
for (var i = obstacles.length - 1; i >= 0; i--) {
var obs = obstacles[i];
if (!obs.passed && obs.x + obs.width / 2 < plumber.x - plumber.width / 2) {
obs.passed = true;
score++;
scoreTxt.setText(score);
}
}
};
// --- Enter lahim (underground) ---
function enterLahim() {
transitionAnim = true;
LK.getSound('pipe').play();
// Animate plumber into pipe (down)
tween(plumber, {
y: plumber.y + 200
}, {
duration: 220,
easing: tween.easeIn,
onFinish: function onFinish() {
// Switch to lahim
inLahim = true;
plumber.setState('lahim', LAHIM_GROUND_Y);
ground.visible = false;
lahimfloor.visible = true;
lahim_bg.visible = true;
sky.visible = false;
// Remove all surface obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Hide all clouds in lahim
for (var i = 0; i < clouds.length; i++) {
clouds[i].visible = false;
}
if (pipe) {
pipe.destroy();
pipe = null;
}
// Animate plumber up from below
plumber.y = LAHIM_GROUND_Y + 200;
tween(plumber, {
y: LAHIM_GROUND_Y
}, {
duration: 220,
easing: tween.easeOut,
onFinish: function onFinish() {
transitionAnim = false;
}
});
}
});
}
// --- Fail in lahim: game over ---
function failLahim() {
gameOver = true;
LK.getSound('fail').play();
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// --- Fail on surface: game over ---
function failSurface() {
gameOver = true;
LK.getSound('fail').play();
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
// --- Reset state on game restart ---
game.on('reset', function () {
// Remove all obstacles, clouds, pipe
for (var i = 0; i < obstacles.length; i++) obstacles[i].destroy();
obstacles = [];
for (var i = 0; i < clouds.length; i++) {
clouds[i].destroy();
clouds[i].visible = true;
}
clouds = [];
if (pipe) {
pipe.destroy();
pipe = null;
}
inLahim = false;
plumber.setState('surface', SURFACE_GROUND_Y);
plumber.x = PLUMBER_X;
plumber.y = SURFACE_GROUND_Y;
ground.visible = true;
lahimfloor.visible = false;
lahim_bg.visible = false;
sky.visible = true;
score = 0;
scoreTxt.setText(score);
lastObstacleTick = LK.ticks;
lastCloudTick = LK.ticks;
pipeCooldown = 0;
gameOver = false;
transitionAnim = false;
});
A man with a mustache. In the style of Pixek Art.. In-Game asset. 2d. High contrast. No shadows
white thorn. flat. thick bottom. upright. pixel art.. In-Game asset. 2d. High contrast. No shadows
green pipe. pixel art.. In-Game asset. 2d. High contrast. No shadows
a grey, brick floor. pixel art.. In-Game asset. 2d. High contrast. No shadows
green crocodile. looking left. pixel art.. In-Game asset. 2d. High contrast. No shadows
sunny sky. no cloud. pixel art.. In-Game asset. 2d. High contrast. No shadows
white cloud. pixel art.. In-Game asset. 2d. High contrast. No shadows
a brown, flat strip with green top (due to short, flat grass) pixel art.. In-Game asset. 2d. High contrast. No shadows