/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
best: 0
});
/****
* Classes
****/
// Fuel class
var Fuel = Container.expand(function () {
var self = Container.call(this);
var fuelGfx = self.attachAsset('fuel', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = fuelGfx.width;
self.height = fuelGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle1 class (obstacle1 asset, for 10000-19999m)
var Obstacle1 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle1', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle2 class (different look/behavior for >20000m)
var Obstacle2 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle2', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle3 class (for >20000m, new look/behavior)
var Obstacle3 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle3', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle4 class (for >40000m, new look/behavior)
var Obstacle4 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle4', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Rocket (player) class
var Rocket = Container.expand(function () {
var self = Container.call(this);
// Attach rocket asset, centered
var rocketGfx = self.attachAsset('rocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = rocketGfx.width;
self.height = rocketGfx.height;
// Rocket state
self.fuel = 100;
self.alive = true;
// No per-frame update needed (movement handled in game.update)
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- Biome/level state ---
var currentBiome = 1;
var biomeTransitioning = false;
var biomeTransitionDistance = 10000;
// --- Biome scaling ---
// At biome 1: scale = 1.0, biome 2: 0.8, biome 3: 0.65, biome 4: 0.55, biome 5+: 0.45 (minimum)
function getBiomeScale(biome) {
// Every 10000m, scale decreases by 5% (0.95x), looping every 40000m
// biome: 1,2,3,4,5... (biome 1 = 0-9999m, biome 2 = 10000-19999m, etc)
// At biome 1: 1.0, biome 2: 0.95, biome 3: 0.9025, biome 4: 0.857375, biome 5: 0.81450625, then repeat
var biomeIndex = (biome - 1) % 4; // 0,1,2,3,0,1,2,3...
var scale = 1.0;
for (var i = 0; i < biomeIndex; ++i) {
scale *= 0.95;
}
return scale;
}
var currentBiomeScale = 1.0;
// Explosion effect (for flash)
// Obstacle
// Fuel
// Rocket (player)
// --- Global variables ---
var rocket;
var fuels = [];
var obstacles = [];
var dragStartX = null;
var dragRocketStartX = null;
var isDragging = false;
var fuelTimer = 0;
var obstacleTimer = 0;
var distance = 0;
// var best = typeof storage.best === "number" && storage.best > 0 ? storage.best : 0;
var fuelDepleteRate = 0.18; // per tick
var rocketSpeed = 8; // px per tick (vertical)
var fuelSpawnInterval = 90; // ticks
var obstacleSpawnInterval = 110; // ticks
var minX = 100,
maxX = 2048 - 100;
var minY = 200,
maxY = 2732 - 200;
var fuelMax = 100;
var fuelAmount = fuelMax * 0.2; // Yakıt toplandığında %20 doldurur
var gameStarted = false;
// --- UI ---
var distanceTxt = new Text2('0 m', {
size: 90,
fill: 0xFFFFFF
});
distanceTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(distanceTxt);
// Best score UI (bottom left)
var bestScore = typeof storage.best === "number" && storage.best > 0 ? storage.best : 0;
var bestTxt = new Text2('best: ' + Math.floor(bestScore) + ' m', {
size: 70,
fill: 0xffffff,
fontWeight: 'bold'
});
bestTxt.anchor.set(0, 1); // bottom left
LK.gui.bottomLeft.addChild(bestTxt);
bestTxt.x = 20;
bestTxt.y = -20;
// Last score UI (bottom left, below best)
var lastScore = 0;
var lastTxt = new Text2('last: 0 m', {
size: 60,
fill: 0xffffff
});
lastTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(lastTxt);
lastTxt.x = 20;
lastTxt.y = bestTxt.y + bestTxt.height + 10;
// Fuel bar (simple text)
var fuelTxt = new Text2('Yakıt: 100%', {
size: 60,
fill: 0xff2222
});
fuelTxt.anchor.set(0.5, 0);
// Place fuelTxt below distanceTxt (metre göstergesinin altında)
LK.gui.top.addChild(fuelTxt);
fuelTxt.y = distanceTxt.height + 10;
// --- Fuel Bar UI ---
// Black border (slightly bigger than background)
var fuelBarBorder = LK.getAsset('backgroundImage', {
width: 68,
height: 408,
x: 0,
y: 0,
anchorX: 0.5,
anchorY: 0.0
});
fuelBarBorder.tint = 0x000000;
fuelBarBorder.alpha = 1.0;
// Bar background (light gray, inside border)
var fuelBarBg = LK.getAsset('backgroundImage', {
width: 60,
height: 400,
x: 0,
y: 4,
anchorX: 0.5,
anchorY: 0.0
});
fuelBarBg.tint = 0xeeeeee;
fuelBarBg.alpha = 1.0;
// Bar fill (will be resized from top down)
var fuelBarFill = LK.getAsset('backgroundImage', {
width: 44,
height: 392,
x: 0,
y: 8,
anchorX: 0.5,
anchorY: 0.0
});
fuelBarFill.tint = 0x00ff00; // Start as vivid green
fuelBarFill.alpha = 1.0;
// Container for bar
var fuelBarContainer = new Container();
fuelBarContainer.addChild(fuelBarBorder);
fuelBarContainer.addChild(fuelBarBg);
fuelBarContainer.addChild(fuelBarFill);
// Position bar at top right, with margin from top and right
fuelBarContainer.x = 2048 - 80;
fuelBarContainer.y = 100;
// Add to game (not GUI, so it scales with gameplay)
game.addChild(fuelBarContainer);
// (bestTxt positioning removed)
// --- Helper functions ---
function resetGame() {
// Remove old objects
if (rocket) rocket.destroy();
for (var i = 0; i < fuels.length; ++i) fuels[i].destroy();
for (var i = 0; i < obstacles.length; ++i) obstacles[i].destroy();
fuels = [];
obstacles = [];
// Reset variables
distance = 0;
fuelTimer = 0;
obstacleTimer = 0;
currentBiome = 1;
biomeTransitioning = false;
// background and biome2Background removed
// (best score logic removed)
// Create rocket
rocket = new Rocket();
rocket.x = 2048 / 2;
rocket.y = 2732 - 350;
rocket.scaleX = currentBiomeScale;
rocket.scaleY = currentBiomeScale;
if (rocket.children && rocket.children.length > 0) {
rocket.children[0].scaleX = currentBiomeScale;
rocket.children[0].scaleY = currentBiomeScale;
}
game.addChild(rocket);
rocket.fuel = 100;
rocket.alive = true;
gameStarted = true;
updateUI();
}
function updateUI() {
distanceTxt.setText(Math.floor(distance) + ' m');
fuelTxt.setText('Fuel: ' + Math.max(0, Math.floor(rocket ? rocket.fuel : 0)) + '%');
// Update fuel bar fill
if (typeof fuelBarFill !== "undefined" && rocket) {
// Height: 392px max, min 0 (depletes from top to bottom)
var pct = Math.max(0, Math.min(1, rocket.fuel / fuelMax));
fuelBarFill.height = 392 * pct;
// Always keep the top of the fill at the same y (so it shrinks from top)
fuelBarFill.y = 8 + 392 * (1 - pct);
// Vivid color gradient: green > yellow > orange > red
var color;
if (pct > 0.5) {
// Interpolate green (0x00ff00) to yellow (0xffff00)
var t = (pct - 0.5) * 2; // 1 at full, 0 at 0.5
var r = Math.floor(0x00 + (0xff - 0x00) * (1 - t));
var g = 0xff;
var b = 0x00;
color = r << 16 | g << 8 | b;
} else if (pct > 0.2) {
// Interpolate yellow (0xffff00) to orange (0xff8800)
var t = (pct - 0.2) / 0.3; // 1 at 0.5, 0 at 0.2
var r = 0xff;
var g = Math.floor(0x88 + (0xff - 0x88) * t);
var b = 0x00;
color = r << 16 | g << 8 | b;
} else {
// Interpolate orange (0xff8800) to red (0xff0000)
var t = pct / 0.2; // 1 at 0.2, 0 at 0
var r = 0xff;
var g = Math.floor(0x00 + (0x88 - 0x00) * t);
var b = 0x00;
color = r << 16 | g << 8 | b;
}
fuelBarFill.tint = color;
}
}
// --- Input (drag to move rocket) ---
game.down = function (x, y, obj) {
if (!rocket || !rocket.alive) return;
isDragging = true;
dragStartX = x;
dragRocketStartX = rocket.x;
};
game.move = function (x, y, obj) {
if (!rocket || !rocket.alive) return;
if (isDragging) {
var dx = x - dragStartX;
rocket.x = Math.max(minX, Math.min(maxX, dragRocketStartX + dx));
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// --- Main game loop ---
game.update = function () {
if (!rocket || !rocket.alive) return;
// Move rocket up (rocket stays at same y, background moves down)
// (Handled by moving all objects up, not down!)
// Distance increases as if rocket is moving up
distance += rocketSpeed / 6; // scale to meters
// Speed up every 200 meters
if (!rocket.lastSpeedupDistance) rocket.lastSpeedupDistance = 0;
if (Math.floor(distance / 200) > Math.floor(rocket.lastSpeedupDistance / 200)) {
rocketSpeed += 1.5;
rocket.lastSpeedupDistance = distance;
}
// --- Biome/level transition every 10000m ---
// --- Background color logic by distance ---
// Smooth biome background color transitions using tweening
if (typeof game._bgColor === "undefined") game._bgColor = 0x75d1ff;
if (typeof game._bgColorTarget === "undefined") game._bgColorTarget = 0x75d1ff;
if (typeof game._bgColorTweening === "undefined") game._bgColorTweening = false;
function getBiomeBgColor(distance) {
if (distance < 10000) {
return 0x75d1ff; // açık mavi
} else if (distance < 20000) {
return 0x1e90ff; // mavi
} else if (distance < 30000) {
return 0x003366; // koyu mavi
} else if (distance < 40000) {
return 0x22232a; // koyu gri
} else if (distance < 50000) {
return 0x000000; // siyah
} else {
// 50000'den sonra arka plan sürekli renk değiştirsin
// Renk döngüsü: HSV renk çarkı üzerinde döner
var t = (distance - 50000) / 400;
var h = t % 1 * 6;
var c = 1,
x = 1 - Math.abs(h % 2 - 1);
var r = 0,
g = 0,
b = 0;
if (h < 1) {
r = c;
g = x;
b = 0;
} else if (h < 2) {
r = x;
g = c;
b = 0;
} else if (h < 3) {
r = 0;
g = c;
b = x;
} else if (h < 4) {
r = 0;
g = x;
b = c;
} else if (h < 5) {
r = x;
g = 0;
b = c;
} else {
r = c;
g = 0;
b = x;
}
var m = 0.25;
r = Math.floor((r + m) / (1 + m) * 255);
g = Math.floor((g + m) / (1 + m) * 255);
b = Math.floor((b + m) / (1 + m) * 255);
return r << 16 | g << 8 | b;
}
}
// Determine which biome we're in
var biomeIdx = Math.floor(distance / 10000);
var nextBiomeColor = getBiomeBgColor(distance);
// If biome color target changed, tween to new color
if (game._bgColorTarget !== nextBiomeColor && distance < 50000) {
// Only tween for biome transitions below 50000m, above that we cycle colors
if (!game._bgColorTweening) {
var fromColor = game._bgColor;
var toColor = nextBiomeColor;
// Decompose colors
var fromR = fromColor >> 16 & 0xff,
fromG = fromColor >> 8 & 0xff,
fromB = fromColor & 0xff;
var toR = toColor >> 16 & 0xff,
toG = toColor >> 8 & 0xff,
toB = toColor & 0xff;
var colorObj = {
r: fromR,
g: fromG,
b: fromB
};
game._bgColorTweening = true;
game._bgColorTarget = nextBiomeColor;
tween(colorObj, {
r: toR,
g: toG,
b: toB
}, {
duration: 1200,
easing: tween.cubicInOut,
onUpdate: function onUpdate(obj) {
var c = Math.round(obj.r) << 16 | Math.round(obj.g) << 8 | Math.round(obj.b);
game._bgColor = c;
game.setBackgroundColor(c);
},
onFinish: function onFinish() {
game._bgColor = nextBiomeColor;
game.setBackgroundColor(nextBiomeColor);
game._bgColorTweening = false;
}
});
}
} else if (distance < 50000) {
// Not transitioning, just set color
if (!game._bgColorTweening) {
game._bgColor = nextBiomeColor;
game._bgColorTarget = nextBiomeColor;
game.setBackgroundColor(nextBiomeColor);
}
} else {
// 50000m+ : color cycles, no tween, set instantly
var cycleColor = getBiomeBgColor(distance);
game._bgColor = cycleColor;
game._bgColorTarget = cycleColor;
game.setBackgroundColor(cycleColor);
}
if (!biomeTransitioning && Math.floor(distance / biomeTransitionDistance) + 1 !== currentBiome) {
biomeTransitioning = true;
var nextBiome = Math.floor(distance / biomeTransitionDistance) + 1;
// Slow rocket
var oldSpeed = rocketSpeed;
tween({
v: oldSpeed
}, {
v: 6
}, {
duration: 1200,
easing: tween.cubicOut,
onUpdate: function onUpdate(obj) {
rocketSpeed = obj.v;
},
onFinish: function onFinish() {
rocketSpeed = 6;
currentBiome = nextBiome;
biomeTransitioning = false;
// --- Scale down rocket, fuels, and obstacles for new biome ---
var newScale = getBiomeScale(currentBiome);
if (Math.abs(currentBiomeScale - newScale) > 0.01) {
currentBiomeScale = newScale;
// Tween rocket scale
if (rocket) {
tween(rocket, {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
// Also scale attached asset if needed
if (rocket.children && rocket.children.length > 0) {
tween(rocket.children[0], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
}
}
// Tween all fuels
for (var i = 0; i < fuels.length; ++i) {
tween(fuels[i], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
if (fuels[i].children && fuels[i].children.length > 0) {
tween(fuels[i].children[0], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
}
}
// Tween all obstacles
for (var i = 0; i < obstacles.length; ++i) {
tween(obstacles[i], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
if (obstacles[i].children && obstacles[i].children.length > 0) {
tween(obstacles[i].children[0], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
}
}
}
}
});
// background fade logic removed
}
updateUI();
// Deplete fuel
rocket.fuel -= fuelDepleteRate;
if (rocket.fuel <= 0) {
rocket.fuel = 0;
rocket.alive = false;
LK.effects.flashScreen(0xff0000, 800);
// Update last score and best score if needed
lastScore = Math.floor(distance);
lastTxt.setText('last: ' + lastScore + ' m');
if (lastScore > bestScore) {
bestScore = lastScore;
bestTxt.setText('best: ' + bestScore + ' m');
storage.best = bestScore;
}
// Show "your score is: X m" message in the center of the screen for 5 seconds, then show game over popup
var scoreMsg = new Text2('your score is: ' + Math.floor(distance) + ' m', {
size: 120,
fill: 0xffff00,
align: 'center',
fontWeight: 'bold'
});
scoreMsg.anchor.set(0.5, 0.5);
scoreMsg.x = 2048 / 2;
scoreMsg.y = 2732 / 2;
game.addChild(scoreMsg);
LK.setTimeout(function () {
if (scoreMsg && scoreMsg.parent) scoreMsg.parent.removeChild(scoreMsg);
LK.showGameOver();
}, 5000);
return;
}
// Move fuels and check collision
for (var i = fuels.length - 1; i >= 0; --i) {
var f = fuels[i];
f.y += rocketSpeed; // move up relative to rocket (background moves down)
if (f.y > maxY + 200) {
f.destroy();
fuels.splice(i, 1);
continue;
}
if (rocket.intersects(f)) {
rocket.fuel = Math.min(fuelMax, rocket.fuel + fuelAmount);
f.destroy();
fuels.splice(i, 1);
// Small flash effect
LK.effects.flashObject(rocket, 0x44ddee, 300);
updateUI();
}
}
// Move obstacles and check collision
for (var i = obstacles.length - 1; i >= 0; --i) {
var o = obstacles[i];
o.y += rocketSpeed; // move up relative to rocket (background moves down)
// Move Obstacle1, Obstacle2, Obstacle3, and Obstacle4 from right to left or left to right randomly, keep Obstacle stationary
if (o.constructor && (o.constructor === Obstacle1 || o.constructor === Obstacle2 || o.constructor === Obstacle3 || o.constructor === Obstacle4) || typeof o.children !== "undefined" && o.children.length > 0 && o.children[0].assetId && (o.children[0].assetId === "obstacle1" || o.children[0].assetId === "obstacle2" || o.children[0].assetId === "obstacle3" || o.children[0].assetId === "obstacle4")) {
// Assign direction and speed if not set
if (typeof o.moveDir === "undefined") {
// 0: right-to-left, 1: left-to-right
o.moveDir = Math.random() < 0.5 ? 0 : 1;
o.moveSpeed = 6 + Math.random() * 3;
}
if (typeof o.lastX === "undefined") o.lastX = o.x;
if (o.moveDir === 0) {
o.x -= o.moveSpeed;
// Bounce off left edge
if (o.lastX >= minX + o.width / 2 && o.x < minX + o.width / 2) {
o.x = minX + o.width / 2;
o.moveDir = 1; // Change direction to right
}
} else {
o.x += o.moveSpeed;
// Bounce off right edge
if (o.lastX <= maxX - o.width / 2 && o.x > maxX - o.width / 2) {
o.x = maxX - o.width / 2;
o.moveDir = 0; // Change direction to left
}
}
o.lastX = o.x;
}
// --- Biome 2: obstacles move horizontally ---
// (removed old biome2Dir logic)
if (o.y > maxY + 200) {
o.destroy();
obstacles.splice(i, 1);
continue;
}
if (rocket.intersects(o)) {
// Only lose 10% fuel, don't die instantly
var lostFuel = fuelMax * 0.10;
rocket.fuel = Math.max(0, rocket.fuel - lostFuel);
LK.effects.flashObject(rocket, 0xff4444, 400);
// --- Rocket collision animation: shake horizontally ---
// Stop any previous tweens on rocket.x
tween.stop(rocket, {
x: true
});
var originalX = rocket.x;
// Animate shake: left, right, center
tween(rocket, {
x: originalX - 40
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(rocket, {
x: originalX + 40
}, {
duration: 90,
easing: tween.cubicInOut,
onFinish: function onFinish() {
tween(rocket, {
x: originalX
}, {
duration: 70,
easing: tween.cubicIn
});
}
});
}
});
// --- End rocket collision animation ---
obstacles.splice(i, 1);
o.destroy();
updateUI();
// If fuel is now 0, trigger game over as normal
if (rocket.fuel <= 0) {
rocket.fuel = 0;
rocket.alive = false;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
continue;
}
}
// Spawn fuel
fuelTimer++;
if (fuelTimer >= fuelSpawnInterval) {
fuelTimer = 0;
var f = new Fuel();
f.x = Math.floor(minX + Math.random() * (maxX - minX));
// Spawn above the visible screen, so they fall into view from the top
f.y = rocket.y - 2732 / 2 - 200 - Math.random() * 300;
f.scaleX = currentBiomeScale;
f.scaleY = currentBiomeScale;
if (f.children && f.children.length > 0) {
f.children[0].scaleX = currentBiomeScale;
f.children[0].scaleY = currentBiomeScale;
}
game.addChild(f);
fuels.push(f);
}
// Spawn obstacle
obstacleTimer++;
if (obstacleTimer >= obstacleSpawnInterval) {
obstacleTimer = 0;
var o;
if (distance >= 50000) {
// After 50000m, spawn a random mix of all obstacle types
var obstacleTypes = [Obstacle, Obstacle1, Obstacle2, Obstacle3, Obstacle4];
var idx = Math.floor(Math.random() * obstacleTypes.length);
o = new obstacleTypes[idx]();
} else if (distance >= 40000) {
o = new Obstacle4();
} else if (distance >= 30000 && distance < 40000) {
o = new Obstacle3();
} else if (distance >= 20000 && distance < 30000) {
o = new Obstacle2();
} else if (distance >= 10000 && distance < 20000) {
o = new Obstacle1();
} else {
o = new Obstacle();
}
o.x = Math.floor(minX + Math.random() * (maxX - minX));
// Spawn above the visible screen, so they fall into view from the top
o.y = rocket.y - 2732 / 2 - 400 - Math.random() * 400;
o.scaleX = currentBiomeScale;
o.scaleY = currentBiomeScale;
if (o.children && o.children.length > 0) {
o.children[0].scaleX = currentBiomeScale;
o.children[0].scaleY = currentBiomeScale;
}
game.addChild(o);
obstacles.push(o);
}
};
// --- Game over / win handling ---
LK.on('gameover', function () {
gameStarted = false;
updateUI();
});
// --- Start game ---
resetGame();
if (obstacleTimer >= obstacleSpawnInterval) {
obstacleTimer = 0;
var o;
if (distance >= 20000) {
o = new Obstacle3();
} else if (distance >= 10000) {
o = new Obstacle2();
} else {
o = new Obstacle();
}
o.x = Math.floor(minX + Math.random() * (maxX - minX));
// Spawn above the visible screen, so they fall into view from the top
o.y = rocket.y - 2732 / 2 - 400 - Math.random() * 400;
o.scaleX = currentBiomeScale;
o.scaleY = currentBiomeScale;
if (o.children && o.children.length > 0) {
o.children[0].scaleX = currentBiomeScale;
o.children[0].scaleY = currentBiomeScale;
}
game.addChild(o);
obstacles.push(o);
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
best: 0
});
/****
* Classes
****/
// Fuel class
var Fuel = Container.expand(function () {
var self = Container.call(this);
var fuelGfx = self.attachAsset('fuel', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = fuelGfx.width;
self.height = fuelGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle1 class (obstacle1 asset, for 10000-19999m)
var Obstacle1 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle1', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle2 class (different look/behavior for >20000m)
var Obstacle2 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle2', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle3 class (for >20000m, new look/behavior)
var Obstacle3 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle3', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Obstacle4 class (for >40000m, new look/behavior)
var Obstacle4 = Container.expand(function () {
var self = Container.call(this);
var obsGfx = self.attachAsset('obstacle4', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = obsGfx.width;
self.height = obsGfx.height;
self.update = function () {
// Movement handled in game.update
};
return self;
});
// Rocket (player) class
var Rocket = Container.expand(function () {
var self = Container.call(this);
// Attach rocket asset, centered
var rocketGfx = self.attachAsset('rocket', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = rocketGfx.width;
self.height = rocketGfx.height;
// Rocket state
self.fuel = 100;
self.alive = true;
// No per-frame update needed (movement handled in game.update)
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// --- Biome/level state ---
var currentBiome = 1;
var biomeTransitioning = false;
var biomeTransitionDistance = 10000;
// --- Biome scaling ---
// At biome 1: scale = 1.0, biome 2: 0.8, biome 3: 0.65, biome 4: 0.55, biome 5+: 0.45 (minimum)
function getBiomeScale(biome) {
// Every 10000m, scale decreases by 5% (0.95x), looping every 40000m
// biome: 1,2,3,4,5... (biome 1 = 0-9999m, biome 2 = 10000-19999m, etc)
// At biome 1: 1.0, biome 2: 0.95, biome 3: 0.9025, biome 4: 0.857375, biome 5: 0.81450625, then repeat
var biomeIndex = (biome - 1) % 4; // 0,1,2,3,0,1,2,3...
var scale = 1.0;
for (var i = 0; i < biomeIndex; ++i) {
scale *= 0.95;
}
return scale;
}
var currentBiomeScale = 1.0;
// Explosion effect (for flash)
// Obstacle
// Fuel
// Rocket (player)
// --- Global variables ---
var rocket;
var fuels = [];
var obstacles = [];
var dragStartX = null;
var dragRocketStartX = null;
var isDragging = false;
var fuelTimer = 0;
var obstacleTimer = 0;
var distance = 0;
// var best = typeof storage.best === "number" && storage.best > 0 ? storage.best : 0;
var fuelDepleteRate = 0.18; // per tick
var rocketSpeed = 8; // px per tick (vertical)
var fuelSpawnInterval = 90; // ticks
var obstacleSpawnInterval = 110; // ticks
var minX = 100,
maxX = 2048 - 100;
var minY = 200,
maxY = 2732 - 200;
var fuelMax = 100;
var fuelAmount = fuelMax * 0.2; // Yakıt toplandığında %20 doldurur
var gameStarted = false;
// --- UI ---
var distanceTxt = new Text2('0 m', {
size: 90,
fill: 0xFFFFFF
});
distanceTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(distanceTxt);
// Best score UI (bottom left)
var bestScore = typeof storage.best === "number" && storage.best > 0 ? storage.best : 0;
var bestTxt = new Text2('best: ' + Math.floor(bestScore) + ' m', {
size: 70,
fill: 0xffffff,
fontWeight: 'bold'
});
bestTxt.anchor.set(0, 1); // bottom left
LK.gui.bottomLeft.addChild(bestTxt);
bestTxt.x = 20;
bestTxt.y = -20;
// Last score UI (bottom left, below best)
var lastScore = 0;
var lastTxt = new Text2('last: 0 m', {
size: 60,
fill: 0xffffff
});
lastTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(lastTxt);
lastTxt.x = 20;
lastTxt.y = bestTxt.y + bestTxt.height + 10;
// Fuel bar (simple text)
var fuelTxt = new Text2('Yakıt: 100%', {
size: 60,
fill: 0xff2222
});
fuelTxt.anchor.set(0.5, 0);
// Place fuelTxt below distanceTxt (metre göstergesinin altında)
LK.gui.top.addChild(fuelTxt);
fuelTxt.y = distanceTxt.height + 10;
// --- Fuel Bar UI ---
// Black border (slightly bigger than background)
var fuelBarBorder = LK.getAsset('backgroundImage', {
width: 68,
height: 408,
x: 0,
y: 0,
anchorX: 0.5,
anchorY: 0.0
});
fuelBarBorder.tint = 0x000000;
fuelBarBorder.alpha = 1.0;
// Bar background (light gray, inside border)
var fuelBarBg = LK.getAsset('backgroundImage', {
width: 60,
height: 400,
x: 0,
y: 4,
anchorX: 0.5,
anchorY: 0.0
});
fuelBarBg.tint = 0xeeeeee;
fuelBarBg.alpha = 1.0;
// Bar fill (will be resized from top down)
var fuelBarFill = LK.getAsset('backgroundImage', {
width: 44,
height: 392,
x: 0,
y: 8,
anchorX: 0.5,
anchorY: 0.0
});
fuelBarFill.tint = 0x00ff00; // Start as vivid green
fuelBarFill.alpha = 1.0;
// Container for bar
var fuelBarContainer = new Container();
fuelBarContainer.addChild(fuelBarBorder);
fuelBarContainer.addChild(fuelBarBg);
fuelBarContainer.addChild(fuelBarFill);
// Position bar at top right, with margin from top and right
fuelBarContainer.x = 2048 - 80;
fuelBarContainer.y = 100;
// Add to game (not GUI, so it scales with gameplay)
game.addChild(fuelBarContainer);
// (bestTxt positioning removed)
// --- Helper functions ---
function resetGame() {
// Remove old objects
if (rocket) rocket.destroy();
for (var i = 0; i < fuels.length; ++i) fuels[i].destroy();
for (var i = 0; i < obstacles.length; ++i) obstacles[i].destroy();
fuels = [];
obstacles = [];
// Reset variables
distance = 0;
fuelTimer = 0;
obstacleTimer = 0;
currentBiome = 1;
biomeTransitioning = false;
// background and biome2Background removed
// (best score logic removed)
// Create rocket
rocket = new Rocket();
rocket.x = 2048 / 2;
rocket.y = 2732 - 350;
rocket.scaleX = currentBiomeScale;
rocket.scaleY = currentBiomeScale;
if (rocket.children && rocket.children.length > 0) {
rocket.children[0].scaleX = currentBiomeScale;
rocket.children[0].scaleY = currentBiomeScale;
}
game.addChild(rocket);
rocket.fuel = 100;
rocket.alive = true;
gameStarted = true;
updateUI();
}
function updateUI() {
distanceTxt.setText(Math.floor(distance) + ' m');
fuelTxt.setText('Fuel: ' + Math.max(0, Math.floor(rocket ? rocket.fuel : 0)) + '%');
// Update fuel bar fill
if (typeof fuelBarFill !== "undefined" && rocket) {
// Height: 392px max, min 0 (depletes from top to bottom)
var pct = Math.max(0, Math.min(1, rocket.fuel / fuelMax));
fuelBarFill.height = 392 * pct;
// Always keep the top of the fill at the same y (so it shrinks from top)
fuelBarFill.y = 8 + 392 * (1 - pct);
// Vivid color gradient: green > yellow > orange > red
var color;
if (pct > 0.5) {
// Interpolate green (0x00ff00) to yellow (0xffff00)
var t = (pct - 0.5) * 2; // 1 at full, 0 at 0.5
var r = Math.floor(0x00 + (0xff - 0x00) * (1 - t));
var g = 0xff;
var b = 0x00;
color = r << 16 | g << 8 | b;
} else if (pct > 0.2) {
// Interpolate yellow (0xffff00) to orange (0xff8800)
var t = (pct - 0.2) / 0.3; // 1 at 0.5, 0 at 0.2
var r = 0xff;
var g = Math.floor(0x88 + (0xff - 0x88) * t);
var b = 0x00;
color = r << 16 | g << 8 | b;
} else {
// Interpolate orange (0xff8800) to red (0xff0000)
var t = pct / 0.2; // 1 at 0.2, 0 at 0
var r = 0xff;
var g = Math.floor(0x00 + (0x88 - 0x00) * t);
var b = 0x00;
color = r << 16 | g << 8 | b;
}
fuelBarFill.tint = color;
}
}
// --- Input (drag to move rocket) ---
game.down = function (x, y, obj) {
if (!rocket || !rocket.alive) return;
isDragging = true;
dragStartX = x;
dragRocketStartX = rocket.x;
};
game.move = function (x, y, obj) {
if (!rocket || !rocket.alive) return;
if (isDragging) {
var dx = x - dragStartX;
rocket.x = Math.max(minX, Math.min(maxX, dragRocketStartX + dx));
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// --- Main game loop ---
game.update = function () {
if (!rocket || !rocket.alive) return;
// Move rocket up (rocket stays at same y, background moves down)
// (Handled by moving all objects up, not down!)
// Distance increases as if rocket is moving up
distance += rocketSpeed / 6; // scale to meters
// Speed up every 200 meters
if (!rocket.lastSpeedupDistance) rocket.lastSpeedupDistance = 0;
if (Math.floor(distance / 200) > Math.floor(rocket.lastSpeedupDistance / 200)) {
rocketSpeed += 1.5;
rocket.lastSpeedupDistance = distance;
}
// --- Biome/level transition every 10000m ---
// --- Background color logic by distance ---
// Smooth biome background color transitions using tweening
if (typeof game._bgColor === "undefined") game._bgColor = 0x75d1ff;
if (typeof game._bgColorTarget === "undefined") game._bgColorTarget = 0x75d1ff;
if (typeof game._bgColorTweening === "undefined") game._bgColorTweening = false;
function getBiomeBgColor(distance) {
if (distance < 10000) {
return 0x75d1ff; // açık mavi
} else if (distance < 20000) {
return 0x1e90ff; // mavi
} else if (distance < 30000) {
return 0x003366; // koyu mavi
} else if (distance < 40000) {
return 0x22232a; // koyu gri
} else if (distance < 50000) {
return 0x000000; // siyah
} else {
// 50000'den sonra arka plan sürekli renk değiştirsin
// Renk döngüsü: HSV renk çarkı üzerinde döner
var t = (distance - 50000) / 400;
var h = t % 1 * 6;
var c = 1,
x = 1 - Math.abs(h % 2 - 1);
var r = 0,
g = 0,
b = 0;
if (h < 1) {
r = c;
g = x;
b = 0;
} else if (h < 2) {
r = x;
g = c;
b = 0;
} else if (h < 3) {
r = 0;
g = c;
b = x;
} else if (h < 4) {
r = 0;
g = x;
b = c;
} else if (h < 5) {
r = x;
g = 0;
b = c;
} else {
r = c;
g = 0;
b = x;
}
var m = 0.25;
r = Math.floor((r + m) / (1 + m) * 255);
g = Math.floor((g + m) / (1 + m) * 255);
b = Math.floor((b + m) / (1 + m) * 255);
return r << 16 | g << 8 | b;
}
}
// Determine which biome we're in
var biomeIdx = Math.floor(distance / 10000);
var nextBiomeColor = getBiomeBgColor(distance);
// If biome color target changed, tween to new color
if (game._bgColorTarget !== nextBiomeColor && distance < 50000) {
// Only tween for biome transitions below 50000m, above that we cycle colors
if (!game._bgColorTweening) {
var fromColor = game._bgColor;
var toColor = nextBiomeColor;
// Decompose colors
var fromR = fromColor >> 16 & 0xff,
fromG = fromColor >> 8 & 0xff,
fromB = fromColor & 0xff;
var toR = toColor >> 16 & 0xff,
toG = toColor >> 8 & 0xff,
toB = toColor & 0xff;
var colorObj = {
r: fromR,
g: fromG,
b: fromB
};
game._bgColorTweening = true;
game._bgColorTarget = nextBiomeColor;
tween(colorObj, {
r: toR,
g: toG,
b: toB
}, {
duration: 1200,
easing: tween.cubicInOut,
onUpdate: function onUpdate(obj) {
var c = Math.round(obj.r) << 16 | Math.round(obj.g) << 8 | Math.round(obj.b);
game._bgColor = c;
game.setBackgroundColor(c);
},
onFinish: function onFinish() {
game._bgColor = nextBiomeColor;
game.setBackgroundColor(nextBiomeColor);
game._bgColorTweening = false;
}
});
}
} else if (distance < 50000) {
// Not transitioning, just set color
if (!game._bgColorTweening) {
game._bgColor = nextBiomeColor;
game._bgColorTarget = nextBiomeColor;
game.setBackgroundColor(nextBiomeColor);
}
} else {
// 50000m+ : color cycles, no tween, set instantly
var cycleColor = getBiomeBgColor(distance);
game._bgColor = cycleColor;
game._bgColorTarget = cycleColor;
game.setBackgroundColor(cycleColor);
}
if (!biomeTransitioning && Math.floor(distance / biomeTransitionDistance) + 1 !== currentBiome) {
biomeTransitioning = true;
var nextBiome = Math.floor(distance / biomeTransitionDistance) + 1;
// Slow rocket
var oldSpeed = rocketSpeed;
tween({
v: oldSpeed
}, {
v: 6
}, {
duration: 1200,
easing: tween.cubicOut,
onUpdate: function onUpdate(obj) {
rocketSpeed = obj.v;
},
onFinish: function onFinish() {
rocketSpeed = 6;
currentBiome = nextBiome;
biomeTransitioning = false;
// --- Scale down rocket, fuels, and obstacles for new biome ---
var newScale = getBiomeScale(currentBiome);
if (Math.abs(currentBiomeScale - newScale) > 0.01) {
currentBiomeScale = newScale;
// Tween rocket scale
if (rocket) {
tween(rocket, {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
// Also scale attached asset if needed
if (rocket.children && rocket.children.length > 0) {
tween(rocket.children[0], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
}
}
// Tween all fuels
for (var i = 0; i < fuels.length; ++i) {
tween(fuels[i], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
if (fuels[i].children && fuels[i].children.length > 0) {
tween(fuels[i].children[0], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
}
}
// Tween all obstacles
for (var i = 0; i < obstacles.length; ++i) {
tween(obstacles[i], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
if (obstacles[i].children && obstacles[i].children.length > 0) {
tween(obstacles[i].children[0], {
scaleX: newScale,
scaleY: newScale
}, {
duration: 600,
easing: tween.cubicInOut
});
}
}
}
}
});
// background fade logic removed
}
updateUI();
// Deplete fuel
rocket.fuel -= fuelDepleteRate;
if (rocket.fuel <= 0) {
rocket.fuel = 0;
rocket.alive = false;
LK.effects.flashScreen(0xff0000, 800);
// Update last score and best score if needed
lastScore = Math.floor(distance);
lastTxt.setText('last: ' + lastScore + ' m');
if (lastScore > bestScore) {
bestScore = lastScore;
bestTxt.setText('best: ' + bestScore + ' m');
storage.best = bestScore;
}
// Show "your score is: X m" message in the center of the screen for 5 seconds, then show game over popup
var scoreMsg = new Text2('your score is: ' + Math.floor(distance) + ' m', {
size: 120,
fill: 0xffff00,
align: 'center',
fontWeight: 'bold'
});
scoreMsg.anchor.set(0.5, 0.5);
scoreMsg.x = 2048 / 2;
scoreMsg.y = 2732 / 2;
game.addChild(scoreMsg);
LK.setTimeout(function () {
if (scoreMsg && scoreMsg.parent) scoreMsg.parent.removeChild(scoreMsg);
LK.showGameOver();
}, 5000);
return;
}
// Move fuels and check collision
for (var i = fuels.length - 1; i >= 0; --i) {
var f = fuels[i];
f.y += rocketSpeed; // move up relative to rocket (background moves down)
if (f.y > maxY + 200) {
f.destroy();
fuels.splice(i, 1);
continue;
}
if (rocket.intersects(f)) {
rocket.fuel = Math.min(fuelMax, rocket.fuel + fuelAmount);
f.destroy();
fuels.splice(i, 1);
// Small flash effect
LK.effects.flashObject(rocket, 0x44ddee, 300);
updateUI();
}
}
// Move obstacles and check collision
for (var i = obstacles.length - 1; i >= 0; --i) {
var o = obstacles[i];
o.y += rocketSpeed; // move up relative to rocket (background moves down)
// Move Obstacle1, Obstacle2, Obstacle3, and Obstacle4 from right to left or left to right randomly, keep Obstacle stationary
if (o.constructor && (o.constructor === Obstacle1 || o.constructor === Obstacle2 || o.constructor === Obstacle3 || o.constructor === Obstacle4) || typeof o.children !== "undefined" && o.children.length > 0 && o.children[0].assetId && (o.children[0].assetId === "obstacle1" || o.children[0].assetId === "obstacle2" || o.children[0].assetId === "obstacle3" || o.children[0].assetId === "obstacle4")) {
// Assign direction and speed if not set
if (typeof o.moveDir === "undefined") {
// 0: right-to-left, 1: left-to-right
o.moveDir = Math.random() < 0.5 ? 0 : 1;
o.moveSpeed = 6 + Math.random() * 3;
}
if (typeof o.lastX === "undefined") o.lastX = o.x;
if (o.moveDir === 0) {
o.x -= o.moveSpeed;
// Bounce off left edge
if (o.lastX >= minX + o.width / 2 && o.x < minX + o.width / 2) {
o.x = minX + o.width / 2;
o.moveDir = 1; // Change direction to right
}
} else {
o.x += o.moveSpeed;
// Bounce off right edge
if (o.lastX <= maxX - o.width / 2 && o.x > maxX - o.width / 2) {
o.x = maxX - o.width / 2;
o.moveDir = 0; // Change direction to left
}
}
o.lastX = o.x;
}
// --- Biome 2: obstacles move horizontally ---
// (removed old biome2Dir logic)
if (o.y > maxY + 200) {
o.destroy();
obstacles.splice(i, 1);
continue;
}
if (rocket.intersects(o)) {
// Only lose 10% fuel, don't die instantly
var lostFuel = fuelMax * 0.10;
rocket.fuel = Math.max(0, rocket.fuel - lostFuel);
LK.effects.flashObject(rocket, 0xff4444, 400);
// --- Rocket collision animation: shake horizontally ---
// Stop any previous tweens on rocket.x
tween.stop(rocket, {
x: true
});
var originalX = rocket.x;
// Animate shake: left, right, center
tween(rocket, {
x: originalX - 40
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(rocket, {
x: originalX + 40
}, {
duration: 90,
easing: tween.cubicInOut,
onFinish: function onFinish() {
tween(rocket, {
x: originalX
}, {
duration: 70,
easing: tween.cubicIn
});
}
});
}
});
// --- End rocket collision animation ---
obstacles.splice(i, 1);
o.destroy();
updateUI();
// If fuel is now 0, trigger game over as normal
if (rocket.fuel <= 0) {
rocket.fuel = 0;
rocket.alive = false;
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
continue;
}
}
// Spawn fuel
fuelTimer++;
if (fuelTimer >= fuelSpawnInterval) {
fuelTimer = 0;
var f = new Fuel();
f.x = Math.floor(minX + Math.random() * (maxX - minX));
// Spawn above the visible screen, so they fall into view from the top
f.y = rocket.y - 2732 / 2 - 200 - Math.random() * 300;
f.scaleX = currentBiomeScale;
f.scaleY = currentBiomeScale;
if (f.children && f.children.length > 0) {
f.children[0].scaleX = currentBiomeScale;
f.children[0].scaleY = currentBiomeScale;
}
game.addChild(f);
fuels.push(f);
}
// Spawn obstacle
obstacleTimer++;
if (obstacleTimer >= obstacleSpawnInterval) {
obstacleTimer = 0;
var o;
if (distance >= 50000) {
// After 50000m, spawn a random mix of all obstacle types
var obstacleTypes = [Obstacle, Obstacle1, Obstacle2, Obstacle3, Obstacle4];
var idx = Math.floor(Math.random() * obstacleTypes.length);
o = new obstacleTypes[idx]();
} else if (distance >= 40000) {
o = new Obstacle4();
} else if (distance >= 30000 && distance < 40000) {
o = new Obstacle3();
} else if (distance >= 20000 && distance < 30000) {
o = new Obstacle2();
} else if (distance >= 10000 && distance < 20000) {
o = new Obstacle1();
} else {
o = new Obstacle();
}
o.x = Math.floor(minX + Math.random() * (maxX - minX));
// Spawn above the visible screen, so they fall into view from the top
o.y = rocket.y - 2732 / 2 - 400 - Math.random() * 400;
o.scaleX = currentBiomeScale;
o.scaleY = currentBiomeScale;
if (o.children && o.children.length > 0) {
o.children[0].scaleX = currentBiomeScale;
o.children[0].scaleY = currentBiomeScale;
}
game.addChild(o);
obstacles.push(o);
}
};
// --- Game over / win handling ---
LK.on('gameover', function () {
gameStarted = false;
updateUI();
});
// --- Start game ---
resetGame();
if (obstacleTimer >= obstacleSpawnInterval) {
obstacleTimer = 0;
var o;
if (distance >= 20000) {
o = new Obstacle3();
} else if (distance >= 10000) {
o = new Obstacle2();
} else {
o = new Obstacle();
}
o.x = Math.floor(minX + Math.random() * (maxX - minX));
// Spawn above the visible screen, so they fall into view from the top
o.y = rocket.y - 2732 / 2 - 400 - Math.random() * 400;
o.scaleX = currentBiomeScale;
o.scaleY = currentBiomeScale;
if (o.children && o.children.length > 0) {
o.children[0].scaleX = currentBiomeScale;
o.children[0].scaleY = currentBiomeScale;
}
game.addChild(o);
obstacles.push(o);
}