/**** * 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);
}