User prompt
We will have a chance to lose hearts in the game. When a car hits the end of the screen, a heart will go away. If there are no hearts, the game is over.
User prompt
hearts will have a different appearance
User prompt
We start the game with 1 heart, every 5 levels 1 heart will increase, there will be a maximum of 3 hearts, the claps will be at the bottom left corner of the screen.
User prompt
The holes will be more visible and slightly larger.
User prompt
The holes will have a different visual
User prompt
Update game.update to animate pits, spawn pits, and handle pit collision
User prompt
The thorns and pits that will kill us will come and will not slide to the far left and right of the screen every now and then.
User prompt
The thorns and pits that will kill us will come and will not slide to the far left and right of the screen every now and then.
User prompt
Every once in a while, thorns and holes that will kill us will come not from the right and left of the screen, but from the right, left and a little from the sides of the middle.
User prompt
The dust cloud will come from the middle and right and left of the screen.
User prompt
The damage indicator of enemy cars will be displayed at the first hit location
User prompt
Enemy cars will not come from the left and right of the screen, they will come from the middle and right and left.
User prompt
enemy cars will come from the middle
User prompt
The cactus will be a little more and will only be on the right and left of the screen, not in the middle
User prompt
With the accident effect, the hit part of the cars will contain a different image.
User prompt
When you say car to the dust cloud, the screen will blur a little and then go back after 2 seconds.
User prompt
The background will be a desert land and there will be cactuses around and there will be a dust cloud in between.
User prompt
Vehicles can only be damaged from the front and rear
User prompt
Damage indicator will be on the ends of enemy cars
User prompt
The game will end when the enemy car hits the end of the screen.
User prompt
The game will end when the enemy car passes us.
User prompt
When we hit cars, the car we hit will die, we won't die.
Code edit (1 edits merged)
Please save this source code
User prompt
CarCvash: Arena Crashers
Initial prompt
CarCvash is a fast-paced arena action game where you control a car, navigating a vertically scrolling arena while being chased by enemy vehicles. Your objective is to crash into and destroy enemy cars to earn points and advance through levels. Each new level introduces tougher enemies with unique abilities, increasing the challenge. As you progress, your car receives upgrades to improve survivability and offensive power. The game ends when your car is destroyed by enemy vehicles.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Enemy Car Class (basic) var EnemyCar = Container.expand(function () { var self = Container.call(this); var car = self.attachAsset('enemyCar', { anchorX: 0.5, anchorY: 0.5 }); self.width = car.width; self.height = car.height; self.speed = 12; // Will increase with level self.type = 1; self.hp = 1; self.crashed = false; // Damage indicator graphics (front, back, left, right) var indicatorLength = car.width * 0.7; var indicatorThickness = 22; var indicatorColor = 0xffeb3b; // Front indicator (top of car) var frontIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -car.height / 2 + indicatorThickness / 2, scaleX: indicatorLength / 220, scaleY: indicatorThickness / 120, alpha: 0.7 }); // Back indicator (bottom of car) var backIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: car.height / 2 - indicatorThickness / 2, scaleX: indicatorLength / 220, scaleY: indicatorThickness / 120, alpha: 0.7 }); // Left indicator (left side of car) var leftIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: -car.width / 2 + indicatorThickness / 2, y: 0, scaleX: indicatorThickness / 120, scaleY: indicatorLength / 220, alpha: 0.7, rotation: Math.PI / 2 }); // Right indicator (right side of car) var rightIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: car.width / 2 - indicatorThickness / 2, y: 0, scaleX: indicatorThickness / 120, scaleY: indicatorLength / 220, alpha: 0.7, rotation: Math.PI / 2 }); // Hide all indicators initially frontIndicator.visible = false; backIndicator.visible = false; leftIndicator.visible = false; rightIndicator.visible = false; // Crash effect, now with hitPart argument: "front", "back", "left", "right" self.showCrash = function (hitPart) { // Only set the indicator on the first hit if (!self._firstCrashPart) { self._firstCrashPart = hitPart; } // Hide all indicators frontIndicator.visible = false; backIndicator.visible = false; leftIndicator.visible = false; rightIndicator.visible = false; // Show only the first hit part indicator var showPart = self._firstCrashPart || hitPart; if (showPart === "front") { frontIndicator.visible = true; } else if (showPart === "back") { backIndicator.visible = true; } else if (showPart === "left") { leftIndicator.visible = true; } else if (showPart === "right") { rightIndicator.visible = true; } else { frontIndicator.visible = true; // fallback } var effect = LK.getAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, alpha: 0.8, scaleX: 0.5, scaleY: 0.5 }); self.addChild(effect); tween(effect, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { effect.destroy(); // Hide indicator after effect // Do NOT hide the first hit indicator, keep it visible } }); }; return self; }); // Enemy Car 2 (tougher, for higher levels) var EnemyCar2 = Container.expand(function () { var self = Container.call(this); var car = self.attachAsset('enemyCar2', { anchorX: 0.5, anchorY: 0.5 }); self.width = car.width; self.height = car.height; self.speed = 16; self.type = 2; self.hp = 2; self.crashed = false; // Damage indicator graphics (front, back, left, right) var indicatorLength = car.width * 0.7; var indicatorThickness = 22; var indicatorColor = 0xffeb3b; // Front indicator (top of car) var frontIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -car.height / 2 + indicatorThickness / 2, scaleX: indicatorLength / 220, scaleY: indicatorThickness / 120, alpha: 0.7 }); // Back indicator (bottom of car) var backIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: car.height / 2 - indicatorThickness / 2, scaleX: indicatorLength / 220, scaleY: indicatorThickness / 120, alpha: 0.7 }); // Left indicator (left side of car) var leftIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: -car.width / 2 + indicatorThickness / 2, y: 0, scaleX: indicatorThickness / 120, scaleY: indicatorLength / 220, alpha: 0.7, rotation: Math.PI / 2 }); // Right indicator (right side of car) var rightIndicator = self.attachAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: car.width / 2 - indicatorThickness / 2, y: 0, scaleX: indicatorThickness / 120, scaleY: indicatorLength / 220, alpha: 0.7, rotation: Math.PI / 2 }); // Hide all indicators initially frontIndicator.visible = false; backIndicator.visible = false; leftIndicator.visible = false; rightIndicator.visible = false; self.showCrash = function (hitPart) { // Only set the indicator on the first hit if (!self._firstCrashPart) { self._firstCrashPart = hitPart; } // Hide all indicators frontIndicator.visible = false; backIndicator.visible = false; leftIndicator.visible = false; rightIndicator.visible = false; // Show only the first hit part indicator var showPart = self._firstCrashPart || hitPart; if (showPart === "front") { frontIndicator.visible = true; } else if (showPart === "back") { backIndicator.visible = true; } else if (showPart === "left") { leftIndicator.visible = true; } else if (showPart === "right") { rightIndicator.visible = true; } else { frontIndicator.visible = true; // fallback } var effect = LK.getAsset('crashEffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, alpha: 0.8, scaleX: 0.5, scaleY: 0.5 }); self.addChild(effect); tween(effect, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { effect.destroy(); // Do NOT hide the first hit indicator, keep it visible } }); }; return self; }); // Player Car Class var PlayerCar = Container.expand(function () { var self = Container.call(this); var car = self.attachAsset('playerCar', { anchorX: 0.5, anchorY: 0.5 }); self.width = car.width; self.height = car.height; self.hp = 1; // Upgradable self.invincible = false; self.invincibleTimer = 0; self.upgradeLevel = 0; // Flash effect for invincibility self.flashInvincible = function () { self.invincible = true; self.invincibleTimer = 60; // 1 second at 60fps tween(self, { alpha: 0.5 }, { duration: 100, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { alpha: 1 }, { duration: 100, easing: tween.easeInOut }); } }); }; // Upgrade effect self.showUpgrade = function () { var effect = LK.getAsset('upgradeEffect', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, alpha: 0.7, scaleX: 0.5, scaleY: 0.5 }); self.addChild(effect); tween(effect, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { effect.destroy(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // Arena scroll speed (background illusion) // Player car: blue box // Enemy car: red box // Enemy car 2: green box (for future levels) // Crash effect: yellow ellipse // Upgrade effect: purple ellipse // Sound: crash // Sound: upgrade // Music: background // --- Desert background, cactuses, and dust cloud setup --- // Add a desert background image (fills the arena) // new visual for pit/hole // Stack two desert backgrounds vertically for a longer scrolling road var desertBg1 = LK.getAsset('desertBg', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: 2732 }); var desertBg2 = LK.getAsset('desertBg', { anchorX: 0, anchorY: 0, x: 0, y: -2732, width: 2048, height: 2732 }); game.addChild(desertBg1); game.addChild(desertBg2); // Add more cactuses, only along the left and right edges (not top/bottom or middle) var cactusPerSide = 8; for (var i = 0; i < cactusPerSide; i++) { // Left edge var cactusL = LK.getAsset('cactus', { anchorX: 0.5, anchorY: 1, scaleX: 0.8 + Math.random() * 0.4, scaleY: 0.8 + Math.random() * 0.4 }); cactusL.x = 100 + cactusL.width / 2; // Avoid top/bottom 100px, and avoid the very center (leave a gap in the middle) var minY = 200; var maxY = 2732 - 200; var gapStart = 2732 / 2 - 350; var gapEnd = 2732 / 2 + 350; var yVal = minY + Math.random() * (maxY - minY); // If in the center gap, push up or down if (yVal > gapStart && yVal < gapEnd) { if (Math.random() < 0.5) { yVal = minY + Math.random() * (gapStart - minY); } else { yVal = gapEnd + Math.random() * (maxY - gapEnd); } } cactusL.y = yVal; game.addChild(cactusL); // Right edge var cactusR = LK.getAsset('cactus', { anchorX: 0.5, anchorY: 1, scaleX: 0.8 + Math.random() * 0.4, scaleY: 0.8 + Math.random() * 0.4 }); cactusR.x = 2048 - 100 - cactusR.width / 2; // Use same y logic as left var yValR = minY + Math.random() * (maxY - minY); if (yValR > gapStart && yValR < gapEnd) { if (Math.random() < 0.5) { yValR = minY + Math.random() * (gapStart - minY); } else { yValR = gapEnd + Math.random() * (maxY - gapEnd); } } cactusR.y = yValR; game.addChild(cactusR); } // Dust clouds spawn from all screen edges and move in random directions var dustClouds = []; var dustCloudCount = 8; for (var i = 0; i < dustCloudCount; i++) { // Randomly pick a spawn edge: 0=top, 1=bottom, 2=left, 3=right var edge = Math.floor(Math.random() * 4); var x, y, dx, dy; var margin = 100; var speed = 2 + Math.random() * 2; if (edge === 0) { // top x = margin + Math.random() * (2048 - 2 * margin); y = -80; dx = (Math.random() - 0.5) * 2; dy = speed; } else if (edge === 1) { // bottom x = margin + Math.random() * (2048 - 2 * margin); y = 2732 + 80; dx = (Math.random() - 0.5) * 2; dy = -speed; } else if (edge === 2) { // left x = -80; y = margin + Math.random() * (2732 - 2 * margin); dx = speed; dy = (Math.random() - 0.5) * 2; } else { // right x = 2048 + 80; y = margin + Math.random() * (2732 - 2 * margin); dx = -speed; dy = (Math.random() - 0.5) * 2; } var dustCloud = LK.getAsset('dustCloud', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 2.2, scaleY: 1.2, alpha: 0.5 }); dustCloud._dx = dx; dustCloud._dy = dy; dustCloud._spawnEdge = edge; game.addChild(dustCloud); dustClouds.push(dustCloud); } var scrollSpeed = 10; // Player var player = new PlayerCar(); player.x = 2048 / 2; player.y = 2732 - 500; game.addChild(player); // --- Heart (life) system --- var maxHearts = 3; var hearts = 0; // Start with 0 var heartIcons = []; // Create all heart icons at once, hide them initially for (var i = 0; i < maxHearts; i++) { var heart = LK.getAsset('heart', { x: 60 + i * 80, y: 60, anchorX: 0.5, anchorY: 0.5, scaleX: 0.25, scaleY: 0.25 }); heart.visible = false; game.addChild(heart); heartIcons.push(heart); } function updateHeartsDisplay() { for (var i = 0; i < maxHearts; i++) { heartIcons[i].visible = i < hearts; } } updateHeartsDisplay(); // Score var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Level var level = 1; var levelTxt = new Text2('Lv.1', { size: 70, fill: "#fff" }); levelTxt.anchor.set(0.5, 0); LK.gui.top.addChild(levelTxt); levelTxt.y = 120; // Upgrades var upgradeTxt = new Text2('', { size: 70, fill: 0xFFD600 }); upgradeTxt.anchor.set(0.5, 0); LK.gui.top.addChild(upgradeTxt); upgradeTxt.y = 200; // Enemies var enemies = []; var enemySpawnTimer = 0; var enemySpawnInterval = 60; // frames // Dragging var dragNode = null; // Invincibility after crash var invincibleFrames = 0; // Arena bounds (leave 100px at top for menu) var arenaLeft = 100; var arenaRight = 2048 - 100; var arenaTop = 100; var arenaBottom = 2732 - 100; // Music LK.playMusic('arenaMusic'); // Helper: spawn enemy function spawnEnemy() { var enemy; if (level < 3 || Math.random() < 0.7) { enemy = new EnemyCar(); } else { enemy = new EnemyCar2(); } // Spawn from center and off-center lanes (not far left/right) // Thorns and pits will not slide to the far left and right of the screen var laneCenters = [arenaLeft + (arenaRight - arenaLeft) * 0.28, // left off-center arenaLeft + (arenaRight - arenaLeft) * 0.38, // left of center (arenaLeft + arenaRight) / 2, // center arenaLeft + (arenaRight - arenaLeft) * 0.62, // right of center arenaLeft + (arenaRight - arenaLeft) * 0.72 // right off-center ]; var lane = Math.floor(Math.random() * laneCenters.length); enemy.x = laneCenters[lane]; enemy.y = -enemy.height / 2; // Speed up with level if (enemy.type === 1) { enemy.speed = 12 + level * 1.5; } else { enemy.speed = 16 + level * 1.2; } enemies.push(enemy); game.addChild(enemy); } // Helper: upgrade player function upgradePlayer() { player.upgradeLevel += 1; player.hp += 1; player.showUpgrade(); upgradeTxt.setText('Upgrade! HP: ' + player.hp); LK.getSound('upgrade').play(); LK.setTimeout(function () { upgradeTxt.setText(''); }, 1200); } // Handle move (dragging player) function handleMove(x, y, obj) { if (dragNode === player) { // Clamp to arena var px = x; var py = y; var halfW = player.width / 2; var halfH = player.height / 2; if (px < arenaLeft + halfW) px = arenaLeft + halfW; if (px > arenaRight - halfW) px = arenaRight - halfW; if (py < arenaTop + halfH) py = arenaTop + halfH; if (py > arenaBottom - halfH) py = arenaBottom - halfH; player.x = px; player.y = py; } } // Touch/drag events game.down = function (x, y, obj) { // Only start drag if touch is on player var dx = x - player.x; var dy = y - player.y; if (dx * dx + dy * dy < player.width / 2 * (player.width / 2)) { dragNode = player; handleMove(x, y, obj); } }; game.move = function (x, y, obj) { handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; // Main update loop game.update = function () { // Scroll both desert backgrounds for a seamless, infinite road effect desertBg1.y += scrollSpeed; desertBg2.y += scrollSpeed; // If a background moves off the bottom, move it to the top of the other if (desertBg1.y >= 2732) { desertBg1.y = desertBg2.y - 2732; } if (desertBg2.y >= 2732) { desertBg2.y = desertBg1.y - 2732; } // Animate dust clouds for movement illusion (from all edges) if (dustClouds && dustClouds.length) { for (var d = 0; d < dustClouds.length; d++) { var dc = dustClouds[d]; dc.x += dc._dx; dc.y += dc._dy; // If cloud is out of bounds, respawn from a random edge var outOfBounds = dc.x < -200 || dc.x > 2048 + 200 || dc.y < -200 || dc.y > 2732 + 200; if (outOfBounds) { // Pick a new random edge and direction var edge = Math.floor(Math.random() * 4); var margin = 100; var speed = 2 + Math.random() * 2; if (edge === 0) { // top dc.x = margin + Math.random() * (2048 - 2 * margin); dc.y = -80; dc._dx = (Math.random() - 0.5) * 2; dc._dy = speed; } else if (edge === 1) { // bottom dc.x = margin + Math.random() * (2048 - 2 * margin); dc.y = 2732 + 80; dc._dx = (Math.random() - 0.5) * 2; dc._dy = -speed; } else if (edge === 2) { // left dc.x = -80; dc.y = margin + Math.random() * (2732 - 2 * margin); dc._dx = speed; dc._dy = (Math.random() - 0.5) * 2; } else { // right dc.x = 2048 + 80; dc.y = margin + Math.random() * (2732 - 2 * margin); dc._dx = -speed; dc._dy = (Math.random() - 0.5) * 2; } } } } // --- Pits: spawn, animate, and collision --- // Pit setup: global arrays and spawn timer if (typeof pits === "undefined") { pits = []; pitSpawnTimer = 0; pitSpawnInterval = 90; // frames, can be tuned // Pit lanes: center and off-center, not far left/right pitLanes = [arenaLeft + (arenaRight - arenaLeft) * 0.28, arenaLeft + (arenaRight - arenaLeft) * 0.38, (arenaLeft + arenaRight) / 2, arenaLeft + (arenaRight - arenaLeft) * 0.62, arenaLeft + (arenaRight - arenaLeft) * 0.72]; } // Animate pits for (var p = pits.length - 1; p >= 0; p--) { var pit = pits[p]; if (pit.lastY === undefined) pit.lastY = pit.y; pit.y += scrollSpeed + 14 + level * 0.7; // pits move slightly faster than scroll // Remove pit if off screen if (pit.lastY <= 2732 + 200 && pit.y > 2732 + 200) { pit.destroy(); pits.splice(p, 1); continue; } pit.lastY = pit.y; } // Spawn pits pitSpawnTimer++; var pitInt = Math.max(50, pitSpawnInterval - level * 2); if (pitSpawnTimer >= pitInt) { // Only spawn in center and off-center lanes, not far left/right var lane = Math.floor(Math.random() * pitLanes.length); var pit = LK.getAsset('pitHole', { anchorX: 0.5, anchorY: 0.5, x: pitLanes[lane], y: -60, scaleX: 1.2 + Math.random() * 0.3, scaleY: 0.7 + Math.random() * 0.2, alpha: 0.95 }); pit.width = 180 + Math.random() * 80; pit.height = 80 + Math.random() * 30; pits.push(pit); game.addChild(pit); pit.lastY = pit.y; pit.lastWasIntersecting = false; pit.isPit = true; pit.isDeadly = true; pit._type = "pit"; pit._alreadyHit = false; pit._id = "pit_" + Date.now() + "_" + Math.floor(Math.random() * 10000); pitSpawnTimer = 0; } // Pit collision with player (kill instantly) for (var p = pits.length - 1; p >= 0; p--) { var pit = pits[p]; if (pit._alreadyHit) continue; var isIntersecting = player.intersects(pit); if (pit.lastWasIntersecting === false && isIntersecting) { pit._alreadyHit = true; if (hearts > 0 && !player.invincible) { // Use a heart, grant invincibility, do not die hearts--; if (hearts >= 0 && hearts < heartIcons.length) { heartIcons[hearts].visible = false; } updateHeartsDisplay(); player.flashInvincible(); LK.effects.flashScreen(0x000000, 600); } else { LK.effects.flashScreen(0x000000, 1000); LK.showGameOver(); return; } } pit.lastWasIntersecting = isIntersecting; } // Arena scroll illusion: move all enemies down for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; enemy.y += enemy.speed + scrollSpeed; // End game if enemy reaches the end of the screen (bottom) if (enemy.lastY === undefined) { enemy.lastY = enemy.y; } var screenEndY = 2732 + 100; if (enemy.lastY <= screenEndY && enemy.y > screenEndY) { // Lose a heart if enemy reaches the end hearts--; if (hearts >= 0 && hearts < heartIcons.length) { heartIcons[hearts].visible = false; } updateHeartsDisplay(); LK.effects.flashScreen(0xff0000, 1000); // Remove the enemy from the game enemy.destroy(); enemies.splice(i, 1); // Game over if no hearts left if (hearts <= 0) { LK.showGameOver(); return; } // Otherwise, continue the game continue; } enemy.lastY = enemy.y; } // Spawn enemies enemySpawnTimer++; var spawnInt = Math.max(30, enemySpawnInterval - level * 2); if (enemySpawnTimer >= spawnInt) { spawnEnemy(); enemySpawnTimer = 0; } // Collision: player vs enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.crashed) continue; if (player.invincible) continue; if (player.intersects(enemy)) { // Determine which side of the enemy was hit by the player var dx = player.x - enemy.x; var dy = player.y - enemy.y; var absDx = Math.abs(dx); var absDy = Math.abs(dy); var hitPart = "front"; if (absDx > absDy) { // Side hit if (dx < 0) { hitPart = "left"; } else { hitPart = "right"; } } else { // Vertical hit if (dy < 0) { hitPart = "front"; } else { hitPart = "back"; } } // Only allow crash and destroy if hitPart is "back" if (hitPart === "back") { // Crash! // For EnemyCar2, require 2 hits to destroy if (enemy.type === 2) { if (typeof enemy._hitCount === "undefined") enemy._hitCount = 0; enemy._hitCount++; enemy.showCrash(hitPart); LK.getSound('crash').play(); // Only destroy and score after 2 hits if (enemy._hitCount >= 2) { enemy.crashed = true; score += 10; LK.setScore(score); scoreTxt.setText(score); // Remove enemy after effect LK.setTimeout(function (e, idx) { return function () { if (enemies[idx] === e) { e.destroy(); enemies.splice(idx, 1); } }; }(enemy, i), 400); } // Player does not take damage, only enemy is destroyed } else { enemy.crashed = true; enemy.showCrash(hitPart); LK.getSound('crash').play(); score += 10; LK.setScore(score); scoreTxt.setText(score); // Remove enemy after effect LK.setTimeout(function (e, idx) { return function () { if (enemies[idx] === e) { e.destroy(); enemies.splice(idx, 1); } }; }(enemy, i), 400); // Player does not take damage, only enemy is destroyed } } } } // --- Blur effect when player collides with dust cloud --- if (typeof game._blurActive === "undefined") { game._blurActive = false; game._blurLastIntersecting = false; } // Check intersection with any dust cloud var dustCloudIntersecting = false; if (dustClouds && dustClouds.length) { for (var d = 0; d < dustClouds.length; d++) { if (player.intersects(dustClouds[d])) { dustCloudIntersecting = true; break; } } } if (!game._blurLastIntersecting && dustCloudIntersecting && !game._blurActive) { game._blurActive = true; // Darken the screen more when colliding with a dust cloud LK.effects.flashScreen(0x000000, 1200, { alpha: 0.55 }); // More darkness, longer duration // Optionally, you can still use blur if you want both effects: // if (LK.effects && typeof LK.effects.blurScreen === "function") { // LK.effects.blurScreen(8, 2000); // } LK.setTimeout(function () { game._blurActive = false; }, 1200); } game._blurLastIntersecting = dustCloudIntersecting; // Invincibility timer if (player.invincible) { player.invincibleTimer--; if (player.invincibleTimer <= 0) { player.invincible = false; player.alpha = 1; } } // Level up every 100 points var newLevel = Math.floor(score / 100) + 1; if (newLevel > level) { level = newLevel; levelTxt.setText('Lv.' + level); // Every 2 levels, upgrade player if (level % 2 === 0) { upgradePlayer(); } // Every 5 levels, gain a heart (up to 3), starting from 0 var newHearts = Math.min(Math.floor(level / 5), maxHearts); if (newHearts > hearts) { hearts = newHearts; updateHeartsDisplay(); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Enemy Car Class (basic)
var EnemyCar = Container.expand(function () {
var self = Container.call(this);
var car = self.attachAsset('enemyCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = car.width;
self.height = car.height;
self.speed = 12; // Will increase with level
self.type = 1;
self.hp = 1;
self.crashed = false;
// Damage indicator graphics (front, back, left, right)
var indicatorLength = car.width * 0.7;
var indicatorThickness = 22;
var indicatorColor = 0xffeb3b;
// Front indicator (top of car)
var frontIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -car.height / 2 + indicatorThickness / 2,
scaleX: indicatorLength / 220,
scaleY: indicatorThickness / 120,
alpha: 0.7
});
// Back indicator (bottom of car)
var backIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: car.height / 2 - indicatorThickness / 2,
scaleX: indicatorLength / 220,
scaleY: indicatorThickness / 120,
alpha: 0.7
});
// Left indicator (left side of car)
var leftIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: -car.width / 2 + indicatorThickness / 2,
y: 0,
scaleX: indicatorThickness / 120,
scaleY: indicatorLength / 220,
alpha: 0.7,
rotation: Math.PI / 2
});
// Right indicator (right side of car)
var rightIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: car.width / 2 - indicatorThickness / 2,
y: 0,
scaleX: indicatorThickness / 120,
scaleY: indicatorLength / 220,
alpha: 0.7,
rotation: Math.PI / 2
});
// Hide all indicators initially
frontIndicator.visible = false;
backIndicator.visible = false;
leftIndicator.visible = false;
rightIndicator.visible = false;
// Crash effect, now with hitPart argument: "front", "back", "left", "right"
self.showCrash = function (hitPart) {
// Only set the indicator on the first hit
if (!self._firstCrashPart) {
self._firstCrashPart = hitPart;
}
// Hide all indicators
frontIndicator.visible = false;
backIndicator.visible = false;
leftIndicator.visible = false;
rightIndicator.visible = false;
// Show only the first hit part indicator
var showPart = self._firstCrashPart || hitPart;
if (showPart === "front") {
frontIndicator.visible = true;
} else if (showPart === "back") {
backIndicator.visible = true;
} else if (showPart === "left") {
leftIndicator.visible = true;
} else if (showPart === "right") {
rightIndicator.visible = true;
} else {
frontIndicator.visible = true; // fallback
}
var effect = LK.getAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0.8,
scaleX: 0.5,
scaleY: 0.5
});
self.addChild(effect);
tween(effect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
effect.destroy();
// Hide indicator after effect
// Do NOT hide the first hit indicator, keep it visible
}
});
};
return self;
});
// Enemy Car 2 (tougher, for higher levels)
var EnemyCar2 = Container.expand(function () {
var self = Container.call(this);
var car = self.attachAsset('enemyCar2', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = car.width;
self.height = car.height;
self.speed = 16;
self.type = 2;
self.hp = 2;
self.crashed = false;
// Damage indicator graphics (front, back, left, right)
var indicatorLength = car.width * 0.7;
var indicatorThickness = 22;
var indicatorColor = 0xffeb3b;
// Front indicator (top of car)
var frontIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -car.height / 2 + indicatorThickness / 2,
scaleX: indicatorLength / 220,
scaleY: indicatorThickness / 120,
alpha: 0.7
});
// Back indicator (bottom of car)
var backIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: car.height / 2 - indicatorThickness / 2,
scaleX: indicatorLength / 220,
scaleY: indicatorThickness / 120,
alpha: 0.7
});
// Left indicator (left side of car)
var leftIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: -car.width / 2 + indicatorThickness / 2,
y: 0,
scaleX: indicatorThickness / 120,
scaleY: indicatorLength / 220,
alpha: 0.7,
rotation: Math.PI / 2
});
// Right indicator (right side of car)
var rightIndicator = self.attachAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: car.width / 2 - indicatorThickness / 2,
y: 0,
scaleX: indicatorThickness / 120,
scaleY: indicatorLength / 220,
alpha: 0.7,
rotation: Math.PI / 2
});
// Hide all indicators initially
frontIndicator.visible = false;
backIndicator.visible = false;
leftIndicator.visible = false;
rightIndicator.visible = false;
self.showCrash = function (hitPart) {
// Only set the indicator on the first hit
if (!self._firstCrashPart) {
self._firstCrashPart = hitPart;
}
// Hide all indicators
frontIndicator.visible = false;
backIndicator.visible = false;
leftIndicator.visible = false;
rightIndicator.visible = false;
// Show only the first hit part indicator
var showPart = self._firstCrashPart || hitPart;
if (showPart === "front") {
frontIndicator.visible = true;
} else if (showPart === "back") {
backIndicator.visible = true;
} else if (showPart === "left") {
leftIndicator.visible = true;
} else if (showPart === "right") {
rightIndicator.visible = true;
} else {
frontIndicator.visible = true; // fallback
}
var effect = LK.getAsset('crashEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0.8,
scaleX: 0.5,
scaleY: 0.5
});
self.addChild(effect);
tween(effect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
effect.destroy();
// Do NOT hide the first hit indicator, keep it visible
}
});
};
return self;
});
// Player Car Class
var PlayerCar = Container.expand(function () {
var self = Container.call(this);
var car = self.attachAsset('playerCar', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = car.width;
self.height = car.height;
self.hp = 1; // Upgradable
self.invincible = false;
self.invincibleTimer = 0;
self.upgradeLevel = 0;
// Flash effect for invincibility
self.flashInvincible = function () {
self.invincible = true;
self.invincibleTimer = 60; // 1 second at 60fps
tween(self, {
alpha: 0.5
}, {
duration: 100,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
alpha: 1
}, {
duration: 100,
easing: tween.easeInOut
});
}
});
};
// Upgrade effect
self.showUpgrade = function () {
var effect = LK.getAsset('upgradeEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
alpha: 0.7,
scaleX: 0.5,
scaleY: 0.5
});
self.addChild(effect);
tween(effect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
effect.destroy();
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Arena scroll speed (background illusion)
// Player car: blue box
// Enemy car: red box
// Enemy car 2: green box (for future levels)
// Crash effect: yellow ellipse
// Upgrade effect: purple ellipse
// Sound: crash
// Sound: upgrade
// Music: background
// --- Desert background, cactuses, and dust cloud setup ---
// Add a desert background image (fills the arena)
// new visual for pit/hole
// Stack two desert backgrounds vertically for a longer scrolling road
var desertBg1 = LK.getAsset('desertBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
var desertBg2 = LK.getAsset('desertBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: -2732,
width: 2048,
height: 2732
});
game.addChild(desertBg1);
game.addChild(desertBg2);
// Add more cactuses, only along the left and right edges (not top/bottom or middle)
var cactusPerSide = 8;
for (var i = 0; i < cactusPerSide; i++) {
// Left edge
var cactusL = LK.getAsset('cactus', {
anchorX: 0.5,
anchorY: 1,
scaleX: 0.8 + Math.random() * 0.4,
scaleY: 0.8 + Math.random() * 0.4
});
cactusL.x = 100 + cactusL.width / 2;
// Avoid top/bottom 100px, and avoid the very center (leave a gap in the middle)
var minY = 200;
var maxY = 2732 - 200;
var gapStart = 2732 / 2 - 350;
var gapEnd = 2732 / 2 + 350;
var yVal = minY + Math.random() * (maxY - minY);
// If in the center gap, push up or down
if (yVal > gapStart && yVal < gapEnd) {
if (Math.random() < 0.5) {
yVal = minY + Math.random() * (gapStart - minY);
} else {
yVal = gapEnd + Math.random() * (maxY - gapEnd);
}
}
cactusL.y = yVal;
game.addChild(cactusL);
// Right edge
var cactusR = LK.getAsset('cactus', {
anchorX: 0.5,
anchorY: 1,
scaleX: 0.8 + Math.random() * 0.4,
scaleY: 0.8 + Math.random() * 0.4
});
cactusR.x = 2048 - 100 - cactusR.width / 2;
// Use same y logic as left
var yValR = minY + Math.random() * (maxY - minY);
if (yValR > gapStart && yValR < gapEnd) {
if (Math.random() < 0.5) {
yValR = minY + Math.random() * (gapStart - minY);
} else {
yValR = gapEnd + Math.random() * (maxY - gapEnd);
}
}
cactusR.y = yValR;
game.addChild(cactusR);
}
// Dust clouds spawn from all screen edges and move in random directions
var dustClouds = [];
var dustCloudCount = 8;
for (var i = 0; i < dustCloudCount; i++) {
// Randomly pick a spawn edge: 0=top, 1=bottom, 2=left, 3=right
var edge = Math.floor(Math.random() * 4);
var x, y, dx, dy;
var margin = 100;
var speed = 2 + Math.random() * 2;
if (edge === 0) {
// top
x = margin + Math.random() * (2048 - 2 * margin);
y = -80;
dx = (Math.random() - 0.5) * 2;
dy = speed;
} else if (edge === 1) {
// bottom
x = margin + Math.random() * (2048 - 2 * margin);
y = 2732 + 80;
dx = (Math.random() - 0.5) * 2;
dy = -speed;
} else if (edge === 2) {
// left
x = -80;
y = margin + Math.random() * (2732 - 2 * margin);
dx = speed;
dy = (Math.random() - 0.5) * 2;
} else {
// right
x = 2048 + 80;
y = margin + Math.random() * (2732 - 2 * margin);
dx = -speed;
dy = (Math.random() - 0.5) * 2;
}
var dustCloud = LK.getAsset('dustCloud', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
scaleX: 2.2,
scaleY: 1.2,
alpha: 0.5
});
dustCloud._dx = dx;
dustCloud._dy = dy;
dustCloud._spawnEdge = edge;
game.addChild(dustCloud);
dustClouds.push(dustCloud);
}
var scrollSpeed = 10;
// Player
var player = new PlayerCar();
player.x = 2048 / 2;
player.y = 2732 - 500;
game.addChild(player);
// --- Heart (life) system ---
var maxHearts = 3;
var hearts = 0; // Start with 0
var heartIcons = [];
// Create all heart icons at once, hide them initially
for (var i = 0; i < maxHearts; i++) {
var heart = LK.getAsset('heart', {
x: 60 + i * 80,
y: 60,
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25,
scaleY: 0.25
});
heart.visible = false;
game.addChild(heart);
heartIcons.push(heart);
}
function updateHeartsDisplay() {
for (var i = 0; i < maxHearts; i++) {
heartIcons[i].visible = i < hearts;
}
}
updateHeartsDisplay();
// Score
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Level
var level = 1;
var levelTxt = new Text2('Lv.1', {
size: 70,
fill: "#fff"
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
levelTxt.y = 120;
// Upgrades
var upgradeTxt = new Text2('', {
size: 70,
fill: 0xFFD600
});
upgradeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(upgradeTxt);
upgradeTxt.y = 200;
// Enemies
var enemies = [];
var enemySpawnTimer = 0;
var enemySpawnInterval = 60; // frames
// Dragging
var dragNode = null;
// Invincibility after crash
var invincibleFrames = 0;
// Arena bounds (leave 100px at top for menu)
var arenaLeft = 100;
var arenaRight = 2048 - 100;
var arenaTop = 100;
var arenaBottom = 2732 - 100;
// Music
LK.playMusic('arenaMusic');
// Helper: spawn enemy
function spawnEnemy() {
var enemy;
if (level < 3 || Math.random() < 0.7) {
enemy = new EnemyCar();
} else {
enemy = new EnemyCar2();
}
// Spawn from center and off-center lanes (not far left/right)
// Thorns and pits will not slide to the far left and right of the screen
var laneCenters = [arenaLeft + (arenaRight - arenaLeft) * 0.28,
// left off-center
arenaLeft + (arenaRight - arenaLeft) * 0.38,
// left of center
(arenaLeft + arenaRight) / 2,
// center
arenaLeft + (arenaRight - arenaLeft) * 0.62,
// right of center
arenaLeft + (arenaRight - arenaLeft) * 0.72 // right off-center
];
var lane = Math.floor(Math.random() * laneCenters.length);
enemy.x = laneCenters[lane];
enemy.y = -enemy.height / 2;
// Speed up with level
if (enemy.type === 1) {
enemy.speed = 12 + level * 1.5;
} else {
enemy.speed = 16 + level * 1.2;
}
enemies.push(enemy);
game.addChild(enemy);
}
// Helper: upgrade player
function upgradePlayer() {
player.upgradeLevel += 1;
player.hp += 1;
player.showUpgrade();
upgradeTxt.setText('Upgrade! HP: ' + player.hp);
LK.getSound('upgrade').play();
LK.setTimeout(function () {
upgradeTxt.setText('');
}, 1200);
}
// Handle move (dragging player)
function handleMove(x, y, obj) {
if (dragNode === player) {
// Clamp to arena
var px = x;
var py = y;
var halfW = player.width / 2;
var halfH = player.height / 2;
if (px < arenaLeft + halfW) px = arenaLeft + halfW;
if (px > arenaRight - halfW) px = arenaRight - halfW;
if (py < arenaTop + halfH) py = arenaTop + halfH;
if (py > arenaBottom - halfH) py = arenaBottom - halfH;
player.x = px;
player.y = py;
}
}
// Touch/drag events
game.down = function (x, y, obj) {
// Only start drag if touch is on player
var dx = x - player.x;
var dy = y - player.y;
if (dx * dx + dy * dy < player.width / 2 * (player.width / 2)) {
dragNode = player;
handleMove(x, y, obj);
}
};
game.move = function (x, y, obj) {
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// Main update loop
game.update = function () {
// Scroll both desert backgrounds for a seamless, infinite road effect
desertBg1.y += scrollSpeed;
desertBg2.y += scrollSpeed;
// If a background moves off the bottom, move it to the top of the other
if (desertBg1.y >= 2732) {
desertBg1.y = desertBg2.y - 2732;
}
if (desertBg2.y >= 2732) {
desertBg2.y = desertBg1.y - 2732;
}
// Animate dust clouds for movement illusion (from all edges)
if (dustClouds && dustClouds.length) {
for (var d = 0; d < dustClouds.length; d++) {
var dc = dustClouds[d];
dc.x += dc._dx;
dc.y += dc._dy;
// If cloud is out of bounds, respawn from a random edge
var outOfBounds = dc.x < -200 || dc.x > 2048 + 200 || dc.y < -200 || dc.y > 2732 + 200;
if (outOfBounds) {
// Pick a new random edge and direction
var edge = Math.floor(Math.random() * 4);
var margin = 100;
var speed = 2 + Math.random() * 2;
if (edge === 0) {
// top
dc.x = margin + Math.random() * (2048 - 2 * margin);
dc.y = -80;
dc._dx = (Math.random() - 0.5) * 2;
dc._dy = speed;
} else if (edge === 1) {
// bottom
dc.x = margin + Math.random() * (2048 - 2 * margin);
dc.y = 2732 + 80;
dc._dx = (Math.random() - 0.5) * 2;
dc._dy = -speed;
} else if (edge === 2) {
// left
dc.x = -80;
dc.y = margin + Math.random() * (2732 - 2 * margin);
dc._dx = speed;
dc._dy = (Math.random() - 0.5) * 2;
} else {
// right
dc.x = 2048 + 80;
dc.y = margin + Math.random() * (2732 - 2 * margin);
dc._dx = -speed;
dc._dy = (Math.random() - 0.5) * 2;
}
}
}
}
// --- Pits: spawn, animate, and collision ---
// Pit setup: global arrays and spawn timer
if (typeof pits === "undefined") {
pits = [];
pitSpawnTimer = 0;
pitSpawnInterval = 90; // frames, can be tuned
// Pit lanes: center and off-center, not far left/right
pitLanes = [arenaLeft + (arenaRight - arenaLeft) * 0.28, arenaLeft + (arenaRight - arenaLeft) * 0.38, (arenaLeft + arenaRight) / 2, arenaLeft + (arenaRight - arenaLeft) * 0.62, arenaLeft + (arenaRight - arenaLeft) * 0.72];
}
// Animate pits
for (var p = pits.length - 1; p >= 0; p--) {
var pit = pits[p];
if (pit.lastY === undefined) pit.lastY = pit.y;
pit.y += scrollSpeed + 14 + level * 0.7; // pits move slightly faster than scroll
// Remove pit if off screen
if (pit.lastY <= 2732 + 200 && pit.y > 2732 + 200) {
pit.destroy();
pits.splice(p, 1);
continue;
}
pit.lastY = pit.y;
}
// Spawn pits
pitSpawnTimer++;
var pitInt = Math.max(50, pitSpawnInterval - level * 2);
if (pitSpawnTimer >= pitInt) {
// Only spawn in center and off-center lanes, not far left/right
var lane = Math.floor(Math.random() * pitLanes.length);
var pit = LK.getAsset('pitHole', {
anchorX: 0.5,
anchorY: 0.5,
x: pitLanes[lane],
y: -60,
scaleX: 1.2 + Math.random() * 0.3,
scaleY: 0.7 + Math.random() * 0.2,
alpha: 0.95
});
pit.width = 180 + Math.random() * 80;
pit.height = 80 + Math.random() * 30;
pits.push(pit);
game.addChild(pit);
pit.lastY = pit.y;
pit.lastWasIntersecting = false;
pit.isPit = true;
pit.isDeadly = true;
pit._type = "pit";
pit._alreadyHit = false;
pit._id = "pit_" + Date.now() + "_" + Math.floor(Math.random() * 10000);
pitSpawnTimer = 0;
}
// Pit collision with player (kill instantly)
for (var p = pits.length - 1; p >= 0; p--) {
var pit = pits[p];
if (pit._alreadyHit) continue;
var isIntersecting = player.intersects(pit);
if (pit.lastWasIntersecting === false && isIntersecting) {
pit._alreadyHit = true;
if (hearts > 0 && !player.invincible) {
// Use a heart, grant invincibility, do not die
hearts--;
if (hearts >= 0 && hearts < heartIcons.length) {
heartIcons[hearts].visible = false;
}
updateHeartsDisplay();
player.flashInvincible();
LK.effects.flashScreen(0x000000, 600);
} else {
LK.effects.flashScreen(0x000000, 1000);
LK.showGameOver();
return;
}
}
pit.lastWasIntersecting = isIntersecting;
}
// Arena scroll illusion: move all enemies down
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
enemy.y += enemy.speed + scrollSpeed;
// End game if enemy reaches the end of the screen (bottom)
if (enemy.lastY === undefined) {
enemy.lastY = enemy.y;
}
var screenEndY = 2732 + 100;
if (enemy.lastY <= screenEndY && enemy.y > screenEndY) {
// Lose a heart if enemy reaches the end
hearts--;
if (hearts >= 0 && hearts < heartIcons.length) {
heartIcons[hearts].visible = false;
}
updateHeartsDisplay();
LK.effects.flashScreen(0xff0000, 1000);
// Remove the enemy from the game
enemy.destroy();
enemies.splice(i, 1);
// Game over if no hearts left
if (hearts <= 0) {
LK.showGameOver();
return;
}
// Otherwise, continue the game
continue;
}
enemy.lastY = enemy.y;
}
// Spawn enemies
enemySpawnTimer++;
var spawnInt = Math.max(30, enemySpawnInterval - level * 2);
if (enemySpawnTimer >= spawnInt) {
spawnEnemy();
enemySpawnTimer = 0;
}
// Collision: player vs enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.crashed) continue;
if (player.invincible) continue;
if (player.intersects(enemy)) {
// Determine which side of the enemy was hit by the player
var dx = player.x - enemy.x;
var dy = player.y - enemy.y;
var absDx = Math.abs(dx);
var absDy = Math.abs(dy);
var hitPart = "front";
if (absDx > absDy) {
// Side hit
if (dx < 0) {
hitPart = "left";
} else {
hitPart = "right";
}
} else {
// Vertical hit
if (dy < 0) {
hitPart = "front";
} else {
hitPart = "back";
}
}
// Only allow crash and destroy if hitPart is "back"
if (hitPart === "back") {
// Crash!
// For EnemyCar2, require 2 hits to destroy
if (enemy.type === 2) {
if (typeof enemy._hitCount === "undefined") enemy._hitCount = 0;
enemy._hitCount++;
enemy.showCrash(hitPart);
LK.getSound('crash').play();
// Only destroy and score after 2 hits
if (enemy._hitCount >= 2) {
enemy.crashed = true;
score += 10;
LK.setScore(score);
scoreTxt.setText(score);
// Remove enemy after effect
LK.setTimeout(function (e, idx) {
return function () {
if (enemies[idx] === e) {
e.destroy();
enemies.splice(idx, 1);
}
};
}(enemy, i), 400);
}
// Player does not take damage, only enemy is destroyed
} else {
enemy.crashed = true;
enemy.showCrash(hitPart);
LK.getSound('crash').play();
score += 10;
LK.setScore(score);
scoreTxt.setText(score);
// Remove enemy after effect
LK.setTimeout(function (e, idx) {
return function () {
if (enemies[idx] === e) {
e.destroy();
enemies.splice(idx, 1);
}
};
}(enemy, i), 400);
// Player does not take damage, only enemy is destroyed
}
}
}
}
// --- Blur effect when player collides with dust cloud ---
if (typeof game._blurActive === "undefined") {
game._blurActive = false;
game._blurLastIntersecting = false;
}
// Check intersection with any dust cloud
var dustCloudIntersecting = false;
if (dustClouds && dustClouds.length) {
for (var d = 0; d < dustClouds.length; d++) {
if (player.intersects(dustClouds[d])) {
dustCloudIntersecting = true;
break;
}
}
}
if (!game._blurLastIntersecting && dustCloudIntersecting && !game._blurActive) {
game._blurActive = true;
// Darken the screen more when colliding with a dust cloud
LK.effects.flashScreen(0x000000, 1200, {
alpha: 0.55
}); // More darkness, longer duration
// Optionally, you can still use blur if you want both effects:
// if (LK.effects && typeof LK.effects.blurScreen === "function") {
// LK.effects.blurScreen(8, 2000);
// }
LK.setTimeout(function () {
game._blurActive = false;
}, 1200);
}
game._blurLastIntersecting = dustCloudIntersecting;
// Invincibility timer
if (player.invincible) {
player.invincibleTimer--;
if (player.invincibleTimer <= 0) {
player.invincible = false;
player.alpha = 1;
}
}
// Level up every 100 points
var newLevel = Math.floor(score / 100) + 1;
if (newLevel > level) {
level = newLevel;
levelTxt.setText('Lv.' + level);
// Every 2 levels, upgrade player
if (level % 2 === 0) {
upgradePlayer();
}
// Every 5 levels, gain a heart (up to 3), starting from 0
var newHearts = Math.min(Math.floor(level / 5), maxHearts);
if (newHearts > hearts) {
hearts = newHearts;
updateHeartsDisplay();
}
}
};
A Mad Max style car will be old and will have a metal spiked plate on the front and will have a top view of the car
It will be in the style of a Mad Max truck and will have a top view. There will be a thorn plate in the front and the back will be smoother.
cactus 2d. In-Game asset. 2d. High contrast. No shadows
The asphalt road will be a little wider
dust cloud. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
It will be a small car in the style of Madmax. It will have a top view. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
middle lava
The hearts will be in a slightly old Mad Max style look and half will be inside the leather case. In-Game asset. 2d. High contrast. No shadows
scatter light. In-Game asset. 2d. High contrast. No shadows