/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Block obstacle
var Block = Container.expand(function () {
var self = Container.call(this);
var blockGfx = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 1
});
self.width = blockGfx.width;
self.height = blockGfx.height;
self.type = 'block';
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Player Cube
var Cube = Container.expand(function () {
var self = Container.call(this);
var cubeGfx = self.attachAsset('cube', {
anchorX: 0.5,
anchorY: 1
});
self.width = cubeGfx.width;
self.height = cubeGfx.height;
self.velY = 0;
self.isOnGround = false;
// Jump method
self.jump = function () {
if (self.isOnGround) {
self.velY = -36; // Increased jump velocity for higher jump
self.isOnGround = false;
}
};
// Update method
self.update = function () {
// Gravity
self.velY += 2.1; // Slightly increased gravity to balance higher jump arc
if (self.velY > 60) self.velY = 60;
self.y += self.velY;
// Check for landing on top of a block
self.isOnGround = false;
for (var i = 0; i < obstacles.length; ++i) {
var obs = obstacles[i];
if (obs.type === 'block') {
// Check if cube is falling and feet are just above the block, and horizontally overlapping
var cubeBottom = self.y;
var prevCubeBottom = self.y - self.velY;
var blockTop = obs.y - obs.height;
var blockLeft = obs.x - obs.width / 2;
var blockRight = obs.x + obs.width / 2;
var cubeLeft = self.x - self.width / 2;
var cubeRight = self.x + self.width / 2;
if (self.velY >= 0 && prevCubeBottom <= blockTop && cubeBottom >= blockTop && cubeRight > blockLeft && cubeLeft < blockRight) {
self.y = blockTop;
self.velY = 0;
self.isOnGround = true;
break;
}
}
}
// Prevent falling below ground
if (self.y > groundY) {
self.y = groundY;
self.velY = 0;
self.isOnGround = true;
}
};
return self;
});
// Flying obstacle for flying section
var FlyingObstacle = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.type = 'flyingObstacle';
self.passed = false;
self.lastX = 0;
self.update = function () {
self.x -= flyingObstacleSpeed;
};
return self;
});
// Minicoin collectible
var Minicoin = Container.expand(function () {
var self = Container.call(this);
var coinGfx = self.attachAsset('minicoin', {
anchorX: 0.5,
anchorY: 1
});
self.width = coinGfx.width;
self.height = coinGfx.height;
self.type = 'minicoin';
self.collected = false;
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Ship player for flying section
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGfx = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = shipGfx.width;
self.height = shipGfx.height;
self.velY = 0;
self.targetY = 0;
self.isFlying = true;
self.lastY = 0;
self.update = function () {
// Smoothly move toward targetY (touch/move position)
var dy = self.targetY - self.y;
self.velY = dy * 0.18;
self.y += self.velY;
// Clamp to screen
if (self.y < 100 + self.height / 2) self.y = 100 + self.height / 2;
if (self.y > groundY - 200) self.y = groundY - 200;
};
return self;
});
// ShopIcon class for shop UI
var ShopIcon = Container.expand(function () {
var self = Container.call(this);
self.iconId = null;
self.price = 0;
self.purchased = false;
self.selected = false;
var iconGfx = null;
var priceTxt = null;
var selectBorder = null;
// Initialize the icon display
self.initIcon = function (iconId, price, purchased, selected) {
self.iconId = iconId;
self.price = price;
self.purchased = purchased;
self.selected = selected;
if (iconGfx) {
self.removeChild(iconGfx);
}
iconGfx = self.attachAsset(iconId, {
anchorX: 0.5,
anchorY: 0.5
});
iconGfx.x = 0;
iconGfx.y = 0;
if (priceTxt) {
self.removeChild(priceTxt);
}
priceTxt = new Text2(purchased ? selected ? "Selected" : "Owned" : price + " pts", {
size: 48,
fill: purchased ? selected ? 0x44bb44 : 0x888888 : 0x222222
});
priceTxt.anchor.set(0.5, 0);
priceTxt.x = 0;
priceTxt.y = 70;
self.addChild(priceTxt);
if (selectBorder) {
self.removeChild(selectBorder);
}
selectBorder = null;
if (selected) {
selectBorder = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
selectBorder.width = 140;
selectBorder.height = 140;
selectBorder.tint = 0x44bb44;
selectBorder.alpha = 0.3;
selectBorder.x = 0;
selectBorder.y = 0;
self.addChild(selectBorder);
self.setChildIndex(selectBorder, 0);
}
};
// Handle icon tap: buy or select
self.down = function (x, y, obj) {
if (!self.purchased && score >= self.price) {
score -= self.price;
scoreTxt.setText(score);
self.purchased = true;
// Save purchase
storage['shop_' + self.iconId] = true;
}
if (self.purchased) {
// Deselect all others
for (var i = 0; i < shopIcons.length; ++i) {
shopIcons[i].selected = false;
shopIcons[i].initIcon(shopIcons[i].iconId, shopIcons[i].price, !!storage['shop_' + shopIcons[i].iconId] || shopIcons[i].price === 0, false);
}
self.selected = true;
self.initIcon(self.iconId, self.price, self.purchased, true);
// Save selection
storage['selected_icon'] = self.iconId;
// Change player icon
setPlayerIcon(self.iconId);
}
};
return self;
});
// Spike obstacle
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGfx = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1
});
self.width = spikeGfx.width;
self.height = spikeGfx.height;
self.type = 'spike';
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf0f0f0
});
/****
* Game Code
****/
// Removed old cube asset and re-added it
// Constants
// Cube player
// Ground
// Spike obstacle
// Block obstacle
// New ship asset
// Deleted and re-added player asset
var groundHeight = 80;
var groundY = 2732 - groundHeight;
var startX = 400;
var startY = groundY;
var obstacleSpeed = 12;
var minObstacleGap = 420;
var maxObstacleGap = 700;
var lastObstacleX = 0;
var score = 0;
var gameStarted = false;
// Arrays
var obstacles = [];
// --- Flying section variables ---
var flyingSectionStarted = false;
var flyingSectionTriggered = false;
var flyingObstacles = [];
var ship = null;
var flyingObstacleSpeed = 16;
var flyingSectionStartX = 1800; // X position to trigger flying section
var flyingSectionEndX = 400; // X position to end flying section (ship lands)
var flyingSectionDuration = 1200; // px to fly
var flyingSectionScore = 0;
var flyingSectionMaxScore = 10;
var flyingSectionProgress = 0;
var flyingSectionActive = false;
var flyingSectionShipStartY = 1000;
var flyingSectionShipStartX = 400;
var flyingSectionShipEndX = 1800;
var flyingSectionShipTargetX = 1800;
var flyingSectionShipLanded = false;
var flyingSectionObstacleGap = 320;
var flyingSectionLastObstacleX = 0;
var flyingSectionPassedObstacles = 0;
// Add background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
background.x = 0;
background.y = 0;
game.addChild(background);
// Add ground
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
});
ground.x = 0;
ground.y = groundY;
game.addChild(ground);
// Add player cube (deleted and re-added)
var cube = new Cube();
cube.x = startX;
cube.y = startY;
game.addChild(cube);
// Shop UI variables
var shopIcons = [];
var shopBg = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
shopBg.width = 900;
shopBg.height = 400;
shopBg.x = 2048 / 2;
shopBg.y = 2732 / 2;
shopBg.tint = 0xffffff;
shopBg.alpha = 0.97;
shopBg.visible = false;
var shopTitle = new Text2('Shop', {
size: 100,
fill: 0x222222
});
shopTitle.anchor.set(0.5, 0);
shopTitle.x = 2048 / 2;
shopTitle.y = 2732 / 2 - 180;
shopTitle.visible = false;
var closeShopBtn = new Text2('Close', {
size: 70,
fill: 0xcc2222
});
closeShopBtn.anchor.set(0.5, 0.5);
closeShopBtn.x = 2048 / 2 + 350;
closeShopBtn.y = 2732 / 2 - 170;
closeShopBtn.visible = false;
closeShopBtn.down = function (x, y, obj) {
showShop(false);
};
LK.gui.center.addChild(shopBg);
LK.gui.center.addChild(shopTitle);
LK.gui.center.addChild(closeShopBtn);
// Shop icon data
var iconShopData = [{
iconId: 'icon1',
price: 0
},
// Default, free
{
iconId: 'icon2',
price: 50
}, {
iconId: 'icon3',
price: 100
}];
// Helper: set player icon
function setPlayerIcon(iconId) {
if (cube && cube.children && cube.children.length > 0) {
// Remove old icon
for (var i = cube.children.length - 1; i >= 0; --i) {
var ch = cube.children[i];
if (ch.assetId && (ch.assetId === 'cube' || ch.assetId === 'icon1' || ch.assetId === 'icon2' || ch.assetId === 'icon3')) {
cube.removeChild(ch);
}
}
// Add new icon
var newIcon = cube.attachAsset(iconId, {
anchorX: 0.5,
anchorY: 1
});
newIcon.x = 0;
newIcon.y = 0;
cube.width = newIcon.width;
cube.height = newIcon.height;
}
}
// Helper: show/hide shop
function showShop(show) {
shopBg.visible = show;
shopTitle.visible = show;
closeShopBtn.visible = show;
for (var i = 0; i < shopIcons.length; ++i) {
shopIcons[i].visible = show;
}
if (show) {
gameStarted = false;
}
}
// Create shop icons
for (var i = 0; i < iconShopData.length; ++i) {
var iconId = iconShopData[i].iconId;
var price = iconShopData[i].price;
var purchased = !!storage['shop_' + iconId] || price === 0;
var selected = false;
var selectedIconId = storage['selected_icon'];
if (!selectedIconId && price === 0) selected = true;
if (selectedIconId && selectedIconId === iconId) selected = true;
var shopIcon = new ShopIcon();
shopIcon.x = 2048 / 2 - 250 + i * 250;
shopIcon.y = 2732 / 2 + 40;
shopIcon.initIcon(iconId, price, purchased, selected);
shopIcon.visible = false;
LK.gui.center.addChild(shopIcon);
shopIcons.push(shopIcon);
if (selected) {
setPlayerIcon(iconId);
}
}
// Shop button
var shopBtn = new Text2('Shop', {
size: 80,
fill: 0x2266cc
});
shopBtn.anchor.set(0.5, 0.5);
shopBtn.x = 2048 / 2;
shopBtn.y = 2732 / 2;
shopBtn.down = function (x, y, obj) {
showShop(true);
// Optionally, you could pause the game here if needed, but shop UI already disables gameplay.
};
LK.gui.center.addChild(shopBtn);
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0x222222
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Progress bar variables
var progressBarBg = LK.getAsset('block', {
anchorX: 0,
anchorY: 0
});
progressBarBg.width = 800;
progressBarBg.height = 40;
progressBarBg.x = 624; // center horizontally (2048-800)/2
progressBarBg.y = 160;
progressBarBg.tint = 0xcccccc;
var progressBarFill = LK.getAsset('block', {
anchorX: 0,
anchorY: 0
});
progressBarFill.width = 0;
progressBarFill.height = 40;
progressBarFill.x = 624;
progressBarFill.y = 160;
progressBarFill.tint = 0x44bb44;
LK.gui.top.addChild(progressBarBg);
LK.gui.top.addChild(progressBarFill);
var progressPercent = 0;
var progressMax = 100; // 100%
var progressPerScore = 2; // Each point increases progress by 2%
var winShown = false;
// Win text and replay button
var winText = new Text2('You win!', {
size: 180,
fill: 0x228822
});
winText.anchor.set(0.5, 0.5);
winText.x = 2048 / 2;
winText.y = 2732 / 2 - 120;
winText.visible = false;
var replayBtn = new Text2('Replay', {
size: 120,
fill: 0x2266cc
});
replayBtn.anchor.set(0.5, 0.5);
replayBtn.x = 2048 / 2;
replayBtn.y = 2732 / 2 + 80;
replayBtn.visible = false;
LK.gui.center.addChild(winText);
LK.gui.center.addChild(replayBtn);
// Replay handler
replayBtn.down = function (x, y, obj) {
if (winShown) {
winText.visible = false;
replayBtn.visible = false;
winShown = false;
resetGame();
gameStarted = true;
LK.playMusic('Mad');
}
};
// Helper: spawn obstacle
function spawnObstacle() {
// Randomly choose spike or block structure
var type = Math.random() < 0.6 ? 'spike' : 'block';
if (type === 'spike') {
var obs = new Spike();
obs.x = 2048 + 100;
obs.y = groundY;
obstacles.push(obs);
game.addChild(obs);
lastObstacleX = obs.x;
} else {
// Structure: always 1 block high for passable platforms
var platformHeight = 1;
var platformLength = Math.floor(Math.random() * 3) + 2; // 2 to 4 blocks long
var baseX = 2048 + 100;
var blockW = 120;
var blockH = 120;
var minicoinsOnTop = Math.random() < 0.8; // 80% chance to spawn coins
for (var i = 0; i < platformLength; ++i) {
// Only one block high
var block = new Block();
block.x = baseX + i * blockW;
block.y = groundY - 0 * blockH;
obstacles.push(block);
game.addChild(block);
lastObstacleX = block.x;
// Place minicoins on top of the structure
if (minicoinsOnTop) {
var coin = new Minicoin();
coin.x = baseX + i * blockW;
coin.y = groundY - blockH - 10; // 10px above top block
obstacles.push(coin);
game.addChild(coin);
}
}
// Place a single spike on a random block in the structure
if (platformLength > 1) {
var spikeIndex = Math.floor(Math.random() * platformLength);
var spike = new Spike();
spike.x = baseX + spikeIndex * blockW;
spike.y = groundY;
obstacles.push(spike);
game.addChild(spike);
}
}
}
// Helper: reset game state
function resetGame() {
// Remove obstacles
for (var i = 0; i < obstacles.length; ++i) {
obstacles[i].destroy();
}
obstacles.length = 0;
// Remove flying obstacles
for (var i = 0; i < flyingObstacles.length; ++i) {
flyingObstacles[i].destroy();
}
flyingObstacles.length = 0;
// Hide ship
if (ship) {
ship.visible = false;
}
cube.x = startX;
cube.y = startY;
cube.velY = 0;
cube.isOnGround = true;
lastObstacleX = 1200;
score = 0;
scoreTxt.setText(score);
progressPercent = 0;
progressBarFill.width = 0;
winText.visible = false;
replayBtn.visible = false;
winShown = false;
gameStarted = false;
// Reset flying section state
flyingSectionStarted = false;
flyingSectionTriggered = false;
flyingSectionActive = false;
flyingSectionShipLanded = false;
// Hide shop UI on reset
showShop(false);
// Update shop icons (in case of new purchases)
for (var i = 0; i < shopIcons.length; ++i) {
var iconId = shopIcons[i].iconId;
var purchased = !!storage['shop_' + iconId] || iconShopData[i].price === 0;
var selectedIconId = storage['selected_icon'];
var selected = selectedIconId && selectedIconId === iconId || !selectedIconId && iconShopData[i].price === 0;
shopIcons[i].initIcon(iconId, iconShopData[i].price, purchased, selected);
}
}
// Start game on first tap
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
return;
}
// If in flying section, do nothing on tap
if (flyingSectionActive) return;
cube.jump();
};
// Ship control: move ship to touch/move Y in flying section
game.move = function (x, y, obj) {
if (flyingSectionActive && ship && ship.visible) {
// Convert to local game coordinates if needed
ship.targetY = y;
}
};
// Main update loop
game.update = function () {
if (!gameStarted || winShown) return;
// --- FLYING SECTION TRIGGER ---
if (!flyingSectionTriggered && score >= 15) {
// Start flying section after 15 points
flyingSectionTriggered = true;
flyingSectionStarted = true;
flyingSectionActive = true;
// Hide cube, show ship
cube.visible = false;
if (!ship) {
ship = new Ship();
ship.x = flyingSectionShipStartX;
ship.y = flyingSectionShipStartY;
game.addChild(ship);
}
ship.visible = true;
ship.x = flyingSectionShipStartX;
ship.y = flyingSectionShipStartY;
ship.targetY = flyingSectionShipStartY;
// Remove all ground obstacles
for (var i = obstacles.length - 1; i >= 0; --i) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Reset flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
flyingObstacles[i].destroy();
flyingObstacles.splice(i, 1);
}
flyingSectionLastObstacleX = 2048 + 200;
flyingSectionPassedObstacles = 0;
flyingSectionProgress = 0;
flyingSectionScore = 0;
flyingSectionShipLanded = false;
}
// --- FLYING SECTION ACTIVE ---
if (flyingSectionActive) {
// Update ship
if (ship) {
ship.update();
}
// Move ship forward
if (ship && !flyingSectionShipLanded) {
ship.x += 10;
if (ship.x > flyingSectionShipTargetX) ship.x = flyingSectionShipTargetX;
}
// Spawn flying obstacles
if (flyingSectionLastObstacleX - ship.x < 1200) {
var gap = flyingSectionObstacleGap + Math.random() * 180;
var obs = new FlyingObstacle();
obs.x = flyingSectionLastObstacleX + gap;
obs.y = 400 + Math.random() * (groundY - 600);
flyingObstacles.push(obs);
game.addChild(obs);
flyingSectionLastObstacleX = obs.x;
}
// Update flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
var obs = flyingObstacles[i];
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
flyingObstacles.splice(i, 1);
continue;
}
// Collision with ship
if (ship && ship.intersects(obs)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Score: passed obstacle
if (!obs.passed && obs.x + obs.width / 2 < ship.x - ship.width / 2) {
obs.passed = true;
flyingSectionScore += 1;
score += 2;
scoreTxt.setText(score);
// Progress bar
progressPercent = Math.min(progressPercent + progressPerScore * 2, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
}
}
}
// End flying section after enough distance or obstacles
if (ship && ship.x >= flyingSectionShipTargetX && !flyingSectionShipLanded) {
flyingSectionShipLanded = true;
flyingSectionActive = false;
// Hide ship, show cube, reset cube position
ship.visible = false;
cube.visible = true;
cube.x = ship.x;
cube.y = groundY;
cube.velY = 0;
cube.isOnGround = true;
// Remove all flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
flyingObstacles[i].destroy();
flyingObstacles.splice(i, 1);
}
// Continue normal section, spawn obstacles ahead
lastObstacleX = cube.x + 600;
}
return;
}
// Update player
cube.update();
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; --i) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
if (obs.type === 'minicoin') {
if (!obs.collected && cube.intersects(obs)) {
obs.collected = true;
score += 5; // Minicoin gives 5 points
scoreTxt.setText(score);
// Play coin sound
LK.getSound('Coin').play();
// Update progress
progressPercent = Math.min(progressPercent + progressPerScore * 5, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
// LK.pauseGame(); //{30} // Removed: not needed, LK handles game state on win
}
obs.destroy();
obstacles.splice(i, 1);
continue;
}
} else {
if (cube.intersects(obs)) {
// Flash screen and game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Score: passed obstacle
if (!obs.passed && obs.x + obs.width / 2 < cube.x - cube.width / 2) {
obs.passed = true;
score += 1;
scoreTxt.setText(score);
// Update progress
progressPercent = Math.min(progressPercent + progressPerScore, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
// LK.pauseGame(); //{3h} // Removed: not needed, LK handles game state on win
}
}
}
}
// Spawn new obstacles
if (obstacles.length === 0 || 2048 - lastObstacleX > minObstacleGap + Math.random() * (maxObstacleGap - minObstacleGap)) {
spawnObstacle();
}
};
// Reset game on game over
LK.on('gameover', function () {
resetGame();
LK.playMusic('Mad');
});
// Initial state
resetGame();
// Play music asset when the game starts
LK.playMusic('Mad');
// Shop icon assets (add more as needed) /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Block obstacle
var Block = Container.expand(function () {
var self = Container.call(this);
var blockGfx = self.attachAsset('block', {
anchorX: 0.5,
anchorY: 1
});
self.width = blockGfx.width;
self.height = blockGfx.height;
self.type = 'block';
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Player Cube
var Cube = Container.expand(function () {
var self = Container.call(this);
var cubeGfx = self.attachAsset('cube', {
anchorX: 0.5,
anchorY: 1
});
self.width = cubeGfx.width;
self.height = cubeGfx.height;
self.velY = 0;
self.isOnGround = false;
// Jump method
self.jump = function () {
if (self.isOnGround) {
self.velY = -36; // Increased jump velocity for higher jump
self.isOnGround = false;
}
};
// Update method
self.update = function () {
// Gravity
self.velY += 2.1; // Slightly increased gravity to balance higher jump arc
if (self.velY > 60) self.velY = 60;
self.y += self.velY;
// Check for landing on top of a block
self.isOnGround = false;
for (var i = 0; i < obstacles.length; ++i) {
var obs = obstacles[i];
if (obs.type === 'block') {
// Check if cube is falling and feet are just above the block, and horizontally overlapping
var cubeBottom = self.y;
var prevCubeBottom = self.y - self.velY;
var blockTop = obs.y - obs.height;
var blockLeft = obs.x - obs.width / 2;
var blockRight = obs.x + obs.width / 2;
var cubeLeft = self.x - self.width / 2;
var cubeRight = self.x + self.width / 2;
if (self.velY >= 0 && prevCubeBottom <= blockTop && cubeBottom >= blockTop && cubeRight > blockLeft && cubeLeft < blockRight) {
self.y = blockTop;
self.velY = 0;
self.isOnGround = true;
break;
}
}
}
// Prevent falling below ground
if (self.y > groundY) {
self.y = groundY;
self.velY = 0;
self.isOnGround = true;
}
};
return self;
});
// Flying obstacle for flying section
var FlyingObstacle = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.type = 'flyingObstacle';
self.passed = false;
self.lastX = 0;
self.update = function () {
self.x -= flyingObstacleSpeed;
};
return self;
});
// Minicoin collectible
var Minicoin = Container.expand(function () {
var self = Container.call(this);
var coinGfx = self.attachAsset('minicoin', {
anchorX: 0.5,
anchorY: 1
});
self.width = coinGfx.width;
self.height = coinGfx.height;
self.type = 'minicoin';
self.collected = false;
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
// Ship player for flying section
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGfx = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = shipGfx.width;
self.height = shipGfx.height;
self.velY = 0;
self.targetY = 0;
self.isFlying = true;
self.lastY = 0;
self.update = function () {
// Smoothly move toward targetY (touch/move position)
var dy = self.targetY - self.y;
self.velY = dy * 0.18;
self.y += self.velY;
// Clamp to screen
if (self.y < 100 + self.height / 2) self.y = 100 + self.height / 2;
if (self.y > groundY - 200) self.y = groundY - 200;
};
return self;
});
// ShopIcon class for shop UI
var ShopIcon = Container.expand(function () {
var self = Container.call(this);
self.iconId = null;
self.price = 0;
self.purchased = false;
self.selected = false;
var iconGfx = null;
var priceTxt = null;
var selectBorder = null;
// Initialize the icon display
self.initIcon = function (iconId, price, purchased, selected) {
self.iconId = iconId;
self.price = price;
self.purchased = purchased;
self.selected = selected;
if (iconGfx) {
self.removeChild(iconGfx);
}
iconGfx = self.attachAsset(iconId, {
anchorX: 0.5,
anchorY: 0.5
});
iconGfx.x = 0;
iconGfx.y = 0;
if (priceTxt) {
self.removeChild(priceTxt);
}
priceTxt = new Text2(purchased ? selected ? "Selected" : "Owned" : price + " pts", {
size: 48,
fill: purchased ? selected ? 0x44bb44 : 0x888888 : 0x222222
});
priceTxt.anchor.set(0.5, 0);
priceTxt.x = 0;
priceTxt.y = 70;
self.addChild(priceTxt);
if (selectBorder) {
self.removeChild(selectBorder);
}
selectBorder = null;
if (selected) {
selectBorder = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
selectBorder.width = 140;
selectBorder.height = 140;
selectBorder.tint = 0x44bb44;
selectBorder.alpha = 0.3;
selectBorder.x = 0;
selectBorder.y = 0;
self.addChild(selectBorder);
self.setChildIndex(selectBorder, 0);
}
};
// Handle icon tap: buy or select
self.down = function (x, y, obj) {
if (!self.purchased && score >= self.price) {
score -= self.price;
scoreTxt.setText(score);
self.purchased = true;
// Save purchase
storage['shop_' + self.iconId] = true;
}
if (self.purchased) {
// Deselect all others
for (var i = 0; i < shopIcons.length; ++i) {
shopIcons[i].selected = false;
shopIcons[i].initIcon(shopIcons[i].iconId, shopIcons[i].price, !!storage['shop_' + shopIcons[i].iconId] || shopIcons[i].price === 0, false);
}
self.selected = true;
self.initIcon(self.iconId, self.price, self.purchased, true);
// Save selection
storage['selected_icon'] = self.iconId;
// Change player icon
setPlayerIcon(self.iconId);
}
};
return self;
});
// Spike obstacle
var Spike = Container.expand(function () {
var self = Container.call(this);
var spikeGfx = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1
});
self.width = spikeGfx.width;
self.height = spikeGfx.height;
self.type = 'spike';
self.update = function () {
self.x -= obstacleSpeed;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xf0f0f0
});
/****
* Game Code
****/
// Removed old cube asset and re-added it
// Constants
// Cube player
// Ground
// Spike obstacle
// Block obstacle
// New ship asset
// Deleted and re-added player asset
var groundHeight = 80;
var groundY = 2732 - groundHeight;
var startX = 400;
var startY = groundY;
var obstacleSpeed = 12;
var minObstacleGap = 420;
var maxObstacleGap = 700;
var lastObstacleX = 0;
var score = 0;
var gameStarted = false;
// Arrays
var obstacles = [];
// --- Flying section variables ---
var flyingSectionStarted = false;
var flyingSectionTriggered = false;
var flyingObstacles = [];
var ship = null;
var flyingObstacleSpeed = 16;
var flyingSectionStartX = 1800; // X position to trigger flying section
var flyingSectionEndX = 400; // X position to end flying section (ship lands)
var flyingSectionDuration = 1200; // px to fly
var flyingSectionScore = 0;
var flyingSectionMaxScore = 10;
var flyingSectionProgress = 0;
var flyingSectionActive = false;
var flyingSectionShipStartY = 1000;
var flyingSectionShipStartX = 400;
var flyingSectionShipEndX = 1800;
var flyingSectionShipTargetX = 1800;
var flyingSectionShipLanded = false;
var flyingSectionObstacleGap = 320;
var flyingSectionLastObstacleX = 0;
var flyingSectionPassedObstacles = 0;
// Add background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
background.x = 0;
background.y = 0;
game.addChild(background);
// Add ground
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
});
ground.x = 0;
ground.y = groundY;
game.addChild(ground);
// Add player cube (deleted and re-added)
var cube = new Cube();
cube.x = startX;
cube.y = startY;
game.addChild(cube);
// Shop UI variables
var shopIcons = [];
var shopBg = LK.getAsset('block', {
anchorX: 0.5,
anchorY: 0.5
});
shopBg.width = 900;
shopBg.height = 400;
shopBg.x = 2048 / 2;
shopBg.y = 2732 / 2;
shopBg.tint = 0xffffff;
shopBg.alpha = 0.97;
shopBg.visible = false;
var shopTitle = new Text2('Shop', {
size: 100,
fill: 0x222222
});
shopTitle.anchor.set(0.5, 0);
shopTitle.x = 2048 / 2;
shopTitle.y = 2732 / 2 - 180;
shopTitle.visible = false;
var closeShopBtn = new Text2('Close', {
size: 70,
fill: 0xcc2222
});
closeShopBtn.anchor.set(0.5, 0.5);
closeShopBtn.x = 2048 / 2 + 350;
closeShopBtn.y = 2732 / 2 - 170;
closeShopBtn.visible = false;
closeShopBtn.down = function (x, y, obj) {
showShop(false);
};
LK.gui.center.addChild(shopBg);
LK.gui.center.addChild(shopTitle);
LK.gui.center.addChild(closeShopBtn);
// Shop icon data
var iconShopData = [{
iconId: 'icon1',
price: 0
},
// Default, free
{
iconId: 'icon2',
price: 50
}, {
iconId: 'icon3',
price: 100
}];
// Helper: set player icon
function setPlayerIcon(iconId) {
if (cube && cube.children && cube.children.length > 0) {
// Remove old icon
for (var i = cube.children.length - 1; i >= 0; --i) {
var ch = cube.children[i];
if (ch.assetId && (ch.assetId === 'cube' || ch.assetId === 'icon1' || ch.assetId === 'icon2' || ch.assetId === 'icon3')) {
cube.removeChild(ch);
}
}
// Add new icon
var newIcon = cube.attachAsset(iconId, {
anchorX: 0.5,
anchorY: 1
});
newIcon.x = 0;
newIcon.y = 0;
cube.width = newIcon.width;
cube.height = newIcon.height;
}
}
// Helper: show/hide shop
function showShop(show) {
shopBg.visible = show;
shopTitle.visible = show;
closeShopBtn.visible = show;
for (var i = 0; i < shopIcons.length; ++i) {
shopIcons[i].visible = show;
}
if (show) {
gameStarted = false;
}
}
// Create shop icons
for (var i = 0; i < iconShopData.length; ++i) {
var iconId = iconShopData[i].iconId;
var price = iconShopData[i].price;
var purchased = !!storage['shop_' + iconId] || price === 0;
var selected = false;
var selectedIconId = storage['selected_icon'];
if (!selectedIconId && price === 0) selected = true;
if (selectedIconId && selectedIconId === iconId) selected = true;
var shopIcon = new ShopIcon();
shopIcon.x = 2048 / 2 - 250 + i * 250;
shopIcon.y = 2732 / 2 + 40;
shopIcon.initIcon(iconId, price, purchased, selected);
shopIcon.visible = false;
LK.gui.center.addChild(shopIcon);
shopIcons.push(shopIcon);
if (selected) {
setPlayerIcon(iconId);
}
}
// Shop button
var shopBtn = new Text2('Shop', {
size: 80,
fill: 0x2266cc
});
shopBtn.anchor.set(0.5, 0.5);
shopBtn.x = 2048 / 2;
shopBtn.y = 2732 / 2;
shopBtn.down = function (x, y, obj) {
showShop(true);
// Optionally, you could pause the game here if needed, but shop UI already disables gameplay.
};
LK.gui.center.addChild(shopBtn);
// Score text
var scoreTxt = new Text2('0', {
size: 120,
fill: 0x222222
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Progress bar variables
var progressBarBg = LK.getAsset('block', {
anchorX: 0,
anchorY: 0
});
progressBarBg.width = 800;
progressBarBg.height = 40;
progressBarBg.x = 624; // center horizontally (2048-800)/2
progressBarBg.y = 160;
progressBarBg.tint = 0xcccccc;
var progressBarFill = LK.getAsset('block', {
anchorX: 0,
anchorY: 0
});
progressBarFill.width = 0;
progressBarFill.height = 40;
progressBarFill.x = 624;
progressBarFill.y = 160;
progressBarFill.tint = 0x44bb44;
LK.gui.top.addChild(progressBarBg);
LK.gui.top.addChild(progressBarFill);
var progressPercent = 0;
var progressMax = 100; // 100%
var progressPerScore = 2; // Each point increases progress by 2%
var winShown = false;
// Win text and replay button
var winText = new Text2('You win!', {
size: 180,
fill: 0x228822
});
winText.anchor.set(0.5, 0.5);
winText.x = 2048 / 2;
winText.y = 2732 / 2 - 120;
winText.visible = false;
var replayBtn = new Text2('Replay', {
size: 120,
fill: 0x2266cc
});
replayBtn.anchor.set(0.5, 0.5);
replayBtn.x = 2048 / 2;
replayBtn.y = 2732 / 2 + 80;
replayBtn.visible = false;
LK.gui.center.addChild(winText);
LK.gui.center.addChild(replayBtn);
// Replay handler
replayBtn.down = function (x, y, obj) {
if (winShown) {
winText.visible = false;
replayBtn.visible = false;
winShown = false;
resetGame();
gameStarted = true;
LK.playMusic('Mad');
}
};
// Helper: spawn obstacle
function spawnObstacle() {
// Randomly choose spike or block structure
var type = Math.random() < 0.6 ? 'spike' : 'block';
if (type === 'spike') {
var obs = new Spike();
obs.x = 2048 + 100;
obs.y = groundY;
obstacles.push(obs);
game.addChild(obs);
lastObstacleX = obs.x;
} else {
// Structure: always 1 block high for passable platforms
var platformHeight = 1;
var platformLength = Math.floor(Math.random() * 3) + 2; // 2 to 4 blocks long
var baseX = 2048 + 100;
var blockW = 120;
var blockH = 120;
var minicoinsOnTop = Math.random() < 0.8; // 80% chance to spawn coins
for (var i = 0; i < platformLength; ++i) {
// Only one block high
var block = new Block();
block.x = baseX + i * blockW;
block.y = groundY - 0 * blockH;
obstacles.push(block);
game.addChild(block);
lastObstacleX = block.x;
// Place minicoins on top of the structure
if (minicoinsOnTop) {
var coin = new Minicoin();
coin.x = baseX + i * blockW;
coin.y = groundY - blockH - 10; // 10px above top block
obstacles.push(coin);
game.addChild(coin);
}
}
// Place a single spike on a random block in the structure
if (platformLength > 1) {
var spikeIndex = Math.floor(Math.random() * platformLength);
var spike = new Spike();
spike.x = baseX + spikeIndex * blockW;
spike.y = groundY;
obstacles.push(spike);
game.addChild(spike);
}
}
}
// Helper: reset game state
function resetGame() {
// Remove obstacles
for (var i = 0; i < obstacles.length; ++i) {
obstacles[i].destroy();
}
obstacles.length = 0;
// Remove flying obstacles
for (var i = 0; i < flyingObstacles.length; ++i) {
flyingObstacles[i].destroy();
}
flyingObstacles.length = 0;
// Hide ship
if (ship) {
ship.visible = false;
}
cube.x = startX;
cube.y = startY;
cube.velY = 0;
cube.isOnGround = true;
lastObstacleX = 1200;
score = 0;
scoreTxt.setText(score);
progressPercent = 0;
progressBarFill.width = 0;
winText.visible = false;
replayBtn.visible = false;
winShown = false;
gameStarted = false;
// Reset flying section state
flyingSectionStarted = false;
flyingSectionTriggered = false;
flyingSectionActive = false;
flyingSectionShipLanded = false;
// Hide shop UI on reset
showShop(false);
// Update shop icons (in case of new purchases)
for (var i = 0; i < shopIcons.length; ++i) {
var iconId = shopIcons[i].iconId;
var purchased = !!storage['shop_' + iconId] || iconShopData[i].price === 0;
var selectedIconId = storage['selected_icon'];
var selected = selectedIconId && selectedIconId === iconId || !selectedIconId && iconShopData[i].price === 0;
shopIcons[i].initIcon(iconId, iconShopData[i].price, purchased, selected);
}
}
// Start game on first tap
game.down = function (x, y, obj) {
if (!gameStarted) {
gameStarted = true;
return;
}
// If in flying section, do nothing on tap
if (flyingSectionActive) return;
cube.jump();
};
// Ship control: move ship to touch/move Y in flying section
game.move = function (x, y, obj) {
if (flyingSectionActive && ship && ship.visible) {
// Convert to local game coordinates if needed
ship.targetY = y;
}
};
// Main update loop
game.update = function () {
if (!gameStarted || winShown) return;
// --- FLYING SECTION TRIGGER ---
if (!flyingSectionTriggered && score >= 15) {
// Start flying section after 15 points
flyingSectionTriggered = true;
flyingSectionStarted = true;
flyingSectionActive = true;
// Hide cube, show ship
cube.visible = false;
if (!ship) {
ship = new Ship();
ship.x = flyingSectionShipStartX;
ship.y = flyingSectionShipStartY;
game.addChild(ship);
}
ship.visible = true;
ship.x = flyingSectionShipStartX;
ship.y = flyingSectionShipStartY;
ship.targetY = flyingSectionShipStartY;
// Remove all ground obstacles
for (var i = obstacles.length - 1; i >= 0; --i) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
// Reset flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
flyingObstacles[i].destroy();
flyingObstacles.splice(i, 1);
}
flyingSectionLastObstacleX = 2048 + 200;
flyingSectionPassedObstacles = 0;
flyingSectionProgress = 0;
flyingSectionScore = 0;
flyingSectionShipLanded = false;
}
// --- FLYING SECTION ACTIVE ---
if (flyingSectionActive) {
// Update ship
if (ship) {
ship.update();
}
// Move ship forward
if (ship && !flyingSectionShipLanded) {
ship.x += 10;
if (ship.x > flyingSectionShipTargetX) ship.x = flyingSectionShipTargetX;
}
// Spawn flying obstacles
if (flyingSectionLastObstacleX - ship.x < 1200) {
var gap = flyingSectionObstacleGap + Math.random() * 180;
var obs = new FlyingObstacle();
obs.x = flyingSectionLastObstacleX + gap;
obs.y = 400 + Math.random() * (groundY - 600);
flyingObstacles.push(obs);
game.addChild(obs);
flyingSectionLastObstacleX = obs.x;
}
// Update flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
var obs = flyingObstacles[i];
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
flyingObstacles.splice(i, 1);
continue;
}
// Collision with ship
if (ship && ship.intersects(obs)) {
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Score: passed obstacle
if (!obs.passed && obs.x + obs.width / 2 < ship.x - ship.width / 2) {
obs.passed = true;
flyingSectionScore += 1;
score += 2;
scoreTxt.setText(score);
// Progress bar
progressPercent = Math.min(progressPercent + progressPerScore * 2, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
}
}
}
// End flying section after enough distance or obstacles
if (ship && ship.x >= flyingSectionShipTargetX && !flyingSectionShipLanded) {
flyingSectionShipLanded = true;
flyingSectionActive = false;
// Hide ship, show cube, reset cube position
ship.visible = false;
cube.visible = true;
cube.x = ship.x;
cube.y = groundY;
cube.velY = 0;
cube.isOnGround = true;
// Remove all flying obstacles
for (var i = flyingObstacles.length - 1; i >= 0; --i) {
flyingObstacles[i].destroy();
flyingObstacles.splice(i, 1);
}
// Continue normal section, spawn obstacles ahead
lastObstacleX = cube.x + 600;
}
return;
}
// Update player
cube.update();
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; --i) {
var obs = obstacles[i];
obs.update();
// Remove if off screen
if (obs.x < -200) {
obs.destroy();
obstacles.splice(i, 1);
continue;
}
// Collision detection
if (obs.type === 'minicoin') {
if (!obs.collected && cube.intersects(obs)) {
obs.collected = true;
score += 5; // Minicoin gives 5 points
scoreTxt.setText(score);
// Play coin sound
LK.getSound('Coin').play();
// Update progress
progressPercent = Math.min(progressPercent + progressPerScore * 5, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
// LK.pauseGame(); //{30} // Removed: not needed, LK handles game state on win
}
obs.destroy();
obstacles.splice(i, 1);
continue;
}
} else {
if (cube.intersects(obs)) {
// Flash screen and game over
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
return;
}
// Score: passed obstacle
if (!obs.passed && obs.x + obs.width / 2 < cube.x - cube.width / 2) {
obs.passed = true;
score += 1;
scoreTxt.setText(score);
// Update progress
progressPercent = Math.min(progressPercent + progressPerScore, progressMax);
progressBarFill.width = progressPercent / progressMax * 800;
if (progressPercent >= progressMax && !winShown) {
winShown = true;
gameStarted = false;
winText.visible = true;
replayBtn.visible = true;
// LK.pauseGame(); //{3h} // Removed: not needed, LK handles game state on win
}
}
}
}
// Spawn new obstacles
if (obstacles.length === 0 || 2048 - lastObstacleX > minObstacleGap + Math.random() * (maxObstacleGap - minObstacleGap)) {
spawnObstacle();
}
};
// Reset game on game over
LK.on('gameover', function () {
resetGame();
LK.playMusic('Mad');
});
// Initial state
resetGame();
// Play music asset when the game starts
LK.playMusic('Mad');
// Shop icon assets (add more as needed)