User prompt
add needed sound effects and nonstop background music matches with the theme
User prompt
random obstacles extending from the left and right of the screen, but not overlapping with standard obstacles ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make it background more realistic and give a motion toskeleton obstacle and fire pit obstacle ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
remove escape power up. every time on left middle and right, one of them should be without obstacle but in every 250 points game should be harder obstacles can be more
User prompt
game should be faster in every 100 point
User prompt
it cannot be 3 obstacles at the same time in the way
User prompt
there should be a escape all the time
User prompt
Please fix the bug: 'TypeError: undefined is not an object (evaluating 'streamGraphics.skew.y = flow * 0.01')' in or related to this line: 'streamGraphics.skew.y = flow * 0.01;' Line Number: 135
User prompt
make background black lavas
User prompt
road should be last vertical as well and the road must flow with curves ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make it vertical. there should be road and walls. besides of obstacles there should be coins and special power-ups, coins for more score, power ups as being invisible, getting slower and making obstacles smaller ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
add bonus system
Code edit (1 edits merged)
Please save this source code
User prompt
Hell Escape Racer
Initial prompt
Create a simple 2D endless runner-style mobile game with the theme “Escape from Hell”. Core Features: - The player controls a car trying to escape Hell. - The car moves forward automatically. - The player can swipe or tap to move the car vertically (up/down) between lanes. - The environment should scroll continuously to simulate speed and escape. - Random obstacles appear ahead: demons, pits of fire, lava rocks, etc. Crashing ends the game. - Bonus items (like glowing fire orbs) can be collected to increase score. - A simple score counter should be visible during gameplay. - Graphics can be very simple (e.g., solid colors, minimal shapes). - The game should be lightweight, mobile-friendly, and written as simply as possible. Optional Enhancements (choose any): - Power-ups: Add items that give temporary invincibility or boost. - Absurd hell obstacles: Screaming skulls, hellish traffic lights, flying demon wings. - Dynamic difficulty: Speed or obstacle frequency increases over time. - Visual twist: Background color changes slowly (red → purple → black) as the game progresses. - Random events: Occasionally flash "The Devil is Near!" and shake the screen. - Parallax layers: Add a second or third scrolling background for atmosphere. - Sound (optional): Low growls, fire crackling, heartbeat when low on score. Avoid unnecessary complexity. Keep the code readable and beginner-friendly. Mobile-ready is a must.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Car = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.targetLane = 1; // 0 = left, 1 = middle, 2 = right
self.currentLane = 1;
self.invincible = false;
self.invincibilityTimer = 0;
self.slowEffect = false;
self.slowTimer = 0;
self.shrinkObstacles = false;
self.shrinkTimer = 0;
self.update = function () {
// Smooth lane transitions
var targetX = getLaneX(self.targetLane);
if (Math.abs(self.x - targetX) > 5) {
self.x += (targetX - self.x) * 0.2;
} else {
self.x = targetX;
self.currentLane = self.targetLane;
}
// Handle invincibility
if (self.invincible) {
self.invincibilityTimer--;
carGraphics.alpha = self.invincibilityTimer % 10 < 5 ? 0.3 : 1.0;
carGraphics.tint = 0x00ffff;
if (self.invincibilityTimer <= 0) {
self.invincible = false;
carGraphics.alpha = 1.0;
carGraphics.tint = 0xffffff;
}
}
// Handle slow effect
if (self.slowEffect) {
self.slowTimer--;
if (self.slowTimer <= 0) {
self.slowEffect = false;
}
}
// Handle shrink obstacles effect
if (self.shrinkObstacles) {
self.shrinkTimer--;
if (self.shrinkTimer <= 0) {
self.shrinkObstacles = false;
}
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.glowTimer = 0;
self.update = function () {
self.y += gameSpeed;
// Glowing effect
self.glowTimer++;
var scale = 1.0 + Math.sin(self.glowTimer * 0.2) * 0.2;
coinGraphics.scaleX = scale;
coinGraphics.scaleY = scale;
// Rotation effect
coinGraphics.rotation += 0.1;
};
return self;
});
var LavaBackground = Container.expand(function () {
var self = Container.call(this);
var lavaGraphics = self.attachAsset('lavaBackground', {
anchorX: 0.5,
anchorY: 0.5
});
self.pulseTimer = Math.random() * 100;
self.update = function () {
self.y += gameSpeed * 0.3;
// Enhanced pulsing dark lava effect with multiple layers
self.pulseTimer++;
var pulse = 1.0 + Math.sin(self.pulseTimer * 0.05) * 0.15;
var secondaryPulse = 1.0 + Math.sin(self.pulseTimer * 0.08) * 0.08;
lavaGraphics.scaleY = pulse * secondaryPulse;
lavaGraphics.scaleX = 1.0 + Math.sin(self.pulseTimer * 0.04) * 0.05;
// Enhanced color shifting for more realistic lava
var colorShift = Math.sin(self.pulseTimer * 0.03) * 0.3;
var redIntensity = Math.sin(self.pulseTimer * 0.02) * 0.2;
var baseColor = 0x1a0000;
var redComponent = Math.floor((0.1 + colorShift + redIntensity) * 255);
var greenComponent = Math.floor(Math.sin(self.pulseTimer * 0.025) * 0.1 * 255);
lavaGraphics.tint = (redComponent << 16) + (greenComponent << 8);
// Slight rotation for flowing effect
lavaGraphics.rotation = Math.sin(self.pulseTimer * 0.01) * 0.02;
// Reset position when off screen
if (self.y > 2732 + 150) {
self.y = -150;
}
};
return self;
});
var LavaStream = Container.expand(function () {
var self = Container.call(this);
var streamGraphics = self.attachAsset('lavaStream', {
anchorX: 0.5,
anchorY: 0.5
});
self.flowTimer = Math.random() * 200;
self.update = function () {
self.y += gameSpeed * 0.5;
// Enhanced flowing lava effect with realistic motion
self.flowTimer++;
var flow = Math.sin(self.flowTimer * 0.08) * 0.08;
var crossFlow = Math.sin(self.flowTimer * 0.06) * 0.03;
streamGraphics.rotation = flow;
streamGraphics.skewX = crossFlow;
// Multi-layered intensity variation
var intensity = 1.0 + Math.sin(self.flowTimer * 0.06) * 0.4;
var secondaryIntensity = 1.0 + Math.sin(self.flowTimer * 0.09) * 0.2;
streamGraphics.alpha = 0.3 + intensity * secondaryIntensity * 0.25;
// Scale variation for bubbling effect
var scaleVar = 1.0 + Math.sin(self.flowTimer * 0.07) * 0.1;
streamGraphics.scaleX = scaleVar;
// Color variation for hot lava
var heatShift = Math.sin(self.flowTimer * 0.05) * 0.3;
var redHeat = Math.floor((0.2 + heatShift) * 255);
streamGraphics.tint = (redHeat << 16) + (Math.floor(heatShift * 0.5 * 255) << 8);
// Reset position when off screen
if (self.y > 2732 + 200) {
self.y = -200;
}
};
return self;
});
var Obstacle = Container.expand(function (type) {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type;
self.shrunk = false;
self.update = function () {
self.y += car.slowEffect ? gameSpeed * 0.5 : gameSpeed;
// Apply shrink effect if active
if (car.shrinkObstacles && !self.shrunk) {
self.shrunk = true;
tween(obstacleGraphics, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300
});
} else if (!car.shrinkObstacles && self.shrunk) {
self.shrunk = false;
tween(obstacleGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 300
});
}
// Animated effects for different obstacles
if (self.type === 'demon') {
obstacleGraphics.rotation += 0.05;
} else if (self.type === 'demonWing') {
var flap = Math.sin(LK.ticks * 0.3) * 0.3;
obstacleGraphics.scaleY = (self.shrunk ? 0.5 : 1.0) + flap;
} else if (self.type === 'firePit') {
var flicker = 1.0 + Math.sin(LK.ticks * 0.4) * 0.15;
var scale = self.shrunk ? 0.5 : 1.0;
obstacleGraphics.scaleX = scale * flicker;
obstacleGraphics.scaleY = scale * flicker;
// Add intense flickering effect with tween
if (LK.ticks % 120 === 0) {
tween(obstacleGraphics, {
alpha: 0.6
}, {
duration: 150,
onFinish: function onFinish() {
tween(obstacleGraphics, {
alpha: 1.0
}, {
duration: 150
});
}
});
}
// Color shifting for fire effect
var heatColor = Math.sin(LK.ticks * 0.3) * 0.3;
var redIntensity = Math.floor((0.8 + heatColor) * 255);
var yellowIntensity = Math.floor(heatColor * 0.6 * 255);
obstacleGraphics.tint = (redIntensity << 16) + (yellowIntensity << 8);
} else if (self.type === 'skull') {
// Add haunting motion to skeleton/skull
var sway = Math.sin(LK.ticks * 0.1) * 0.03;
var bob = Math.sin(LK.ticks * 0.15) * 0.02;
obstacleGraphics.rotation = sway;
obstacleGraphics.skewY = bob;
// Periodic ghostly fade effect
if (LK.ticks % 180 === 0) {
tween(obstacleGraphics, {
alpha: 0.7
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(obstacleGraphics, {
alpha: 1.0
}, {
duration: 300,
easing: tween.easeInOut
});
}
});
}
// Slight scale breathing effect
var breathe = 1.0 + Math.sin(LK.ticks * 0.08) * 0.05;
var currentScale = self.shrunk ? 0.5 : 1.0;
obstacleGraphics.scaleX = currentScale * breathe;
obstacleGraphics.scaleY = currentScale * breathe;
}
};
return self;
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
var assetName = type + 'PowerUp';
var powerUpGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type;
self.collected = false;
self.update = function () {
self.y += gameSpeed;
// Spinning effect
powerUpGraphics.rotation += 0.1;
// Pulsing effect
var pulse = 1.2 + Math.sin(LK.ticks * 0.2) * 0.3;
powerUpGraphics.scaleX = pulse;
powerUpGraphics.scaleY = pulse;
};
return self;
});
var Road = Container.expand(function () {
var self = Container.call(this);
var roadGraphics = self.attachAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
self.update = function () {
self.y += gameSpeed;
// Reset position when off screen
if (self.y > 2732 + 75) {
self.y = -75;
}
};
return self;
});
var SideObstacle = Container.expand(function (side, type) {
var self = Container.call(this);
var obstacleGraphics = self.attachAsset(type, {
anchorX: 0.5,
anchorY: 0.5
});
self.side = side; // 'left' or 'right'
self.type = type;
self.speedX = side === 'left' ? 6 : -6;
self.startX = side === 'left' ? -100 : 2148;
self.targetX = side === 'left' ? 600 : 1448;
self.x = self.startX;
self.active = false;
self.update = function () {
self.y += gameSpeed * 0.8;
if (!self.active && self.x !== self.targetX) {
self.x += self.speedX;
if (self.side === 'left' && self.x >= self.targetX || self.side === 'right' && self.x <= self.targetX) {
self.x = self.targetX;
self.active = true;
// Add tween animation when reaching target
tween(obstacleGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 400,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(obstacleGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeInOut
});
}
});
}
}
// Add type-specific animations
if (self.type === 'demon') {
obstacleGraphics.rotation += 0.03;
} else if (self.type === 'firePit') {
var flicker = 1.0 + Math.sin(LK.ticks * 0.5) * 0.1;
obstacleGraphics.scaleX *= flicker;
obstacleGraphics.scaleY *= flicker;
}
};
return self;
});
var Wall = Container.expand(function (side) {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
anchorY: 0.5
});
self.side = side; // 'left' or 'right'
self.update = function () {
self.y += gameSpeed;
// Reset position when off screen
if (self.y > 2732 + 50) {
self.y = -50;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Game variables
var gameSpeed = 8;
var baseSpeed = 8;
var car;
var coins = [];
var obstacles = [];
var powerUps = [];
var roads = [];
var walls = [];
var sideObstacles = [];
var lavaBackgrounds = [];
var lavaStreams = [];
var backgroundColorPhase = 0; // 0=red, 1=purple, 2=black
var difficultyTimer = 0;
var devilEventTimer = 0;
var shakeTimer = 0;
var spawnTimer = 0;
var powerUpSpawnTimer = 0;
var sideObstacleSpawnTimer = 0;
// Lane obstacle tracking to prevent overcrowding
var laneObstacleCounts = [0, 0, 0]; // Track obstacles per lane
// Bonus system variables
var scoreMultiplier = 1;
var multiplierTimer = 0;
var comboCount = 0;
var comboTimer = 0;
var lastCollectTime = 0;
var bonusEventTimer = 0;
// Lane positions for vertical game (horizontal lanes)
var lanes = [2048 * 0.2, 2048 * 0.5, 2048 * 0.8];
function getLaneX(laneIndex) {
return lanes[laneIndex];
}
function getRandomLane() {
return Math.floor(Math.random() * 3);
}
function updateLaneObstacleCounts() {
// Reset counts
laneObstacleCounts = [0, 0, 0];
// Count obstacles in each lane
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
var laneIndex = Math.round((obstacle.x - lanes[0]) / (lanes[1] - lanes[0]));
if (laneIndex >= 0 && laneIndex < 3) {
laneObstacleCounts[laneIndex]++;
}
}
}
function getAvailableLane() {
updateLaneObstacleCounts();
var availableLanes = [];
// Find lanes with no obstacles to ensure at least one lane is always clear
for (var i = 0; i < 3; i++) {
if (laneObstacleCounts[i] === 0) {
availableLanes.push(i);
}
}
// If no completely clear lanes, find lanes with fewer obstacles
if (availableLanes.length === 0) {
var minObstacles = Math.min.apply(Math, laneObstacleCounts);
for (var i = 0; i < 3; i++) {
if (laneObstacleCounts[i] === minObstacles) {
availableLanes.push(i);
}
}
}
// Return random available lane
return availableLanes[Math.floor(Math.random() * availableLanes.length)];
}
function calculateBonus(basePoints) {
var bonus = basePoints * scoreMultiplier;
// Combo bonus
if (comboCount > 1) {
bonus += comboCount * 5;
}
// Speed bonus - higher speed gives more points
var speedBonus = Math.floor((gameSpeed - baseSpeed) * 2);
bonus += speedBonus;
return Math.floor(bonus);
}
function updateCombo() {
var currentTime = LK.ticks;
// Check if within combo window (2 seconds)
if (currentTime - lastCollectTime < 120) {
comboCount++;
comboTimer = 180; // Show combo for 3 seconds
} else {
comboCount = 1;
comboTimer = 180;
}
lastCollectTime = currentTime;
// Update combo display
if (comboCount > 1) {
comboTxt.setText('COMBO x' + comboCount + '!');
tween(comboTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
onComplete: function onComplete() {
tween(comboTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
}
}
function triggerBonusEvent() {
// Random bonus events
var eventType = Math.floor(Math.random() * 3);
if (eventType === 0) {
// Score multiplier boost
scoreMultiplier = Math.min(scoreMultiplier + 1, 5);
multiplierTimer = 600; // 10 seconds
LK.effects.flashScreen(0xffff00, 300);
} else if (eventType === 1) {
// Instant score bonus
var bonus = calculateBonus(100);
LK.setScore(LK.getScore() + bonus);
scoreTxt.setText('Score: ' + LK.getScore());
LK.effects.flashScreen(0x00ff00, 300);
} else {
// Combo extension
comboTimer += 300; // Extra 5 seconds
LK.effects.flashScreen(0xff6600, 300);
}
}
// Create score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create multiplier display
var multiplierTxt = new Text2('x1', {
size: 60,
fill: 0xffff00
});
multiplierTxt.anchor.set(1, 0);
multiplierTxt.x = -20;
multiplierTxt.y = 100;
LK.gui.topRight.addChild(multiplierTxt);
// Create combo display
var comboTxt = new Text2('', {
size: 50,
fill: 0xff6600
});
comboTxt.anchor.set(0.5, 0);
comboTxt.y = 120;
LK.gui.top.addChild(comboTxt);
// Create road segments
for (var i = 0; i < 3; i++) {
var road = new Road();
road.x = 2048 / 2;
road.y = i * 150 - 75;
roads.push(road);
game.addChild(road);
}
// Create lava backgrounds
for (var i = 0; i < 12; i++) {
var lavaBg = new LavaBackground();
lavaBg.x = 2048 / 2;
lavaBg.y = i * 250 - 300;
lavaBackgrounds.push(lavaBg);
game.addChild(lavaBg);
}
// Create lava streams
for (var i = 0; i < 6; i++) {
var lavaStream = new LavaStream();
lavaStream.x = 200 + i * 300;
lavaStream.y = 2732 / 2;
lavaStreams.push(lavaStream);
game.addChild(lavaStream);
}
// Create walls
var leftWall = new Wall('left');
leftWall.x = 100;
leftWall.y = 2732 / 2;
walls.push(leftWall);
game.addChild(leftWall);
var rightWall = new Wall('right');
rightWall.x = 2048 - 100;
rightWall.y = 2732 / 2;
walls.push(rightWall);
game.addChild(rightWall);
// Create car
car = game.addChild(new Car());
car.x = getLaneX(1);
car.y = 2732 - 200;
// Touch controls
game.down = function (x, y, obj) {
if (x < 2048 / 3) {
// Left third - move left
if (car.targetLane > 0) {
car.targetLane--;
}
} else if (x > 2048 * 2 / 3) {
// Right third - move right
if (car.targetLane < 2) {
car.targetLane++;
}
}
};
function spawnCoin() {
var coin = new Coin();
coin.x = getLaneX(getRandomLane());
coin.y = -50;
coins.push(coin);
game.addChild(coin);
}
function spawnObstacle() {
// Check if we can spawn while ensuring one lane stays clear
updateLaneObstacleCounts();
// Count how many lanes have obstacles
var lanesWithObstacles = 0;
for (var i = 0; i < 3; i++) {
if (laneObstacleCounts[i] > 0) {
lanesWithObstacles++;
}
}
// Don't spawn if 2 lanes already have obstacles (keep at least one lane clear)
if (lanesWithObstacles >= 2) {
return;
}
var types = ['demon', 'firePit', 'lavaRock', 'skull', 'demonWing'];
var type = types[Math.floor(Math.random() * types.length)];
var obstacle = new Obstacle(type);
obstacle.x = getLaneX(getAvailableLane());
obstacle.y = -100;
obstacles.push(obstacle);
game.addChild(obstacle);
}
function spawnPowerUp() {
var types = ['invisibility', 'slow', 'shrink'];
var type = types[Math.floor(Math.random() * types.length)];
var powerUp = new PowerUp(type);
powerUp.x = getLaneX(getRandomLane());
powerUp.y = -75;
powerUps.push(powerUp);
game.addChild(powerUp);
}
function spawnSideObstacle() {
var types = ['demon', 'firePit', 'lavaRock', 'skull'];
var type = types[Math.floor(Math.random() * types.length)];
var side = Math.random() < 0.5 ? 'left' : 'right';
var sideObstacle = new SideObstacle(side, type);
sideObstacle.y = -100;
sideObstacles.push(sideObstacle);
game.addChild(sideObstacle);
}
function updateBackground() {
var score = LK.getScore();
// Change background based on score - darker lava theme
if (score > 100 && backgroundColorPhase === 0) {
backgroundColorPhase = 1;
tween(game, {
backgroundColor: 0x220000
}, {
duration: 2000
});
} else if (score > 250 && backgroundColorPhase === 1) {
backgroundColorPhase = 2;
tween(game, {
backgroundColor: 0x000000
}, {
duration: 2000
});
}
}
function triggerDevilEvent() {
// Screen shake effect
shakeTimer = 180; // 3 seconds at 60fps
// Flash screen red briefly
LK.effects.flashScreen(0xff0000, 500);
}
game.update = function () {
// Apply screen shake
if (shakeTimer > 0) {
shakeTimer--;
var shakeX = (Math.random() - 0.5) * 20;
var shakeY = (Math.random() - 0.5) * 20;
game.x = shakeX;
game.y = shakeY;
} else {
game.x = 0;
game.y = 0;
}
// Increase difficulty over time
difficultyTimer++;
if (difficultyTimer % 600 === 0) {
// Every 10 seconds
gameSpeed += 0.5;
if (gameSpeed > baseSpeed * 2.5) {
gameSpeed = baseSpeed * 2.5; // Cap speed
}
}
// Increase speed every 100 points
var currentSpeedLevel = Math.floor(LK.getScore() / 100);
var expectedSpeed = baseSpeed + currentSpeedLevel * 2;
if (expectedSpeed > gameSpeed && expectedSpeed <= baseSpeed * 3) {
gameSpeed = expectedSpeed;
// Visual feedback for speed increase
LK.effects.flashScreen(0xffaa00, 400);
}
// Devil event timer
devilEventTimer++;
if (devilEventTimer > 1800 + Math.random() * 1800) {
// 30-60 seconds
triggerDevilEvent();
devilEventTimer = 0;
}
// Spawn objects
spawnTimer++;
powerUpSpawnTimer++;
sideObstacleSpawnTimer++;
// Spawn coins
if (spawnTimer > 60 - Math.min(40, LK.getScore() / 10)) {
if (Math.random() < 0.7) {
spawnCoin();
}
spawnTimer = 0;
}
// Spawn obstacles with increased difficulty every 250 points
var difficultyLevel = Math.floor(LK.getScore() / 250);
var baseSpawnRate = 0.02 + LK.getScore() * 0.0001;
var hardDifficultyBonus = difficultyLevel * 0.01; // Extra spawn chance every 250 points
if (Math.random() < baseSpawnRate + hardDifficultyBonus) {
spawnObstacle();
}
// Spawn power-ups occasionally
if (powerUpSpawnTimer > 600 && Math.random() < 0.15) {
// Every 10+ seconds, 15% chance - increased frequency for escape availability
spawnPowerUp();
powerUpSpawnTimer = 0;
}
// Spawn side obstacles
if (sideObstacleSpawnTimer > 300 && Math.random() < 0.25) {
// Every 5+ seconds, 25% chance
spawnSideObstacle();
sideObstacleSpawnTimer = 0;
}
// Update coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.y > 2732 + 100) {
coin.destroy();
coins.splice(i, 1);
continue;
}
// Check collision with car
if (!coin.collected && car.intersects(coin)) {
coin.collected = true;
var bonus = calculateBonus(10);
LK.setScore(LK.getScore() + bonus);
scoreTxt.setText('Score: ' + LK.getScore());
updateCombo();
LK.getSound('collect').play();
coin.destroy();
coins.splice(i, 1);
}
}
// Update obstacles
for (var i = obstacles.length - 1; i >= 0; i--) {
var obstacle = obstacles[i];
if (obstacle.y > 2732 + 150) {
obstacle.destroy();
obstacles.splice(i, 1);
continue;
}
// Check collision with car
if (!car.invincible && car.currentLane === Math.round((obstacle.x - lanes[0]) / (lanes[1] - lanes[0])) && car.intersects(obstacle)) {
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Update power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (powerUp.y > 2732 + 100) {
powerUp.destroy();
powerUps.splice(i, 1);
continue;
}
// Check collision with car
if (!powerUp.collected && car.intersects(powerUp)) {
powerUp.collected = true;
if (powerUp.type === 'invisibility') {
car.invincible = true;
car.invincibilityTimer = 300; // 5 seconds
scoreMultiplier += 1;
multiplierTimer = 300; // 5 seconds
} else if (powerUp.type === 'slow') {
car.slowEffect = true;
car.slowTimer = 360; // 6 seconds
var bonus = calculateBonus(25);
LK.setScore(LK.getScore() + bonus);
scoreTxt.setText('Score: ' + LK.getScore());
} else if (powerUp.type === 'shrink') {
car.shrinkObstacles = true;
car.shrinkTimer = 480; // 8 seconds
var bonus = calculateBonus(30);
LK.setScore(LK.getScore() + bonus);
scoreTxt.setText('Score: ' + LK.getScore());
}
updateCombo();
LK.getSound('collect').play();
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Update side obstacles
for (var i = sideObstacles.length - 1; i >= 0; i--) {
var sideObstacle = sideObstacles[i];
if (sideObstacle.y > 2732 + 150) {
sideObstacle.destroy();
sideObstacles.splice(i, 1);
continue;
}
// Check collision with car
if (!car.invincible && car.intersects(sideObstacle)) {
LK.getSound('crash').play();
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Update background color progression
updateBackground();
// Update bonus system timers
if (multiplierTimer > 0) {
multiplierTimer--;
if (multiplierTimer <= 0) {
scoreMultiplier = Math.max(1, scoreMultiplier - 1);
}
}
if (comboTimer > 0) {
comboTimer--;
if (comboTimer <= 0) {
comboCount = 0;
comboTxt.setText('');
}
}
// Update displays
multiplierTxt.setText('x' + scoreMultiplier);
if (scoreMultiplier > 1) {
multiplierTxt.fill = 0x00ff00;
} else {
multiplierTxt.fill = 0xffff00;
}
// Bonus events
bonusEventTimer++;
if (bonusEventTimer > 1200 + Math.random() * 600) {
// 20-30 seconds
if (Math.random() < 0.3) {
// 30% chance
triggerBonusEvent();
}
bonusEventTimer = 0;
}
// Add score over time with multiplier
if (LK.ticks % 30 === 0) {
var timeBonus = calculateBonus(1);
LK.setScore(LK.getScore() + timeBonus);
scoreTxt.setText('Score: ' + LK.getScore());
}
};
// Start background music
LK.playMusic('hellMusic'); ===================================================================
--- original.js
+++ change.js
@@ -266,8 +266,58 @@
}
};
return self;
});
+var SideObstacle = Container.expand(function (side, type) {
+ var self = Container.call(this);
+ var obstacleGraphics = self.attachAsset(type, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.side = side; // 'left' or 'right'
+ self.type = type;
+ self.speedX = side === 'left' ? 6 : -6;
+ self.startX = side === 'left' ? -100 : 2148;
+ self.targetX = side === 'left' ? 600 : 1448;
+ self.x = self.startX;
+ self.active = false;
+ self.update = function () {
+ self.y += gameSpeed * 0.8;
+ if (!self.active && self.x !== self.targetX) {
+ self.x += self.speedX;
+ if (self.side === 'left' && self.x >= self.targetX || self.side === 'right' && self.x <= self.targetX) {
+ self.x = self.targetX;
+ self.active = true;
+ // Add tween animation when reaching target
+ tween(obstacleGraphics, {
+ scaleX: 1.2,
+ scaleY: 1.2
+ }, {
+ duration: 400,
+ easing: tween.easeInOut,
+ onFinish: function onFinish() {
+ tween(obstacleGraphics, {
+ scaleX: 1.0,
+ scaleY: 1.0
+ }, {
+ duration: 400,
+ easing: tween.easeInOut
+ });
+ }
+ });
+ }
+ }
+ // Add type-specific animations
+ if (self.type === 'demon') {
+ obstacleGraphics.rotation += 0.03;
+ } else if (self.type === 'firePit') {
+ var flicker = 1.0 + Math.sin(LK.ticks * 0.5) * 0.1;
+ obstacleGraphics.scaleX *= flicker;
+ obstacleGraphics.scaleY *= flicker;
+ }
+ };
+ return self;
+});
var Wall = Container.expand(function (side) {
var self = Container.call(this);
var wallGraphics = self.attachAsset('wall', {
anchorX: 0.5,
@@ -302,16 +352,18 @@
var obstacles = [];
var powerUps = [];
var roads = [];
var walls = [];
+var sideObstacles = [];
var lavaBackgrounds = [];
var lavaStreams = [];
var backgroundColorPhase = 0; // 0=red, 1=purple, 2=black
var difficultyTimer = 0;
var devilEventTimer = 0;
var shakeTimer = 0;
var spawnTimer = 0;
var powerUpSpawnTimer = 0;
+var sideObstacleSpawnTimer = 0;
// Lane obstacle tracking to prevent overcrowding
var laneObstacleCounts = [0, 0, 0]; // Track obstacles per lane
// Bonus system variables
var scoreMultiplier = 1;
@@ -536,8 +588,17 @@
powerUp.y = -75;
powerUps.push(powerUp);
game.addChild(powerUp);
}
+function spawnSideObstacle() {
+ var types = ['demon', 'firePit', 'lavaRock', 'skull'];
+ var type = types[Math.floor(Math.random() * types.length)];
+ var side = Math.random() < 0.5 ? 'left' : 'right';
+ var sideObstacle = new SideObstacle(side, type);
+ sideObstacle.y = -100;
+ sideObstacles.push(sideObstacle);
+ game.addChild(sideObstacle);
+}
function updateBackground() {
var score = LK.getScore();
// Change background based on score - darker lava theme
if (score > 100 && backgroundColorPhase === 0) {
@@ -600,8 +661,9 @@
}
// Spawn objects
spawnTimer++;
powerUpSpawnTimer++;
+ sideObstacleSpawnTimer++;
// Spawn coins
if (spawnTimer > 60 - Math.min(40, LK.getScore() / 10)) {
if (Math.random() < 0.7) {
spawnCoin();
@@ -620,8 +682,14 @@
// Every 10+ seconds, 15% chance - increased frequency for escape availability
spawnPowerUp();
powerUpSpawnTimer = 0;
}
+ // Spawn side obstacles
+ if (sideObstacleSpawnTimer > 300 && Math.random() < 0.25) {
+ // Every 5+ seconds, 25% chance
+ spawnSideObstacle();
+ sideObstacleSpawnTimer = 0;
+ }
// Update coins
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
if (coin.y > 2732 + 100) {
@@ -691,8 +759,24 @@
powerUp.destroy();
powerUps.splice(i, 1);
}
}
+ // Update side obstacles
+ for (var i = sideObstacles.length - 1; i >= 0; i--) {
+ var sideObstacle = sideObstacles[i];
+ if (sideObstacle.y > 2732 + 150) {
+ sideObstacle.destroy();
+ sideObstacles.splice(i, 1);
+ continue;
+ }
+ // Check collision with car
+ if (!car.invincible && car.intersects(sideObstacle)) {
+ LK.getSound('crash').play();
+ LK.effects.flashScreen(0xff0000, 1000);
+ LK.showGameOver();
+ return;
+ }
+ }
// Update background color progression
updateBackground();
// Update bonus system timers
if (multiplierTimer > 0) {
fireFlicker
Sound effect
skullMoan
Sound effect
powerUp
Sound effect
hellMusic
Music
bonusEvent
Sound effect
laneChange
Sound effect
collect
Sound effect
gameOverMusic
Music
devilLaugh
Sound effect
explosion
Sound effect
crash
Sound effect
angelBlessing
Sound effect
failure
Sound effect
angelAppearance
Sound effect