/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { level: 1, stars: 0 }); /**** * Classes ****/ // Exit (level goal) class var Exit = Container.expand(function () { var self = Container.call(this); var exit = self.attachAsset('exit', { anchorX: 0.5, anchorY: 1 }); return self; }); // Parallax background layer class var ParallaxLayer = Container.expand(function () { var self = Container.call(this); self.speed = 0; self.sprite = null; self.setLayer = function (id, speed) { self.sprite = self.attachAsset(id, { anchorX: 0, anchorY: 0 }); self.speed = speed; }; self.update = function (dx) { if (self.sprite) { self.x -= dx * self.speed; if (self.x < -2048) self.x += 2048; if (self.x > 2048) self.x -= 2048; } }; return self; }); // Platform class var Platform = Container.expand(function () { var self = Container.call(this); var plat = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); return self; }); // Runner (player) class var Runner = Container.expand(function () { var self = Container.call(this); var runnerSprite = self.attachAsset('runner', { anchorX: 0.5, anchorY: 1 }); // Physics self.vx = 0; self.vy = 0; self.onGround = false; self.doubleJumped = false; self.wallJumped = false; self.facing = 1; // 1: right, -1: left self.isSliding = false; self.slideTimer = 0; self.jumpBuffer = 0; self.dead = false; // Movement constants self.speed = 16; self.jumpPower = 48; self.gravity = 4; self.maxFall = 48; self.slideDuration = 18; self.slideSpeed = 24; self.wallJumpPower = 44; self.wallJumpH = 18; // For wall jump detection self.touchingWall = 0; // -1: left, 1: right, 0: none // Update per frame self.update = function () { if (self.dead) return; // Horizontal movement (manual, only if arrow pressed) if (leftPressed && !rightPressed) { self.vx = self.isSliding ? self.slideSpeed * -1 : self.speed * -1; } else if (rightPressed && !leftPressed) { self.vx = self.isSliding ? self.slideSpeed * 1 : self.speed * 1; } else { self.vx = 0; } // Gravity self.vy += self.gravity; if (self.vy > self.maxFall) self.vy = self.maxFall; // Slide timer if (self.isSliding) { self.slideTimer--; if (self.slideTimer <= 0) { self.isSliding = false; runnerSprite.height = 180; runnerSprite.y = 0; } } // Apply movement self.x += self.vx; self.y += self.vy; // Clamp to world bounds if (self.x < 60) self.x = 60; if (self.x > 2048 - 60) self.x = 2048 - 60; if (self.y > 2732 - 60) { self.y = 2732 - 60; self.vy = 0; self.onGround = true; self.doubleJumped = false; self.wallJumped = false; } }; // Jump self.jump = function () { if (self.dead) return; if (self.onGround) { self.vy = -self.jumpPower; self.onGround = false; self.doubleJumped = false; LK.getSound('jump').play(); } else if (!self.doubleJumped) { self.vy = -self.jumpPower * 0.9; self.doubleJumped = true; LK.getSound('jump').play(); } else if (self.touchingWall !== 0 && !self.wallJumped) { self.vy = -self.wallJumpPower; self.vx = self.wallJumpH * -self.touchingWall; self.wallJumped = true; self.facing = -self.touchingWall; LK.getSound('jump').play(); } }; // Slide self.slide = function () { if (self.dead) return; if (self.onGround && !self.isSliding) { self.isSliding = true; self.slideTimer = self.slideDuration; runnerSprite.height = 90; runnerSprite.y = 90; } }; // Die self.die = function () { if (self.dead) return; self.dead = true; LK.getSound('death').play(); tween(self, { alpha: 0 }, { duration: 400, easing: tween.linear, onFinish: function onFinish() { LK.effects.flashScreen(0xff0000, 600); LK.showGameOver(); } }); }; return self; }); // Spike (obstacle) class var Spike = Container.expand(function () { var self = Container.call(this); var spike = self.attachAsset('spike', { anchorX: 0.5, anchorY: 1 }); return self; }); // Star collectible class var Star = Container.expand(function () { var self = Container.call(this); var star = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); self.collected = false; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222a38 }); /**** * Game Code ****/ // Music // Sound effects // Parallax background layers // Level exit // Star collectible // Obstacle (spike) // Ground platform // Silhouette runner (player) // --- Level Data (MVP: 1 level, can expand later) --- var levelData = { platforms: [{ x: 1024, y: 2600, w: 1200 }, // ground { x: 600, y: 2200, w: 400 }, { x: 1400, y: 2000, w: 400 }, { x: 900, y: 1700, w: 300 }, { x: 400, y: 1400, w: 300 }, { x: 1200, y: 1200, w: 400 }, { x: 1800, y: 900, w: 300 }], spikes: [{ x: 800, y: 2540 }, { x: 1300, y: 2540 }, { x: 1400, y: 1940 }, { x: 1200, y: 1140 }], stars: [{ x: 600, y: 2100 }, { x: 900, y: 1600 }, { x: 1800, y: 800 }], exit: { x: 1800, y: 740 } }; // --- Parallax Background --- var bg_far = new ParallaxLayer(); bg_far.setLayer('bg_far', 0.1); game.addChild(bg_far); var bg_mid = new ParallaxLayer(); bg_mid.setLayer('bg_mid', 0.2); game.addChild(bg_mid); var bg_near = new ParallaxLayer(); bg_near.setLayer('bg_near', 0.3); game.addChild(bg_near); // --- Platforms --- var platforms = []; for (var i = 0; i < levelData.platforms.length; i++) { var p = new Platform(); p.x = levelData.platforms[i].x; p.y = levelData.platforms[i].y; p.width = levelData.platforms[i].w; p.children[0].width = levelData.platforms[i].w; platforms.push(p); game.addChild(p); } // --- Spikes --- var spikes = []; for (var i = 0; i < levelData.spikes.length; i++) { var s = new Spike(); s.x = levelData.spikes[i].x; s.y = levelData.spikes[i].y; spikes.push(s); game.addChild(s); } // --- Stars --- var stars = []; for (var i = 0; i < levelData.stars.length; i++) { var st = new Star(); st.x = levelData.stars[i].x; st.y = levelData.stars[i].y; stars.push(st); game.addChild(st); } // --- Exit --- var exit = new Exit(); exit.x = levelData.exit.x; exit.y = levelData.exit.y; game.addChild(exit); // --- Runner (Player) --- var runner = new Runner(); runner.x = 200; runner.y = 2500; game.addChild(runner); // --- Camera --- var cameraY = 0; // --- Score/Stars UI --- var starTxt = new Text2('0', { size: 100, fill: 0xFFE066 }); starTxt.anchor.set(0, 0); LK.gui.top.addChild(starTxt); starTxt.x = 120; starTxt.y = 10; // --- Level UI --- var levelTxt = new Text2('Level 1', { size: 80, fill: 0xFFFFFF }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); levelTxt.x = 1024; levelTxt.y = 10; // --- Touch Controls (Jump/Slide/Left/Right) --- // Left Arrow Button var leftBtn = new Text2('←', { size: 120, fill: 0xFFFFFF }); leftBtn.anchor.set(0.5, 1); LK.gui.bottomLeft.addChild(leftBtn); leftBtn.x = 80; leftBtn.y = -40; // Right Arrow Button var rightBtn = new Text2('→', { size: 120, fill: 0xFFFFFF }); rightBtn.anchor.set(0.5, 1); LK.gui.bottomLeft.addChild(rightBtn); rightBtn.x = 320; rightBtn.y = -40; // Jump Button var jumpBtn = new Text2('JUMP', { size: 80, fill: 0xFFFFFF }); jumpBtn.anchor.set(0.5, 1); LK.gui.bottomLeft.addChild(jumpBtn); jumpBtn.x = 560; jumpBtn.y = -40; // Slide Button var slideBtn = new Text2('SLIDE', { size: 80, fill: 0xFFFFFF }); slideBtn.anchor.set(0.5, 1); LK.gui.bottomRight.addChild(slideBtn); slideBtn.x = -200; slideBtn.y = -40; // --- Touch Controls State for Arrows --- var leftPressed = false; var rightPressed = false; // --- Arrow Button Events --- leftBtn.down = function () { leftPressed = true; rightPressed = false; runner.facing = -1; }; leftBtn.up = function () { leftPressed = false; }; rightBtn.down = function () { rightPressed = true; leftPressed = false; runner.facing = 1; }; rightBtn.up = function () { rightPressed = false; }; // --- State --- var collectedStars = 0; var levelComplete = false; // --- Helper: AABB collision --- function aabb(a, b) { return a.x - a.width / 2 < b.x + b.width / 2 && a.x + a.width / 2 > b.x - b.width / 2 && a.y - a.height < b.y && a.y > b.y - b.height; } // --- Game Update --- game.update = function () { // Camera follows runner var targetY = runner.y - 1800; cameraY += (targetY - cameraY) * 0.15; game.y = -cameraY; // Parallax backgrounds bg_far.update(runner.vx * 0.5); bg_mid.update(runner.vx * 0.7); bg_near.update(runner.vx); // Runner physics runner.onGround = false; runner.touchingWall = 0; // --- Arrow Button Direction Control --- if (leftPressed && !rightPressed) { runner.facing = -1; } else if (rightPressed && !leftPressed) { runner.facing = 1; } // Platform collision for (var i = 0; i < platforms.length; i++) { var p = platforms[i]; // Only check if close if (Math.abs(runner.x - p.x) < p.width / 2 + 80 && Math.abs(runner.y - p.y) < 200) { // Vertical collision (landing) if (runner.y + runner.vy >= p.y - 30 && runner.y < p.y && runner.x > p.x - p.width / 2 + 30 && runner.x < p.x + p.width / 2 - 30 && runner.vy >= 0) { runner.y = p.y - 30; runner.vy = 0; runner.onGround = true; runner.doubleJumped = false; runner.wallJumped = false; } // Wall collision (left) if (runner.x + runner.vx > p.x - p.width / 2 - 60 && runner.x < p.x - p.width / 2 && runner.y > p.y - 120 && runner.y < p.y + 30) { runner.x = p.x - p.width / 2 - 60; runner.vx = 0; runner.touchingWall = -1; } // Wall collision (right) if (runner.x + runner.vx < p.x + p.width / 2 + 60 && runner.x > p.x + p.width / 2 && runner.y > p.y - 120 && runner.y < p.y + 30) { runner.x = p.x + p.width / 2 + 60; runner.vx = 0; runner.touchingWall = 1; } } } // Spikes collision for (var i = 0; i < spikes.length; i++) { var s = spikes[i]; if (!runner.dead && Math.abs(runner.x - s.x) < 60 && Math.abs(runner.y - s.y) < 60) { runner.die(); } } // Star collection for (var i = 0; i < stars.length; i++) { var st = stars[i]; if (!st.collected && Math.abs(runner.x - st.x) < 60 && Math.abs(runner.y - st.y) < 80) { st.collected = true; tween(st, { alpha: 0, y: st.y - 80 }, { duration: 400, onFinish: function onFinish() { st.destroy(); } }); collectedStars++; LK.getSound('star').play(); starTxt.setText(collectedStars); } } // Exit collision if (!levelComplete && Math.abs(runner.x - exit.x) < 80 && Math.abs(runner.y - exit.y) < 160) { levelComplete = true; LK.getSound('exit').play(); tween(runner, { alpha: 0 }, { duration: 400, onFinish: function onFinish() { // Save progress storage.level = Math.max(storage.level, 2); storage.stars = (storage.stars || 0) + collectedStars; LK.setScore(collectedStars); LK.showYouWin(); } }); } }; // --- Touch Controls --- jumpBtn.down = function () { runner.jump(); }; slideBtn.down = function () { runner.slide(); }; // --- Swipe Controls (for mobile) --- var swipeStartY = null; game.down = function (x, y, obj) { swipeStartY = y; }; game.up = function (x, y, obj) { if (swipeStartY !== null) { var dy = y - swipeStartY; if (dy < -80) { runner.jump(); } else if (dy > 80) { runner.slide(); } } swipeStartY = null; }; // --- Music --- LK.playMusic('bgm1'); // --- Init UI --- starTxt.setText(collectedStars); levelTxt.setText('Level 1');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
stars: 0
});
/****
* Classes
****/
// Exit (level goal) class
var Exit = Container.expand(function () {
var self = Container.call(this);
var exit = self.attachAsset('exit', {
anchorX: 0.5,
anchorY: 1
});
return self;
});
// Parallax background layer class
var ParallaxLayer = Container.expand(function () {
var self = Container.call(this);
self.speed = 0;
self.sprite = null;
self.setLayer = function (id, speed) {
self.sprite = self.attachAsset(id, {
anchorX: 0,
anchorY: 0
});
self.speed = speed;
};
self.update = function (dx) {
if (self.sprite) {
self.x -= dx * self.speed;
if (self.x < -2048) self.x += 2048;
if (self.x > 2048) self.x -= 2048;
}
};
return self;
});
// Platform class
var Platform = Container.expand(function () {
var self = Container.call(this);
var plat = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Runner (player) class
var Runner = Container.expand(function () {
var self = Container.call(this);
var runnerSprite = self.attachAsset('runner', {
anchorX: 0.5,
anchorY: 1
});
// Physics
self.vx = 0;
self.vy = 0;
self.onGround = false;
self.doubleJumped = false;
self.wallJumped = false;
self.facing = 1; // 1: right, -1: left
self.isSliding = false;
self.slideTimer = 0;
self.jumpBuffer = 0;
self.dead = false;
// Movement constants
self.speed = 16;
self.jumpPower = 48;
self.gravity = 4;
self.maxFall = 48;
self.slideDuration = 18;
self.slideSpeed = 24;
self.wallJumpPower = 44;
self.wallJumpH = 18;
// For wall jump detection
self.touchingWall = 0; // -1: left, 1: right, 0: none
// Update per frame
self.update = function () {
if (self.dead) return;
// Horizontal movement (manual, only if arrow pressed)
if (leftPressed && !rightPressed) {
self.vx = self.isSliding ? self.slideSpeed * -1 : self.speed * -1;
} else if (rightPressed && !leftPressed) {
self.vx = self.isSliding ? self.slideSpeed * 1 : self.speed * 1;
} else {
self.vx = 0;
}
// Gravity
self.vy += self.gravity;
if (self.vy > self.maxFall) self.vy = self.maxFall;
// Slide timer
if (self.isSliding) {
self.slideTimer--;
if (self.slideTimer <= 0) {
self.isSliding = false;
runnerSprite.height = 180;
runnerSprite.y = 0;
}
}
// Apply movement
self.x += self.vx;
self.y += self.vy;
// Clamp to world bounds
if (self.x < 60) self.x = 60;
if (self.x > 2048 - 60) self.x = 2048 - 60;
if (self.y > 2732 - 60) {
self.y = 2732 - 60;
self.vy = 0;
self.onGround = true;
self.doubleJumped = false;
self.wallJumped = false;
}
};
// Jump
self.jump = function () {
if (self.dead) return;
if (self.onGround) {
self.vy = -self.jumpPower;
self.onGround = false;
self.doubleJumped = false;
LK.getSound('jump').play();
} else if (!self.doubleJumped) {
self.vy = -self.jumpPower * 0.9;
self.doubleJumped = true;
LK.getSound('jump').play();
} else if (self.touchingWall !== 0 && !self.wallJumped) {
self.vy = -self.wallJumpPower;
self.vx = self.wallJumpH * -self.touchingWall;
self.wallJumped = true;
self.facing = -self.touchingWall;
LK.getSound('jump').play();
}
};
// Slide
self.slide = function () {
if (self.dead) return;
if (self.onGround && !self.isSliding) {
self.isSliding = true;
self.slideTimer = self.slideDuration;
runnerSprite.height = 90;
runnerSprite.y = 90;
}
};
// Die
self.die = function () {
if (self.dead) return;
self.dead = true;
LK.getSound('death').play();
tween(self, {
alpha: 0
}, {
duration: 400,
easing: tween.linear,
onFinish: function onFinish() {
LK.effects.flashScreen(0xff0000, 600);
LK.showGameOver();
}
});
};
return self;
});
// Spike (obstacle) class
var Spike = Container.expand(function () {
var self = Container.call(this);
var spike = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1
});
return self;
});
// Star collectible class
var Star = Container.expand(function () {
var self = Container.call(this);
var star = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222a38
});
/****
* Game Code
****/
// Music
// Sound effects
// Parallax background layers
// Level exit
// Star collectible
// Obstacle (spike)
// Ground platform
// Silhouette runner (player)
// --- Level Data (MVP: 1 level, can expand later) ---
var levelData = {
platforms: [{
x: 1024,
y: 2600,
w: 1200
},
// ground
{
x: 600,
y: 2200,
w: 400
}, {
x: 1400,
y: 2000,
w: 400
}, {
x: 900,
y: 1700,
w: 300
}, {
x: 400,
y: 1400,
w: 300
}, {
x: 1200,
y: 1200,
w: 400
}, {
x: 1800,
y: 900,
w: 300
}],
spikes: [{
x: 800,
y: 2540
}, {
x: 1300,
y: 2540
}, {
x: 1400,
y: 1940
}, {
x: 1200,
y: 1140
}],
stars: [{
x: 600,
y: 2100
}, {
x: 900,
y: 1600
}, {
x: 1800,
y: 800
}],
exit: {
x: 1800,
y: 740
}
};
// --- Parallax Background ---
var bg_far = new ParallaxLayer();
bg_far.setLayer('bg_far', 0.1);
game.addChild(bg_far);
var bg_mid = new ParallaxLayer();
bg_mid.setLayer('bg_mid', 0.2);
game.addChild(bg_mid);
var bg_near = new ParallaxLayer();
bg_near.setLayer('bg_near', 0.3);
game.addChild(bg_near);
// --- Platforms ---
var platforms = [];
for (var i = 0; i < levelData.platforms.length; i++) {
var p = new Platform();
p.x = levelData.platforms[i].x;
p.y = levelData.platforms[i].y;
p.width = levelData.platforms[i].w;
p.children[0].width = levelData.platforms[i].w;
platforms.push(p);
game.addChild(p);
}
// --- Spikes ---
var spikes = [];
for (var i = 0; i < levelData.spikes.length; i++) {
var s = new Spike();
s.x = levelData.spikes[i].x;
s.y = levelData.spikes[i].y;
spikes.push(s);
game.addChild(s);
}
// --- Stars ---
var stars = [];
for (var i = 0; i < levelData.stars.length; i++) {
var st = new Star();
st.x = levelData.stars[i].x;
st.y = levelData.stars[i].y;
stars.push(st);
game.addChild(st);
}
// --- Exit ---
var exit = new Exit();
exit.x = levelData.exit.x;
exit.y = levelData.exit.y;
game.addChild(exit);
// --- Runner (Player) ---
var runner = new Runner();
runner.x = 200;
runner.y = 2500;
game.addChild(runner);
// --- Camera ---
var cameraY = 0;
// --- Score/Stars UI ---
var starTxt = new Text2('0', {
size: 100,
fill: 0xFFE066
});
starTxt.anchor.set(0, 0);
LK.gui.top.addChild(starTxt);
starTxt.x = 120;
starTxt.y = 10;
// --- Level UI ---
var levelTxt = new Text2('Level 1', {
size: 80,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.x = 1024;
levelTxt.y = 10;
// --- Touch Controls (Jump/Slide/Left/Right) ---
// Left Arrow Button
var leftBtn = new Text2('←', {
size: 120,
fill: 0xFFFFFF
});
leftBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(leftBtn);
leftBtn.x = 80;
leftBtn.y = -40;
// Right Arrow Button
var rightBtn = new Text2('→', {
size: 120,
fill: 0xFFFFFF
});
rightBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(rightBtn);
rightBtn.x = 320;
rightBtn.y = -40;
// Jump Button
var jumpBtn = new Text2('JUMP', {
size: 80,
fill: 0xFFFFFF
});
jumpBtn.anchor.set(0.5, 1);
LK.gui.bottomLeft.addChild(jumpBtn);
jumpBtn.x = 560;
jumpBtn.y = -40;
// Slide Button
var slideBtn = new Text2('SLIDE', {
size: 80,
fill: 0xFFFFFF
});
slideBtn.anchor.set(0.5, 1);
LK.gui.bottomRight.addChild(slideBtn);
slideBtn.x = -200;
slideBtn.y = -40;
// --- Touch Controls State for Arrows ---
var leftPressed = false;
var rightPressed = false;
// --- Arrow Button Events ---
leftBtn.down = function () {
leftPressed = true;
rightPressed = false;
runner.facing = -1;
};
leftBtn.up = function () {
leftPressed = false;
};
rightBtn.down = function () {
rightPressed = true;
leftPressed = false;
runner.facing = 1;
};
rightBtn.up = function () {
rightPressed = false;
};
// --- State ---
var collectedStars = 0;
var levelComplete = false;
// --- Helper: AABB collision ---
function aabb(a, b) {
return a.x - a.width / 2 < b.x + b.width / 2 && a.x + a.width / 2 > b.x - b.width / 2 && a.y - a.height < b.y && a.y > b.y - b.height;
}
// --- Game Update ---
game.update = function () {
// Camera follows runner
var targetY = runner.y - 1800;
cameraY += (targetY - cameraY) * 0.15;
game.y = -cameraY;
// Parallax backgrounds
bg_far.update(runner.vx * 0.5);
bg_mid.update(runner.vx * 0.7);
bg_near.update(runner.vx);
// Runner physics
runner.onGround = false;
runner.touchingWall = 0;
// --- Arrow Button Direction Control ---
if (leftPressed && !rightPressed) {
runner.facing = -1;
} else if (rightPressed && !leftPressed) {
runner.facing = 1;
}
// Platform collision
for (var i = 0; i < platforms.length; i++) {
var p = platforms[i];
// Only check if close
if (Math.abs(runner.x - p.x) < p.width / 2 + 80 && Math.abs(runner.y - p.y) < 200) {
// Vertical collision (landing)
if (runner.y + runner.vy >= p.y - 30 && runner.y < p.y && runner.x > p.x - p.width / 2 + 30 && runner.x < p.x + p.width / 2 - 30 && runner.vy >= 0) {
runner.y = p.y - 30;
runner.vy = 0;
runner.onGround = true;
runner.doubleJumped = false;
runner.wallJumped = false;
}
// Wall collision (left)
if (runner.x + runner.vx > p.x - p.width / 2 - 60 && runner.x < p.x - p.width / 2 && runner.y > p.y - 120 && runner.y < p.y + 30) {
runner.x = p.x - p.width / 2 - 60;
runner.vx = 0;
runner.touchingWall = -1;
}
// Wall collision (right)
if (runner.x + runner.vx < p.x + p.width / 2 + 60 && runner.x > p.x + p.width / 2 && runner.y > p.y - 120 && runner.y < p.y + 30) {
runner.x = p.x + p.width / 2 + 60;
runner.vx = 0;
runner.touchingWall = 1;
}
}
}
// Spikes collision
for (var i = 0; i < spikes.length; i++) {
var s = spikes[i];
if (!runner.dead && Math.abs(runner.x - s.x) < 60 && Math.abs(runner.y - s.y) < 60) {
runner.die();
}
}
// Star collection
for (var i = 0; i < stars.length; i++) {
var st = stars[i];
if (!st.collected && Math.abs(runner.x - st.x) < 60 && Math.abs(runner.y - st.y) < 80) {
st.collected = true;
tween(st, {
alpha: 0,
y: st.y - 80
}, {
duration: 400,
onFinish: function onFinish() {
st.destroy();
}
});
collectedStars++;
LK.getSound('star').play();
starTxt.setText(collectedStars);
}
}
// Exit collision
if (!levelComplete && Math.abs(runner.x - exit.x) < 80 && Math.abs(runner.y - exit.y) < 160) {
levelComplete = true;
LK.getSound('exit').play();
tween(runner, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
// Save progress
storage.level = Math.max(storage.level, 2);
storage.stars = (storage.stars || 0) + collectedStars;
LK.setScore(collectedStars);
LK.showYouWin();
}
});
}
};
// --- Touch Controls ---
jumpBtn.down = function () {
runner.jump();
};
slideBtn.down = function () {
runner.slide();
};
// --- Swipe Controls (for mobile) ---
var swipeStartY = null;
game.down = function (x, y, obj) {
swipeStartY = y;
};
game.up = function (x, y, obj) {
if (swipeStartY !== null) {
var dy = y - swipeStartY;
if (dy < -80) {
runner.jump();
} else if (dy > 80) {
runner.slide();
}
}
swipeStartY = null;
};
// --- Music ---
LK.playMusic('bgm1');
// --- Init UI ---
starTxt.setText(collectedStars);
levelTxt.setText('Level 1');