/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Enemy visual
var cube = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff0000
});
self.width = cube.width;
self.height = cube.height;
self.speed = 7;
self.shootTimer = 0;
self.shootInterval = 120; // Shoot every 120 frames (2 seconds)
self.update = function () {
self.x -= self.speed;
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
self.shoot = function () {
if (gameRunning && !player.isDead) {
// Only create enemy cube if there are no enemy cubes currently in the game
if (enemyCubes.length === 0) {
createEnemyCube(self.x, self.y);
}
}
};
return self;
});
var EnemyCube = Container.expand(function () {
var self = Container.call(this);
// Cube visual
var cube = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
tint: 0xff6666
});
self.width = cube.width * 0.6;
self.height = cube.height * 0.6;
// Calculate direction towards player's position
self.velocity = {
x: 0,
y: 0
};
self.update = function () {
// Move the cube
self.x += self.velocity.x;
self.y += self.velocity.y;
// Rotate the cube for visual effect
cube.rotation += 0.05;
// Remove if off screen
if (self.y > 2732 || self.x < -self.width || self.x > 2048 + self.width) {
self.destroy();
return true;
}
return false;
};
return self;
});
var Obstacle = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'spike';
self.speed = 10;
if (self.type === 'spike') {
var spikeGraphic = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = spikeGraphic.width;
self.height = spikeGraphic.height;
} else if (self.type === 'greenSpike') {
var greenSpikeGraphic = self.attachAsset('greenSpike', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = greenSpikeGraphic.width;
self.height = greenSpikeGraphic.height;
} else if (self.type === 'laser') {
var laserGraphic = self.attachAsset('laser', {
anchorX: 0.0,
anchorY: 0.5,
alpha: 0.7
});
self.width = laserGraphic.width;
self.height = laserGraphic.height;
// Laser activation
self.active = false;
self.warningTime = 30;
self.activeTime = 60;
self.timer = self.warningTime;
// Warning effect
self.flash = function () {
if (self.timer % 5 === 0) {
laserGraphic.alpha = laserGraphic.alpha === 0.3 ? 0.7 : 0.3;
}
};
} else if (self.type === 'searchlight') {
var searchlightGraphic = self.attachAsset('searchlight', {
anchorX: 0.5,
anchorY: 0.0,
alpha: 0.5
});
self.width = searchlightGraphic.width;
self.height = searchlightGraphic.height;
}
self.update = function () {
self.x -= self.speed;
// Laser special behavior
if (self.type === 'laser') {
self.timer--;
if (self.timer > 0 && !self.active) {
// Warning phase
self.flash();
} else if (!self.active) {
// Activate
self.active = true;
laserGraphic.alpha = 1;
self.timer = self.activeTime;
// Play laser sound when activated
LK.getSound('laser').play();
} else if (self.timer <= 0) {
// Deactivate
self.destroy();
return true;
}
}
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
var platformGraphic = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.0
});
self.width = platformGraphic.width;
self.height = platformGraphic.height;
self.speed = 10;
self.update = function () {
self.x -= self.speed;
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Player states
self.isJumping = false;
self.isSliding = false;
self.isHiding = false;
self.isDead = false;
self.health = 3; // Player starts with 3 health points
self.invulnerable = false; // Invulnerability after getting hit
self.invulnerableTime = 0; // Track invulnerability duration
self.canDoubleJump = false;
self.hasDoubleJumped = false;
self.canTripleJump = false;
self.lastJumpTime = 0;
self.velocity = {
x: 0,
y: 0
};
self.gravity = 1.2;
self.jumpForce = -30; // Increased from -25 to -30 for higher first jump
self.groundY = 2200;
// Visual components
var cube = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial rotation and rotation speed
self.rotationSpeed = 0.05;
self.currentRotation = 0;
// Particle system for the player's trail
self.particles = [];
// Player controls
self.jump = function () {
var currentTime = LK.ticks;
if (!self.isDead && !self.isSliding) {
if (!self.isJumping) {
// First jump
self.velocity.y = self.jumpForce;
self.isJumping = true;
self.canDoubleJump = true;
self.hasDoubleJumped = false;
self.canTripleJump = false;
self.lastJumpTime = currentTime;
// Increase rotation speed temporarily when jumping
tween(self, {
rotationSpeed: 0.15
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotationSpeed: 0.05
}, {
duration: 500,
easing: tween.easeIn
});
}
});
LK.getSound('jump').play();
} else if (self.canDoubleJump && !self.hasDoubleJumped && currentTime - self.lastJumpTime < 30) {
// Double jump (only if tapped quickly after first jump)
self.velocity.y = self.jumpForce * 1.2; // Higher second jump
self.hasDoubleJumped = true;
self.canDoubleJump = false;
self.canTripleJump = true; // Enable triple jump after double jump
self.lastJumpTime = currentTime;
// Extra rotation boost for double jump
tween(self, {
rotationSpeed: 0.25
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotationSpeed: 0.05
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.getSound('jump').play();
} else if (self.canTripleJump && self.hasDoubleJumped && currentTime - self.lastJumpTime < 30) {
// Triple jump (only if tapped quickly after second jump)
self.velocity.y = self.jumpForce * 1.3; // Even higher third jump
self.canTripleJump = false;
// Extreme rotation boost for triple jump
tween(self, {
rotationSpeed: 0.35
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotationSpeed: 0.05
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.getSound('jump').play();
}
}
};
self.slide = function () {
if (!self.isDead && !self.isSliding) {
self.isSliding = true;
// Set velocity directly instead of trying to tween it
self.velocity.x = 10; // Increased from 5 to 10 for faster slide
// Temporarily increase player speed and rotation
tween(self, {
rotationSpeed: 0.3 // Increased rotation speed for more visual impact
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Reset to normal speed after 300ms
self.isSliding = false;
self.velocity.x = 0; // Reset velocity directly
// Store the current x position before slide ends
var originalX = self.x;
// Tween back to normal position
tween(self, {
x: originalX - 50 // Move back slightly from the slid position
}, {
duration: 150,
easing: tween.easeOut
});
tween(self, {
rotationSpeed: 0.05
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
self.hide = function (shadow) {
if (!self.isDead && shadow) {
self.isHiding = true;
cube.alpha = 0.3;
LK.getSound('hide').play();
}
};
self.unhide = function () {
self.isHiding = false;
cube.alpha = 1;
};
self.die = function () {
if (self.invulnerable) {
return;
} // Don't take damage if invulnerable
if (!self.isDead) {
self.health--;
if (self.health <= 0) {
// Player is dead after losing all health
self.isDead = true;
// Stop rotation by tweening to zero
tween(self, {
rotationSpeed: 0
}, {
duration: 100
});
LK.getSound('death').play();
LK.effects.flashScreen(0xff0000, 500);
// Create death particles
for (var i = 0; i < 20; i++) {
var particle = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
// Random velocity for each particle
var angle = Math.random() * Math.PI * 2;
var speed = 5 + Math.random() * 10;
particle.vx = Math.cos(angle) * speed;
particle.vy = Math.sin(angle) * speed;
game.addChild(particle);
deathParticles.push(particle);
}
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
} else {
// Player still has health, make invulnerable temporarily
self.invulnerable = true;
self.invulnerableTime = 60; // 1 second invulnerability (60 frames)
LK.effects.flashScreen(0xff0000, 200);
// Play hit sound
LK.getSound('hit').play();
// Flash the player to indicate damage
var flashCount = 0;
var flashInterval = LK.setInterval(function () {
cube.alpha = cube.alpha === 1 ? 0.3 : 1;
flashCount++;
if (flashCount >= 6) {
// Flash 3 times (6 state changes)
LK.clearInterval(flashInterval);
cube.alpha = 1;
}
}, 100);
}
}
};
// Physics update
self.updatePhysics = function () {
if (self.isDead) {
return;
}
// Update invulnerability timer
if (self.invulnerable) {
self.invulnerableTime--;
if (self.invulnerableTime <= 0) {
self.invulnerable = false;
cube.alpha = 1; // Ensure player is fully visible
}
}
// Apply gravity
self.velocity.y += self.gravity;
// Update position
self.y += self.velocity.y;
self.x += self.velocity.x; // Apply horizontal velocity for sliding
// Check floor collision
if (self.y >= self.groundY) {
self.y = self.groundY;
self.velocity.y = 0;
self.isJumping = false;
self.canDoubleJump = false;
self.hasDoubleJumped = false;
self.canTripleJump = false;
}
// Update cube rotation when moving
if (gameRunning && !self.isDead) {
self.currentRotation += self.rotationSpeed;
cube.rotation = self.currentRotation;
}
// Create trail particles
if (gameRunning && LK.ticks % 5 === 0) {
var particle = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x - 30,
y: self.y + (self.isSliding ? 25 : 0)
});
particle.alpha = 0.7;
particle.life = 30;
game.addChild(particle);
self.particles.push(particle);
}
// Update particles
for (var i = self.particles.length - 1; i >= 0; i--) {
var p = self.particles[i];
p.alpha -= 0.02;
p.scaleX -= 0.02;
p.scaleY -= 0.02;
p.life--;
if (p.life <= 0 || p.alpha <= 0) {
p.destroy();
self.particles.splice(i, 1);
}
}
};
return self;
});
var Shadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphic = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.width = shadowGraphic.width;
self.height = shadowGraphic.height;
self.speed = 10;
self.update = function () {
self.x -= self.speed;
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// Game state
var gameRunning = false;
var speed = 10;
var score = 0;
var highScore = storage.highScore || 0;
var platformY = 2200;
var spawnDistance = 800;
var nextSpawnX = 2048 + 200;
var difficultyTimer = 0;
var difficultyInterval = 1000;
var lastTouchX = 0;
var lastTouchY = 0;
var touchStartTime = 0;
// Game elements
var player;
var platforms = [];
var obstacles = [];
var shadows = [];
var deathParticles = [];
var enemies = [];
var enemyCubes = [];
// UI elements
var scoreTxt;
var highScoreTxt;
var healthTxt;
var tapToStartTxt;
// Initialize the game
function initGame() {
gameRunning = false;
speed = 10;
score = 0;
platformY = 2200;
spawnDistance = 800;
nextSpawnX = 2048 + 200;
difficultyTimer = 0;
// Clear existing elements
while (platforms.length > 0) {
platforms[0].destroy();
platforms.shift();
}
while (obstacles.length > 0) {
obstacles[0].destroy();
obstacles.shift();
}
while (shadows.length > 0) {
shadows[0].destroy();
shadows.shift();
}
while (deathParticles.length > 0) {
deathParticles[0].destroy();
deathParticles.shift();
}
// Clear enemies and enemy cubes
while (enemies.length > 0) {
enemies[0].destroy();
enemies.shift();
}
while (enemyCubes.length > 0) {
enemyCubes[0].destroy();
enemyCubes.shift();
}
// Add background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
game.addChildAt(background, 0); // Add at index 0 to ensure it's at the back
// Create player
player = new Player();
player.x = 400;
player.y = platformY; // Position player on top edge of platform
game.addChild(player);
// Create initial platform
createPlatform(0, platformY, 2048 + 500);
// Create UI
if (!scoreTxt) {
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -250;
scoreTxt.y = 50;
}
// Create health display
healthTxt = new Text2('Health: 3', {
size: 60,
fill: 0xFF5555
});
healthTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthTxt);
healthTxt.x = 120; // Keep away from top-left corner menu icon
healthTxt.y = 50;
if (!highScoreTxt) {
highScoreTxt = new Text2('High Score: ' + highScore, {
size: 40,
fill: 0xAAAAAA
});
highScoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreTxt);
highScoreTxt.x = -250;
highScoreTxt.y = 120;
}
// Create start instruction
tapToStartTxt = new Text2('Tap to Start\nTap to Jump\nSwipe Right to Slide Faster', {
size: 70,
fill: 0xFFFFFF
});
tapToStartTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(tapToStartTxt);
// Start the background music
LK.playMusic('gameBgm', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
// Create a platform at the specified position
function createPlatform(x, y, width) {
var platform = new Platform();
platform.x = x;
platform.y = y;
if (width) {
platform.width = width;
platform.getChildAt(0).width = width;
}
game.addChild(platform);
platforms.push(platform);
return platform;
}
// Create an obstacle
function createObstacle(type, x, y) {
var obstacle = new Obstacle(type);
obstacle.x = x;
obstacle.y = y;
if (type === 'laser') {
obstacle.y = Math.random() * 1500 + 500;
}
game.addChild(obstacle);
obstacles.push(obstacle);
return obstacle;
}
// Create a shadow area
function createShadow(x, y) {
var shadow = new Shadow();
shadow.x = x;
shadow.y = y;
game.addChild(shadow);
shadows.push(shadow);
return shadow;
}
// Create an enemy
function createEnemy(x, y) {
var enemy = new Enemy();
enemy.x = x;
enemy.y = y;
game.addChild(enemy);
enemies.push(enemy);
return enemy;
}
// Create an enemy cube
function createEnemyCube(x, y) {
var cube = new EnemyCube();
cube.x = x;
cube.y = y;
// Calculate direction vector towards player
var dx = player.x - x;
var dy = player.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize and set speed
cube.velocity.x = dx / distance * 12;
cube.velocity.y = dy / distance * 12;
game.addChild(cube);
enemyCubes.push(cube);
// Remove any spike obstacles that are at the bottom when enemy cubes are spawned
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i].type === 'spike' && obstacles[i].y === platformY) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
}
return cube;
}
// Spawn game elements
function spawnElements() {
if (nextSpawnX <= 2048 + 200) {
// Decide what to spawn
var rand = Math.random();
// Always spawn a platform
var platform = createPlatform(nextSpawnX, platformY);
// Maybe spawn an obstacle
if (rand < 0.7) {
// Only spawn spikes if there are no enemy cubes
if (rand < 0.4 && enemyCubes.length === 0) {
// Randomly decide between regular spike and green spike
if (Math.random() < 0.5) {
// Spawn regular spike
createObstacle('spike', nextSpawnX, platformY);
} else {
// Spawn green spike
createObstacle('greenSpike', nextSpawnX, platformY);
}
} else if (rand < 0.6) {
// Spawn searchlight
createObstacle('searchlight', nextSpawnX, 0);
} else {
// Spawn laser
createObstacle('laser', 0, 0);
}
}
// Maybe spawn a shadow
if (Math.random() < 0.3) {
createShadow(nextSpawnX, platformY - 40);
}
// Maybe spawn an enemy at the top
if (Math.random() < 0.2) {
var enemyX = nextSpawnX + Math.random() * 300;
var enemyY = 200 + Math.random() * 300;
createEnemy(enemyX, enemyY);
}
nextSpawnX += spawnDistance;
}
nextSpawnX -= speed;
}
// Check for collisions
function checkCollisions() {
if (player.isDead) {
return;
}
// Check obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.type === 'laser' && !obstacle.active) {
continue; // Skip inactive lasers
}
if (player.intersects(obstacle)) {
// If in a shadow and it's a searchlight, you're safe
if (obstacle.type === 'searchlight' && player.isHiding) {
continue;
}
player.die();
break;
}
}
// Check enemy cube collisions
for (var i = enemyCubes.length - 1; i >= 0; i--) {
if (player.intersects(enemyCubes[i])) {
player.die();
// Remove the enemy cube that hit the player
enemyCubes[i].destroy();
enemyCubes.splice(i, 1);
// Update health display
if (healthTxt) {
healthTxt.setText('Health: ' + Math.max(0, player.health));
}
break;
}
}
// Check shadow interactions
var inShadow = false;
for (var j = 0; j < shadows.length; j++) {
if (player.intersects(shadows[j])) {
inShadow = true;
break;
}
}
if (inShadow) {
player.hide();
} else {
player.unhide();
}
}
// Update score
function updateScore() {
if (!gameRunning || player.isDead) {
return;
}
score++;
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
if (score % 10 === 0) {
scoreTxt.setText('Score: ' + score);
}
}
// Increase difficulty over time
function updateDifficulty() {
difficultyTimer++;
if (difficultyTimer >= difficultyInterval) {
difficultyTimer = 0;
// Increase speed
speed += 0.5;
for (var i = 0; i < platforms.length; i++) {
platforms[i].speed = speed;
}
for (var j = 0; j < obstacles.length; j++) {
obstacles[j].speed = speed;
}
for (var k = 0; k < shadows.length; k++) {
shadows[k].speed = speed;
}
for (var e = 0; e < enemies.length; e++) {
enemies[e].speed = speed * 0.7; // Enemies move a bit slower than obstacles
}
// Decrease spawn distance
spawnDistance = Math.max(400, spawnDistance - 10);
}
}
// Main update function
game.update = function () {
if (gameRunning) {
// Update game elements
player.updatePhysics();
// Spawn new elements
spawnElements();
// Update existing elements
for (var i = platforms.length - 1; i >= 0; i--) {
if (platforms[i].update()) {
platforms.splice(i, 1);
}
}
for (var j = obstacles.length - 1; j >= 0; j--) {
if (obstacles[j].update()) {
obstacles.splice(j, 1);
}
}
for (var k = shadows.length - 1; k >= 0; k--) {
if (shadows[k].update()) {
shadows.splice(k, 1);
}
}
// Update enemies
for (var m = enemies.length - 1; m >= 0; m--) {
if (enemies[m].update()) {
enemies.splice(m, 1);
}
}
// Update enemy cubes
for (var n = enemyCubes.length - 1; n >= 0; n--) {
if (enemyCubes[n].update()) {
enemyCubes.splice(n, 1);
}
}
// Update death particles
for (var l = deathParticles.length - 1; l >= 0; l--) {
var p = deathParticles[l];
p.x += p.vx;
p.y += p.vy;
p.vx *= 0.95;
p.vy *= 0.95;
p.vy += 0.5; // gravity
p.alpha -= 0.02;
if (p.alpha <= 0) {
p.destroy();
deathParticles.splice(l, 1);
}
}
// Check for collisions
checkCollisions();
// Update score (every 10 ticks)
if (LK.ticks % 10 === 0) {
updateScore();
}
// Update difficulty
updateDifficulty();
}
};
// Event handlers
game.down = function (x, y, obj) {
// Track touch position and time for swipe detection
lastTouchX = x;
lastTouchY = y;
touchStartTime = Date.now();
if (!gameRunning) {
// Start the game
gameRunning = true;
tapToStartTxt.destroy();
scoreTxt.setText('Score: 0');
} else if (!player.isDead) {
player.jump();
}
};
// Sliding functionality has been removed
game.up = function (x, y, obj) {
if (gameRunning && !player.isDead) {
var dx = x - lastTouchX;
var dy = y - lastTouchY;
var distance = Math.sqrt(dx * dx + dy * dy);
var time = Date.now() - touchStartTime;
// Add swipe property to event if this appears to be a swipe
if (distance > 50 && time < 300) {
// This is a swipe, create a custom property on the event object
obj.event = obj.event || {};
obj.event.isSwipe = true;
// If it's a right swipe (simplified check for better responsiveness)
if (dx > 50) {
player.slide();
}
}
}
};
game.move = function (x, y, obj) {
if (gameRunning && !player.isDead) {
// We'll let the up handler manage all slide triggers
// This prevents double-triggering slides
}
};
// Initialize the game
initGame(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Enemy visual
var cube = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff0000
});
self.width = cube.width;
self.height = cube.height;
self.speed = 7;
self.shootTimer = 0;
self.shootInterval = 120; // Shoot every 120 frames (2 seconds)
self.update = function () {
self.x -= self.speed;
self.shootTimer++;
if (self.shootTimer >= self.shootInterval) {
self.shoot();
self.shootTimer = 0;
}
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
self.shoot = function () {
if (gameRunning && !player.isDead) {
// Only create enemy cube if there are no enemy cubes currently in the game
if (enemyCubes.length === 0) {
createEnemyCube(self.x, self.y);
}
}
};
return self;
});
var EnemyCube = Container.expand(function () {
var self = Container.call(this);
// Cube visual
var cube = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6,
tint: 0xff6666
});
self.width = cube.width * 0.6;
self.height = cube.height * 0.6;
// Calculate direction towards player's position
self.velocity = {
x: 0,
y: 0
};
self.update = function () {
// Move the cube
self.x += self.velocity.x;
self.y += self.velocity.y;
// Rotate the cube for visual effect
cube.rotation += 0.05;
// Remove if off screen
if (self.y > 2732 || self.x < -self.width || self.x > 2048 + self.width) {
self.destroy();
return true;
}
return false;
};
return self;
});
var Obstacle = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'spike';
self.speed = 10;
if (self.type === 'spike') {
var spikeGraphic = self.attachAsset('spike', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = spikeGraphic.width;
self.height = spikeGraphic.height;
} else if (self.type === 'greenSpike') {
var greenSpikeGraphic = self.attachAsset('greenSpike', {
anchorX: 0.5,
anchorY: 1.0
});
self.width = greenSpikeGraphic.width;
self.height = greenSpikeGraphic.height;
} else if (self.type === 'laser') {
var laserGraphic = self.attachAsset('laser', {
anchorX: 0.0,
anchorY: 0.5,
alpha: 0.7
});
self.width = laserGraphic.width;
self.height = laserGraphic.height;
// Laser activation
self.active = false;
self.warningTime = 30;
self.activeTime = 60;
self.timer = self.warningTime;
// Warning effect
self.flash = function () {
if (self.timer % 5 === 0) {
laserGraphic.alpha = laserGraphic.alpha === 0.3 ? 0.7 : 0.3;
}
};
} else if (self.type === 'searchlight') {
var searchlightGraphic = self.attachAsset('searchlight', {
anchorX: 0.5,
anchorY: 0.0,
alpha: 0.5
});
self.width = searchlightGraphic.width;
self.height = searchlightGraphic.height;
}
self.update = function () {
self.x -= self.speed;
// Laser special behavior
if (self.type === 'laser') {
self.timer--;
if (self.timer > 0 && !self.active) {
// Warning phase
self.flash();
} else if (!self.active) {
// Activate
self.active = true;
laserGraphic.alpha = 1;
self.timer = self.activeTime;
// Play laser sound when activated
LK.getSound('laser').play();
} else if (self.timer <= 0) {
// Deactivate
self.destroy();
return true;
}
}
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
var platformGraphic = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.0
});
self.width = platformGraphic.width;
self.height = platformGraphic.height;
self.speed = 10;
self.update = function () {
self.x -= self.speed;
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
// Player states
self.isJumping = false;
self.isSliding = false;
self.isHiding = false;
self.isDead = false;
self.health = 3; // Player starts with 3 health points
self.invulnerable = false; // Invulnerability after getting hit
self.invulnerableTime = 0; // Track invulnerability duration
self.canDoubleJump = false;
self.hasDoubleJumped = false;
self.canTripleJump = false;
self.lastJumpTime = 0;
self.velocity = {
x: 0,
y: 0
};
self.gravity = 1.2;
self.jumpForce = -30; // Increased from -25 to -30 for higher first jump
self.groundY = 2200;
// Visual components
var cube = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Set initial rotation and rotation speed
self.rotationSpeed = 0.05;
self.currentRotation = 0;
// Particle system for the player's trail
self.particles = [];
// Player controls
self.jump = function () {
var currentTime = LK.ticks;
if (!self.isDead && !self.isSliding) {
if (!self.isJumping) {
// First jump
self.velocity.y = self.jumpForce;
self.isJumping = true;
self.canDoubleJump = true;
self.hasDoubleJumped = false;
self.canTripleJump = false;
self.lastJumpTime = currentTime;
// Increase rotation speed temporarily when jumping
tween(self, {
rotationSpeed: 0.15
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotationSpeed: 0.05
}, {
duration: 500,
easing: tween.easeIn
});
}
});
LK.getSound('jump').play();
} else if (self.canDoubleJump && !self.hasDoubleJumped && currentTime - self.lastJumpTime < 30) {
// Double jump (only if tapped quickly after first jump)
self.velocity.y = self.jumpForce * 1.2; // Higher second jump
self.hasDoubleJumped = true;
self.canDoubleJump = false;
self.canTripleJump = true; // Enable triple jump after double jump
self.lastJumpTime = currentTime;
// Extra rotation boost for double jump
tween(self, {
rotationSpeed: 0.25
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotationSpeed: 0.05
}, {
duration: 400,
easing: tween.easeIn
});
}
});
LK.getSound('jump').play();
} else if (self.canTripleJump && self.hasDoubleJumped && currentTime - self.lastJumpTime < 30) {
// Triple jump (only if tapped quickly after second jump)
self.velocity.y = self.jumpForce * 1.3; // Even higher third jump
self.canTripleJump = false;
// Extreme rotation boost for triple jump
tween(self, {
rotationSpeed: 0.35
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
rotationSpeed: 0.05
}, {
duration: 300,
easing: tween.easeIn
});
}
});
LK.getSound('jump').play();
}
}
};
self.slide = function () {
if (!self.isDead && !self.isSliding) {
self.isSliding = true;
// Set velocity directly instead of trying to tween it
self.velocity.x = 10; // Increased from 5 to 10 for faster slide
// Temporarily increase player speed and rotation
tween(self, {
rotationSpeed: 0.3 // Increased rotation speed for more visual impact
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Reset to normal speed after 300ms
self.isSliding = false;
self.velocity.x = 0; // Reset velocity directly
// Store the current x position before slide ends
var originalX = self.x;
// Tween back to normal position
tween(self, {
x: originalX - 50 // Move back slightly from the slid position
}, {
duration: 150,
easing: tween.easeOut
});
tween(self, {
rotationSpeed: 0.05
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
};
self.hide = function (shadow) {
if (!self.isDead && shadow) {
self.isHiding = true;
cube.alpha = 0.3;
LK.getSound('hide').play();
}
};
self.unhide = function () {
self.isHiding = false;
cube.alpha = 1;
};
self.die = function () {
if (self.invulnerable) {
return;
} // Don't take damage if invulnerable
if (!self.isDead) {
self.health--;
if (self.health <= 0) {
// Player is dead after losing all health
self.isDead = true;
// Stop rotation by tweening to zero
tween(self, {
rotationSpeed: 0
}, {
duration: 100
});
LK.getSound('death').play();
LK.effects.flashScreen(0xff0000, 500);
// Create death particles
for (var i = 0; i < 20; i++) {
var particle = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
// Random velocity for each particle
var angle = Math.random() * Math.PI * 2;
var speed = 5 + Math.random() * 10;
particle.vx = Math.cos(angle) * speed;
particle.vy = Math.sin(angle) * speed;
game.addChild(particle);
deathParticles.push(particle);
}
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
} else {
// Player still has health, make invulnerable temporarily
self.invulnerable = true;
self.invulnerableTime = 60; // 1 second invulnerability (60 frames)
LK.effects.flashScreen(0xff0000, 200);
// Play hit sound
LK.getSound('hit').play();
// Flash the player to indicate damage
var flashCount = 0;
var flashInterval = LK.setInterval(function () {
cube.alpha = cube.alpha === 1 ? 0.3 : 1;
flashCount++;
if (flashCount >= 6) {
// Flash 3 times (6 state changes)
LK.clearInterval(flashInterval);
cube.alpha = 1;
}
}, 100);
}
}
};
// Physics update
self.updatePhysics = function () {
if (self.isDead) {
return;
}
// Update invulnerability timer
if (self.invulnerable) {
self.invulnerableTime--;
if (self.invulnerableTime <= 0) {
self.invulnerable = false;
cube.alpha = 1; // Ensure player is fully visible
}
}
// Apply gravity
self.velocity.y += self.gravity;
// Update position
self.y += self.velocity.y;
self.x += self.velocity.x; // Apply horizontal velocity for sliding
// Check floor collision
if (self.y >= self.groundY) {
self.y = self.groundY;
self.velocity.y = 0;
self.isJumping = false;
self.canDoubleJump = false;
self.hasDoubleJumped = false;
self.canTripleJump = false;
}
// Update cube rotation when moving
if (gameRunning && !self.isDead) {
self.currentRotation += self.rotationSpeed;
cube.rotation = self.currentRotation;
}
// Create trail particles
if (gameRunning && LK.ticks % 5 === 0) {
var particle = LK.getAsset('particle', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x - 30,
y: self.y + (self.isSliding ? 25 : 0)
});
particle.alpha = 0.7;
particle.life = 30;
game.addChild(particle);
self.particles.push(particle);
}
// Update particles
for (var i = self.particles.length - 1; i >= 0; i--) {
var p = self.particles[i];
p.alpha -= 0.02;
p.scaleX -= 0.02;
p.scaleY -= 0.02;
p.life--;
if (p.life <= 0 || p.alpha <= 0) {
p.destroy();
self.particles.splice(i, 1);
}
}
};
return self;
});
var Shadow = Container.expand(function () {
var self = Container.call(this);
var shadowGraphic = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.width = shadowGraphic.width;
self.height = shadowGraphic.height;
self.speed = 10;
self.update = function () {
self.x -= self.speed;
// Remove if off screen
if (self.x < -self.width) {
self.destroy();
return true; // Signal to remove from the array
}
return false;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// Game state
var gameRunning = false;
var speed = 10;
var score = 0;
var highScore = storage.highScore || 0;
var platformY = 2200;
var spawnDistance = 800;
var nextSpawnX = 2048 + 200;
var difficultyTimer = 0;
var difficultyInterval = 1000;
var lastTouchX = 0;
var lastTouchY = 0;
var touchStartTime = 0;
// Game elements
var player;
var platforms = [];
var obstacles = [];
var shadows = [];
var deathParticles = [];
var enemies = [];
var enemyCubes = [];
// UI elements
var scoreTxt;
var highScoreTxt;
var healthTxt;
var tapToStartTxt;
// Initialize the game
function initGame() {
gameRunning = false;
speed = 10;
score = 0;
platformY = 2200;
spawnDistance = 800;
nextSpawnX = 2048 + 200;
difficultyTimer = 0;
// Clear existing elements
while (platforms.length > 0) {
platforms[0].destroy();
platforms.shift();
}
while (obstacles.length > 0) {
obstacles[0].destroy();
obstacles.shift();
}
while (shadows.length > 0) {
shadows[0].destroy();
shadows.shift();
}
while (deathParticles.length > 0) {
deathParticles[0].destroy();
deathParticles.shift();
}
// Clear enemies and enemy cubes
while (enemies.length > 0) {
enemies[0].destroy();
enemies.shift();
}
while (enemyCubes.length > 0) {
enemyCubes[0].destroy();
enemyCubes.shift();
}
// Add background
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
game.addChildAt(background, 0); // Add at index 0 to ensure it's at the back
// Create player
player = new Player();
player.x = 400;
player.y = platformY; // Position player on top edge of platform
game.addChild(player);
// Create initial platform
createPlatform(0, platformY, 2048 + 500);
// Create UI
if (!scoreTxt) {
scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreTxt);
scoreTxt.x = -250;
scoreTxt.y = 50;
}
// Create health display
healthTxt = new Text2('Health: 3', {
size: 60,
fill: 0xFF5555
});
healthTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthTxt);
healthTxt.x = 120; // Keep away from top-left corner menu icon
healthTxt.y = 50;
if (!highScoreTxt) {
highScoreTxt = new Text2('High Score: ' + highScore, {
size: 40,
fill: 0xAAAAAA
});
highScoreTxt.anchor.set(0, 0);
LK.gui.topRight.addChild(highScoreTxt);
highScoreTxt.x = -250;
highScoreTxt.y = 120;
}
// Create start instruction
tapToStartTxt = new Text2('Tap to Start\nTap to Jump\nSwipe Right to Slide Faster', {
size: 70,
fill: 0xFFFFFF
});
tapToStartTxt.anchor.set(0.5, 0.5);
LK.gui.center.addChild(tapToStartTxt);
// Start the background music
LK.playMusic('gameBgm', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
// Create a platform at the specified position
function createPlatform(x, y, width) {
var platform = new Platform();
platform.x = x;
platform.y = y;
if (width) {
platform.width = width;
platform.getChildAt(0).width = width;
}
game.addChild(platform);
platforms.push(platform);
return platform;
}
// Create an obstacle
function createObstacle(type, x, y) {
var obstacle = new Obstacle(type);
obstacle.x = x;
obstacle.y = y;
if (type === 'laser') {
obstacle.y = Math.random() * 1500 + 500;
}
game.addChild(obstacle);
obstacles.push(obstacle);
return obstacle;
}
// Create a shadow area
function createShadow(x, y) {
var shadow = new Shadow();
shadow.x = x;
shadow.y = y;
game.addChild(shadow);
shadows.push(shadow);
return shadow;
}
// Create an enemy
function createEnemy(x, y) {
var enemy = new Enemy();
enemy.x = x;
enemy.y = y;
game.addChild(enemy);
enemies.push(enemy);
return enemy;
}
// Create an enemy cube
function createEnemyCube(x, y) {
var cube = new EnemyCube();
cube.x = x;
cube.y = y;
// Calculate direction vector towards player
var dx = player.x - x;
var dy = player.y - y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Normalize and set speed
cube.velocity.x = dx / distance * 12;
cube.velocity.y = dy / distance * 12;
game.addChild(cube);
enemyCubes.push(cube);
// Remove any spike obstacles that are at the bottom when enemy cubes are spawned
for (var i = obstacles.length - 1; i >= 0; i--) {
if (obstacles[i].type === 'spike' && obstacles[i].y === platformY) {
obstacles[i].destroy();
obstacles.splice(i, 1);
}
}
return cube;
}
// Spawn game elements
function spawnElements() {
if (nextSpawnX <= 2048 + 200) {
// Decide what to spawn
var rand = Math.random();
// Always spawn a platform
var platform = createPlatform(nextSpawnX, platformY);
// Maybe spawn an obstacle
if (rand < 0.7) {
// Only spawn spikes if there are no enemy cubes
if (rand < 0.4 && enemyCubes.length === 0) {
// Randomly decide between regular spike and green spike
if (Math.random() < 0.5) {
// Spawn regular spike
createObstacle('spike', nextSpawnX, platformY);
} else {
// Spawn green spike
createObstacle('greenSpike', nextSpawnX, platformY);
}
} else if (rand < 0.6) {
// Spawn searchlight
createObstacle('searchlight', nextSpawnX, 0);
} else {
// Spawn laser
createObstacle('laser', 0, 0);
}
}
// Maybe spawn a shadow
if (Math.random() < 0.3) {
createShadow(nextSpawnX, platformY - 40);
}
// Maybe spawn an enemy at the top
if (Math.random() < 0.2) {
var enemyX = nextSpawnX + Math.random() * 300;
var enemyY = 200 + Math.random() * 300;
createEnemy(enemyX, enemyY);
}
nextSpawnX += spawnDistance;
}
nextSpawnX -= speed;
}
// Check for collisions
function checkCollisions() {
if (player.isDead) {
return;
}
// Check obstacle collisions
for (var i = 0; i < obstacles.length; i++) {
var obstacle = obstacles[i];
if (obstacle.type === 'laser' && !obstacle.active) {
continue; // Skip inactive lasers
}
if (player.intersects(obstacle)) {
// If in a shadow and it's a searchlight, you're safe
if (obstacle.type === 'searchlight' && player.isHiding) {
continue;
}
player.die();
break;
}
}
// Check enemy cube collisions
for (var i = enemyCubes.length - 1; i >= 0; i--) {
if (player.intersects(enemyCubes[i])) {
player.die();
// Remove the enemy cube that hit the player
enemyCubes[i].destroy();
enemyCubes.splice(i, 1);
// Update health display
if (healthTxt) {
healthTxt.setText('Health: ' + Math.max(0, player.health));
}
break;
}
}
// Check shadow interactions
var inShadow = false;
for (var j = 0; j < shadows.length; j++) {
if (player.intersects(shadows[j])) {
inShadow = true;
break;
}
}
if (inShadow) {
player.hide();
} else {
player.unhide();
}
}
// Update score
function updateScore() {
if (!gameRunning || player.isDead) {
return;
}
score++;
if (score > highScore) {
highScore = score;
storage.highScore = highScore;
highScoreTxt.setText('High Score: ' + highScore);
}
if (score % 10 === 0) {
scoreTxt.setText('Score: ' + score);
}
}
// Increase difficulty over time
function updateDifficulty() {
difficultyTimer++;
if (difficultyTimer >= difficultyInterval) {
difficultyTimer = 0;
// Increase speed
speed += 0.5;
for (var i = 0; i < platforms.length; i++) {
platforms[i].speed = speed;
}
for (var j = 0; j < obstacles.length; j++) {
obstacles[j].speed = speed;
}
for (var k = 0; k < shadows.length; k++) {
shadows[k].speed = speed;
}
for (var e = 0; e < enemies.length; e++) {
enemies[e].speed = speed * 0.7; // Enemies move a bit slower than obstacles
}
// Decrease spawn distance
spawnDistance = Math.max(400, spawnDistance - 10);
}
}
// Main update function
game.update = function () {
if (gameRunning) {
// Update game elements
player.updatePhysics();
// Spawn new elements
spawnElements();
// Update existing elements
for (var i = platforms.length - 1; i >= 0; i--) {
if (platforms[i].update()) {
platforms.splice(i, 1);
}
}
for (var j = obstacles.length - 1; j >= 0; j--) {
if (obstacles[j].update()) {
obstacles.splice(j, 1);
}
}
for (var k = shadows.length - 1; k >= 0; k--) {
if (shadows[k].update()) {
shadows.splice(k, 1);
}
}
// Update enemies
for (var m = enemies.length - 1; m >= 0; m--) {
if (enemies[m].update()) {
enemies.splice(m, 1);
}
}
// Update enemy cubes
for (var n = enemyCubes.length - 1; n >= 0; n--) {
if (enemyCubes[n].update()) {
enemyCubes.splice(n, 1);
}
}
// Update death particles
for (var l = deathParticles.length - 1; l >= 0; l--) {
var p = deathParticles[l];
p.x += p.vx;
p.y += p.vy;
p.vx *= 0.95;
p.vy *= 0.95;
p.vy += 0.5; // gravity
p.alpha -= 0.02;
if (p.alpha <= 0) {
p.destroy();
deathParticles.splice(l, 1);
}
}
// Check for collisions
checkCollisions();
// Update score (every 10 ticks)
if (LK.ticks % 10 === 0) {
updateScore();
}
// Update difficulty
updateDifficulty();
}
};
// Event handlers
game.down = function (x, y, obj) {
// Track touch position and time for swipe detection
lastTouchX = x;
lastTouchY = y;
touchStartTime = Date.now();
if (!gameRunning) {
// Start the game
gameRunning = true;
tapToStartTxt.destroy();
scoreTxt.setText('Score: 0');
} else if (!player.isDead) {
player.jump();
}
};
// Sliding functionality has been removed
game.up = function (x, y, obj) {
if (gameRunning && !player.isDead) {
var dx = x - lastTouchX;
var dy = y - lastTouchY;
var distance = Math.sqrt(dx * dx + dy * dy);
var time = Date.now() - touchStartTime;
// Add swipe property to event if this appears to be a swipe
if (distance > 50 && time < 300) {
// This is a swipe, create a custom property on the event object
obj.event = obj.event || {};
obj.event.isSwipe = true;
// If it's a right swipe (simplified check for better responsiveness)
if (dx > 50) {
player.slide();
}
}
}
};
game.move = function (x, y, obj) {
if (gameRunning && !player.isDead) {
// We'll let the up handler manage all slide triggers
// This prevents double-triggering slides
}
};
// Initialize the game
initGame();
square re cube with 2 eyes and spike teeths inside In-Game asset. 2d. High contrast. No shadows
square white cube with 2 eyes and spike smily teeths inside In-Game asset. 2d. High contrast. No shadows
square yellowgreen cube with 2 eyes and smily face inside In-Game asset. 2d. High contrast. No shadows
background image for sqube darkness game. In-Game asset. 2d. High contrast. No shadows