/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BouncingWall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('bouncingWall', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveSpeed = 2;
self.isVertical = Math.random() > 0.5;
self.direction = Math.random() > 0.5 ? 1 : -1;
if (!self.isVertical) {
wallGraphics.rotation = Math.PI / 2;
}
self.update = function () {
var tempMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
if (self.isVertical) {
self.x += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.x <= mazeLeft + 50 || self.x >= mazeRight - 50) {
self.direction *= -1;
}
} else {
self.y += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.y <= mazeTop + 50 || self.y >= mazeBottom - 50) {
self.direction *= -1;
}
}
};
return self;
});
var LaserWall = Container.expand(function () {
var self = Container.call(this);
var laserGraphics = self.attachAsset('laserWall', {
anchorX: 0.5,
anchorY: 0.5
});
self.isVertical = Math.random() > 0.5;
if (!self.isVertical) {
laserGraphics.rotation = Math.PI / 2;
}
self.moveSpeed = 3;
self.direction = Math.random() > 0.5 ? 1 : -1;
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
var tempMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
if (self.isVertical) {
self.x += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.x <= mazeLeft + 10 || self.x >= mazeRight - 10) {
self.direction *= -1;
}
} else {
self.y += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.y <= mazeTop + 10 || self.y >= mazeBottom - 10) {
self.direction *= -1;
}
}
// Pulsing effect
var pulse = Math.sin(LK.ticks * 0.15) * 0.3 + 0.7;
laserGraphics.alpha = pulse;
};
return self;
});
var MagneticOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('magneticOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.magneticRange = 200;
self.pulseTimer = 0;
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
// Pulsing magnetic effect
self.pulseTimer++;
var pulse = Math.sin(self.pulseTimer * 0.1) * 0.3 + 1;
orbGraphics.scaleX = pulse;
orbGraphics.scaleY = pulse;
// Electrical arc effect
if (LK.ticks % 20 === 0) {
var electricityTint = Math.random() > 0.5 ? 0xffffff : 0x9966ff;
orbGraphics.tint = electricityTint;
var self_ref = self;
LK.setTimeout(function () {
if (orbGraphics.parent) orbGraphics.tint = 0xffffff;
}, 100);
}
// Apply magnetic force to player if within range
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.magneticRange && distance > 0) {
var force = 0.8 * (1 - distance / self.magneticRange);
var forceX = dx / distance * force;
var forceY = dy / distance * force;
// Pull player toward orb
player.x -= forceX * 2;
player.y -= forceY * 2;
}
};
return self;
});
var MultiplierBoost = Container.expand(function () {
var self = Container.call(this);
var boostGraphics = self.attachAsset('multiplierBoost', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 250; // 4.2 seconds
self.floatOffset = 0;
self.update = function () {
self.lifeTimer--;
self.floatOffset++;
// Floating motion
boostGraphics.y = Math.sin(self.floatOffset * 0.1) * 5;
// Golden glow effect
var glow = Math.sin(LK.ticks * 0.2) * 0.3 + 1;
boostGraphics.scaleX = glow;
boostGraphics.scaleY = glow;
if (self.lifeTimer <= 0) {
self.destroy();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.moveDirection = {
x: 0,
y: 0
};
self.shieldActive = false;
self.shieldTimer = 0;
self.update = function () {
// Apply movement
self.x += self.moveDirection.x * self.speed * gameSpeed;
self.y += self.moveDirection.y * self.speed * gameSpeed;
// Handle shield timer
if (self.shieldActive) {
self.shieldTimer--;
if (self.shieldTimer <= 0) {
self.shieldActive = false;
playerGraphics.tint = 0xffffff;
}
}
// Movement animation - bounce effect when moving
if (self.moveDirection.x !== 0 || self.moveDirection.y !== 0) {
var bounce = Math.sin(LK.ticks * 0.4) * 0.1 + 1;
playerGraphics.scaleX = bounce;
playerGraphics.scaleY = bounce;
} else {
playerGraphics.scaleX = 1;
playerGraphics.scaleY = 1;
}
// Keep player within maze bounds
var padding = 30;
if (self.x < mazeLeft + padding) self.x = mazeLeft + padding;
if (self.x > mazeRight - padding) self.x = mazeRight - padding;
if (self.y < mazeTop + padding) self.y = mazeTop + padding;
if (self.y > mazeBottom - padding) self.y = mazeBottom - padding;
// Create trail effect
if (LK.ticks % 3 === 0) {
// Create trail every 3 ticks
var trail = game.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.5,
tint: 0x00ff00
}));
trails.push(trail);
// Tween trail to fade and shrink
tween(trail, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
trail.destroy();
var index = trails.indexOf(trail);
if (index > -1) trails.splice(index, 1);
}
});
}
};
self.activateShield = function () {
self.shieldActive = true;
self.shieldTimer = 60; // 1 second at 60fps
playerGraphics.tint = 0xffaa00;
};
return self;
});
var Powerup = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
var powerupGraphics = self.attachAsset(type === 'slow' ? 'slowPowerup' : 'shieldPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 300; // 5 seconds at 60fps
self.pulseTimer = 0;
self.update = function () {
self.lifeTimer--;
self.pulseTimer++;
// Pulsing effect
var scale = 1 + Math.sin(self.pulseTimer * 0.2) * 0.2;
powerupGraphics.scaleX = scale;
powerupGraphics.scaleY = scale;
if (self.lifeTimer <= 0) {
self.destroy();
}
};
return self;
});
var SawBlade = Container.expand(function () {
var self = Container.call(this);
var sawGraphics = self.attachAsset('sawBlade', {
anchorX: 0.5,
anchorY: 0.5
});
self.rotationSpeed = 0.2;
self.moveSpeed = 2;
self.nearMissTimer = 0;
self.direction = {
x: Math.random() - 0.5,
y: Math.random() - 0.5
};
var length = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y);
self.direction.x /= length;
self.direction.y /= length;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
sawGraphics.rotation += self.rotationSpeed;
// Move
var tempMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
self.x += self.direction.x * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
self.y += self.direction.y * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
// Bounce off maze walls
if (self.x <= mazeLeft + 40 || self.x >= mazeRight - 40) {
self.direction.x *= -1;
}
if (self.y <= mazeTop + 40 || self.y >= mazeBottom - 40) {
self.direction.y *= -1;
}
// Keep within bounds
self.x = Math.max(mazeLeft + 40, Math.min(mazeRight - 40, self.x));
self.y = Math.max(mazeTop + 40, Math.min(mazeBottom - 40, self.y));
};
return self;
});
var SpikeTrap = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spikeTrap', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 180; // 3 seconds at 60fps
self.isActive = true;
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
self.lifeTimer--;
// Blink when about to disappear
if (self.lifeTimer < 60) {
spikeGraphics.alpha = Math.sin(self.lifeTimer * 0.3) * 0.5 + 0.5;
}
if (self.lifeTimer <= 0) {
self.isActive = false;
}
};
return self;
});
var TimePowerup = Container.expand(function () {
var self = Container.call(this);
var timeGraphics = self.attachAsset('timePowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 400; // 6.7 seconds
self.rotationSpeed = 0.05;
self.update = function () {
self.lifeTimer--;
timeGraphics.rotation += self.rotationSpeed;
// Sparkle effect
if (LK.ticks % 15 === 0) {
var sparkle = Math.random() > 0.5 ? 0xffffff : 0xff66ff;
timeGraphics.tint = sparkle;
var self_ref = self;
LK.setTimeout(function () {
if (timeGraphics.parent) timeGraphics.tint = 0xffffff;
}, 200);
}
if (self.lifeTimer <= 0) {
self.destroy();
}
};
return self;
});
var Turret = Container.expand(function () {
var self = Container.call(this);
var turretGraphics = self.attachAsset('turret', {
anchorX: 0.5,
anchorY: 0.5
});
self.fireTimer = 0;
self.bullets = [];
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
// Slowly rotate toward player
var dx = player.x - self.x;
var dy = player.y - self.y;
var targetAngle = Math.atan2(dy, dx);
var currentAngle = turretGraphics.rotation;
var angleDiff = targetAngle - currentAngle;
// Normalize angle difference
while (angleDiff > Math.PI) angleDiff -= 2 * Math.PI;
while (angleDiff < -Math.PI) angleDiff += 2 * Math.PI;
// Slowly rotate toward target
turretGraphics.rotation += angleDiff * 0.05;
// Fire bullets every 120 ticks (2 seconds)
self.fireTimer++;
if (self.fireTimer >= 120) {
self.fireTimer = 0;
var bullet = game.addChild(LK.getAsset('laserWall', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
}));
// Set bullet direction toward player
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.speedX = dx / distance * 6 * hazardSpeedBoost;
bullet.speedY = dy / distance * 6 * hazardSpeedBoost;
bullet.lifeTimer = 180; // 3 seconds
self.bullets.push(bullet);
}
// Update bullets
for (var i = self.bullets.length - 1; i >= 0; i--) {
var bullet = self.bullets[i];
var speedMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
bullet.x += bullet.speedX * gameSpeed * speedMultiplier;
bullet.y += bullet.speedY * gameSpeed * speedMultiplier;
bullet.lifeTimer--;
if (bullet.lifeTimer <= 0 || bullet.x < mazeLeft - 50 || bullet.x > mazeRight + 50 || bullet.y < mazeTop - 50 || bullet.y > mazeBottom + 50) {
bullet.destroy();
self.bullets.splice(i, 1);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// Game variables
var player;
var walls = [];
var hazards = [];
var powerups = [];
var gameSpeed = 1;
var slowMotionTimer = 0;
var scoreMultiplier = 1;
var lastMultiplierIncrease = 0;
var hazardSpawnTimer = 0;
var powerupSpawnTimer = 0;
var hazardSpeedBoost = 1;
var tempSpeedBoostTimer = 0;
var trails = [];
var explosionParticles = [];
var timeSlowActive = false;
var timeSlowDuration = 0;
var magneticOrbs = [];
var achievementTimer = 0;
var comboCounter = 0;
var lastComboTime = 0;
// Persistent game statistics
var gameStats = {
totalGamesPlayed: storage.totalGamesPlayed || 0,
totalScore: storage.totalScore || 0,
bestMultiplier: storage.bestMultiplier || 1,
totalHazardsDestroyed: storage.totalHazardsDestroyed || 0,
achievementsUnlocked: storage.achievementsUnlocked || []
};
// Achievement definitions
var achievements = [{
id: 'first_game',
name: 'First Steps',
desc: 'Play your first game',
target: 1,
stat: 'totalGamesPlayed'
}, {
id: 'score_1000',
name: 'Getting Started',
desc: 'Reach 1000 points',
target: 1000,
stat: 'score'
}, {
id: 'score_5000',
name: 'Skilled Player',
desc: 'Reach 5000 points',
target: 5000,
stat: 'score'
}, {
id: 'multiplier_5',
name: 'Combo Master',
desc: 'Reach 5x multiplier',
target: 5,
stat: 'multiplier'
}, {
id: 'survive_180',
name: 'Survivor',
desc: 'Survive 3 minutes',
target: 180,
stat: 'time'
}, {
id: 'destroy_50',
name: 'Destroyer',
desc: 'Destroy 50 hazards total',
target: 50,
stat: 'totalHazardsDestroyed'
}];
function checkAchievements() {
var currentStats = {
totalGamesPlayed: gameStats.totalGamesPlayed,
score: LK.getScore(),
multiplier: scoreMultiplier,
time: Math.floor(LK.ticks / 60),
totalHazardsDestroyed: gameStats.totalHazardsDestroyed
};
for (var i = 0; i < achievements.length; i++) {
var achievement = achievements[i];
if (gameStats.achievementsUnlocked.indexOf(achievement.id) === -1) {
if (currentStats[achievement.stat] >= achievement.target) {
gameStats.achievementsUnlocked.push(achievement.id);
storage.achievementsUnlocked = gameStats.achievementsUnlocked;
// Achievement notification
LK.getSound('achievement').play();
showAchievementPopup(achievement.name, achievement.desc);
}
}
}
}
function showAchievementPopup(name, desc) {
var popup = game.addChild(LK.getAsset('shieldPowerup', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 200,
scaleX: 2,
scaleY: 2,
tint: 0xFFD700
}));
tween(popup, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 3000,
onFinish: function onFinish() {
popup.destroy();
}
});
}
// Maze boundaries
var mazeLeft = 200;
var mazeRight = 1848;
var mazeTop = 200;
var mazeBottom = 2532;
var shrinkSpeed = 0.35;
// UI
var scoreTxt = new Text2('0', {
size: 80,
fill: 0x00FF41
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var multiplierTxt = new Text2('x1', {
size: 60,
fill: 0xFFAA00
});
multiplierTxt.anchor.set(1, 0);
multiplierTxt.y = 100;
LK.gui.topRight.addChild(multiplierTxt);
// High score display
var highScoreTxt = new Text2('HS: 0', {
size: 60,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(highScoreTxt);
// Initialize high score display
highScoreTxt.setText('HS: ' + (storage.highScore || 0));
// Create player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Create initial maze walls
function createMazeWalls() {
// Clear existing walls
for (var i = 0; i < walls.length; i++) {
walls[i].destroy();
}
walls = [];
// Top wall
for (var x = mazeLeft - 50; x <= mazeRight + 50; x += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: mazeTop - 50
}));
walls.push(wall);
}
// Bottom wall
for (var x = mazeLeft - 50; x <= mazeRight + 50; x += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: mazeBottom + 50
}));
walls.push(wall);
}
// Left wall
for (var y = mazeTop - 50; y <= mazeBottom + 50; y += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: mazeLeft - 50,
y: y
}));
walls.push(wall);
}
// Right wall
for (var y = mazeTop - 50; y <= mazeBottom + 50; y += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: mazeRight + 50,
y: y
}));
walls.push(wall);
}
}
// Initial wall creation
createMazeWalls();
// Touch controls
var touchStartX = 0;
var touchStartY = 0;
var isDragging = false;
game.down = function (x, y, obj) {
touchStartX = x;
touchStartY = y;
isDragging = true;
};
game.move = function (x, y, obj) {
if (!isDragging) return;
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
var threshold = 30;
player.moveDirection.x = 0;
player.moveDirection.y = 0;
if (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
player.moveDirection.x = deltaX > 0 ? 1 : -1;
} else {
player.moveDirection.y = deltaY > 0 ? 1 : -1;
}
}
};
game.up = function (x, y, obj) {
isDragging = false;
player.moveDirection.x = 0;
player.moveDirection.y = 0;
};
// Spawn hazards
function spawnHazard() {
var hazardType;
// Advanced hazard spawning logic based on multiplier
if (scoreMultiplier >= 5 && Math.random() < 0.15) {
hazardType = 5; // MagneticOrb
} else if (scoreMultiplier >= 3 && Math.random() < 0.2) {
hazardType = 4; // BouncingWall
} else {
hazardType = Math.floor(Math.random() * 4);
}
var hazard;
if (hazardType === 0) {
// Saw blade
hazard = new SawBlade();
hazard.x = mazeLeft + Math.random() * (mazeRight - mazeLeft);
hazard.y = mazeTop + Math.random() * (mazeBottom - mazeTop);
} else if (hazardType === 1) {
// Laser wall
hazard = new LaserWall();
if (hazard.isVertical) {
hazard.x = mazeLeft + Math.random() * (mazeRight - mazeLeft);
hazard.y = (mazeTop + mazeBottom) / 2;
} else {
hazard.x = (mazeLeft + mazeRight) / 2;
hazard.y = mazeTop + Math.random() * (mazeBottom - mazeTop);
}
} else if (hazardType === 2) {
// Spike trap
hazard = new SpikeTrap();
hazard.x = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
hazard.y = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
} else if (hazardType === 3) {
// Turret
hazard = new Turret();
hazard.x = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
hazard.y = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
} else if (hazardType === 4) {
// Bouncing wall
hazard = new BouncingWall();
if (hazard.isVertical) {
hazard.x = mazeLeft + Math.random() * (mazeRight - mazeLeft);
hazard.y = (mazeTop + mazeBottom) / 2;
} else {
hazard.x = (mazeLeft + mazeRight) / 2;
hazard.y = mazeTop + Math.random() * (mazeBottom - mazeTop);
}
} else if (hazardType === 5) {
// Magnetic Orb
hazard = new MagneticOrb();
hazard.x = mazeLeft + 120 + Math.random() * (mazeRight - mazeLeft - 240);
hazard.y = mazeTop + 120 + Math.random() * (mazeBottom - mazeTop - 240);
magneticOrbs.push(hazard);
}
hazards.push(hazard);
game.addChild(hazard);
}
// Spawn powerup
function spawnPowerup() {
var rand = Math.random();
var powerup;
if (rand < 0.4) {
powerup = new Powerup('slow');
} else if (rand < 0.7) {
powerup = new Powerup('shield');
} else if (rand < 0.85) {
powerup = new TimePowerup();
} else {
powerup = new MultiplierBoost();
}
powerup.x = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
powerup.y = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
powerups.push(powerup);
game.addChild(powerup);
}
// Play background music
LK.playMusic('bgmusic');
// Main game loop
game.update = function () {
// Handle slow motion
if (slowMotionTimer > 0) {
slowMotionTimer--;
gameSpeed = 0.3;
if (slowMotionTimer <= 0) {
gameSpeed = 1;
}
}
// Handle time slow effect
if (timeSlowDuration > 0) {
timeSlowDuration--;
gameSpeed = 0.1; // Much slower than regular slow motion
hazardSpeedBoost *= 0.5; // Reduce hazard speed as well
if (timeSlowDuration <= 0) {
timeSlowActive = false;
gameSpeed = 1;
hazardSpeedBoost = Math.max(1, hazardSpeedBoost * 2); // Restore hazard speed
}
}
// Emergency teleportation (double tap detection simulation)
if (LK.ticks % 180 === 0 && Math.random() < 0.03) {
// Rare random teleportation for demo
// Find safe teleportation spot
var attempts = 0;
var newX, newY, safe;
do {
newX = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
newY = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
safe = true;
// Check if position is safe from hazards
for (var h = 0; h < hazards.length; h++) {
var dx = newX - hazards[h].x;
var dy = newY - hazards[h].y;
if (Math.sqrt(dx * dx + dy * dy) < 120) {
safe = false;
break;
}
}
attempts++;
} while (!safe && attempts < 10);
if (safe) {
LK.getSound('teleport').play();
// Teleportation effect
var oldX = player.x,
oldY = player.y;
tween(player, {
alpha: 0
}, {
duration: 100,
onFinish: function onFinish() {
player.x = newX;
player.y = newY;
tween(player, {
alpha: 1
}, {
duration: 100
});
}
});
}
}
// Achievement checking
achievementTimer++;
if (achievementTimer >= 60) {
// Check every second
checkAchievements();
achievementTimer = 0;
}
// Handle temp speed boost timer
if (tempSpeedBoostTimer > 0) {
tempSpeedBoostTimer--;
}
// Rhythmic difficulty every 60 seconds (3600 ticks)
if (LK.ticks > 0 && LK.ticks % 3600 === 0) {
hazardSpeedBoost *= 1.2;
// Make all laser walls nearly invisible for 1 second
for (var i = 0; i < hazards.length; i++) {
var hazard = hazards[i];
if (hazard.constructor === LaserWall) {
var laserGraphics = hazard.getChildAt(0);
tween(laserGraphics, {
alpha: 0.05
}, {
duration: 1000,
onFinish: function onFinish() {
// Resume normal pulsing after 1 second
}
});
}
}
}
// Shrink maze
mazeLeft += shrinkSpeed * gameSpeed;
mazeRight -= shrinkSpeed * gameSpeed;
mazeTop += shrinkSpeed * gameSpeed;
mazeBottom -= shrinkSpeed * gameSpeed;
// Recreate walls periodically
if (LK.ticks % 30 === 0) {
createMazeWalls();
}
// Check if maze is too small (game over condition)
if (mazeRight - mazeLeft < 200 || mazeBottom - mazeTop < 200) {
// Update all persistent statistics
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
// Update game statistics
gameStats.totalGamesPlayed++;
gameStats.totalScore += currentScore;
gameStats.bestMultiplier = Math.max(gameStats.bestMultiplier, scoreMultiplier);
// Save to storage
storage.totalGamesPlayed = gameStats.totalGamesPlayed;
storage.totalScore = gameStats.totalScore;
storage.bestMultiplier = gameStats.bestMultiplier;
storage.totalHazardsDestroyed = gameStats.totalHazardsDestroyed;
// Final achievement check
checkAchievements();
LK.showGameOver();
return;
}
// Update score
LK.setScore(Math.floor(LK.ticks / 60 * scoreMultiplier));
scoreTxt.setText(LK.getScore().toString());
// Increase multiplier every 30 seconds
var currentTime = Math.floor(LK.ticks / 60);
if (currentTime - lastMultiplierIncrease >= 30) {
scoreMultiplier++;
lastMultiplierIncrease = currentTime;
multiplierTxt.setText('x' + scoreMultiplier);
// Multiplier reward animation
tween(multiplierTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(multiplierTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
// Spawn hazards
hazardSpawnTimer++;
var spawnRate = Math.max(60, 180 - scoreMultiplier * 20);
if (hazardSpawnTimer >= spawnRate) {
spawnHazard();
hazardSpawnTimer = 0;
}
// Spawn powerups (rare)
powerupSpawnTimer++;
if (powerupSpawnTimer >= 600 && Math.random() < 0.3) {
// Every 10 seconds, 30% chance
spawnPowerup();
powerupSpawnTimer = 0;
}
// Check collision with walls
if (!player.shieldActive) {
if (player.x <= mazeLeft + 30 || player.x >= mazeRight - 30 || player.y <= mazeTop + 30 || player.y >= mazeBottom - 30) {
// Update high score
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.getSound('collision').play();
LK.showGameOver();
return;
}
}
// Update and check hazard collisions
for (var i = hazards.length - 1; i >= 0; i--) {
var hazard = hazards[i];
if (hazard.isActive === false) {
hazard.destroy();
hazards.splice(i, 1);
continue;
}
// Check collision with turret bullets
if (hazard.bullets) {
for (var j = hazard.bullets.length - 1; j >= 0; j--) {
var bullet = hazard.bullets[j];
if (player.intersects(bullet)) {
if (player.shieldActive) {
// Shield absorbs the hit
player.shieldActive = false;
// Reset multiplier and timer
scoreMultiplier = 1;
lastMultiplierIncrease = Math.floor(LK.ticks / 60);
multiplierTxt.setText('x' + scoreMultiplier);
// Flash multiplier text red then back to orange
tween(multiplierTxt, {
tint: 0xFF0000
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(multiplierTxt, {
tint: 0xFFAA00
}, {
duration: 100,
easing: tween.linear
});
}
});
// Stop any existing tweens on player graphics
tween.stop(player.getChildAt(0), {
tint: true
});
// Create sequential white/yellow blink animation
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffff00
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear
});
}
});
}
});
bullet.destroy();
hazard.bullets.splice(j, 1);
} else {
// Update high score before game over
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.getSound('collision').play();
LK.showGameOver();
return;
}
}
}
}
// Special handling for BouncingWall
if (hazard.constructor === BouncingWall && player.intersects(hazard)) {
// Calculate push force
var dx = player.x - hazard.x;
var dy = player.y - hazard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var forceX = dx / distance * 150;
var forceY = dy / distance * 150;
player.x += forceX;
player.y += forceY;
}
// Score penalty
var newScore = Math.max(0, LK.getScore() - 50);
LK.setScore(newScore);
scoreTxt.setText(LK.getScore().toString());
// Blue flash effect
LK.getSound('collision').play();
var playerGraphics = player.getChildAt(0);
tween(playerGraphics, {
tint: 0x0000FF
}, {
duration: 100,
onFinish: function onFinish() {
tween(playerGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
} else if (player.intersects(hazard)) {
if (player.shieldActive) {
// Shield absorbs the hit
player.shieldActive = false;
// Reset multiplier and timer
scoreMultiplier = 1;
lastMultiplierIncrease = Math.floor(LK.ticks / 60);
multiplierTxt.setText('x' + scoreMultiplier);
// Flash multiplier text red then back to orange
tween(multiplierTxt, {
tint: 0xFF0000
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(multiplierTxt, {
tint: 0xFFAA00
}, {
duration: 100,
easing: tween.linear
});
}
});
// Stop any existing tweens on player graphics
tween.stop(player.getChildAt(0), {
tint: true
});
// Create sequential white/yellow blink animation
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffff00
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear
});
}
});
}
});
} else {
// Create explosion effect before game over
for (var p = 0; p < 12; p++) {
var particle = game.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
x: player.x,
y: player.y,
scaleX: 0.2,
scaleY: 0.2,
tint: Math.random() > 0.5 ? 0xFF0000 : 0xFFFF00
}));
var speedX = (Math.random() - 0.5) * 20;
var speedY = (Math.random() - 0.5) * 20;
explosionParticles.push(particle);
tween(particle, {
x: particle.x + speedX,
y: particle.y + speedY,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
particle.destroy();
var index = explosionParticles.indexOf(particle);
if (index > -1) explosionParticles.splice(index, 1);
}
});
}
// Update high score before game over
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.getSound('collision').play();
LK.showGameOver();
return;
}
}
}
// Update and check powerup collisions
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (player.intersects(powerup)) {
LK.getSound('powerup').play();
if (powerup.type === 'slow') {
slowMotionTimer = 180; // 3 seconds
} else if (powerup.type === 'shield') {
player.activateShield();
} else if (powerup.constructor === TimePowerup) {
// Ultra slow motion effect
timeSlowActive = true;
timeSlowDuration = 240; // 4 seconds
} else if (powerup.constructor === MultiplierBoost) {
// Double score multiplier temporarily
scoreMultiplier += 2;
multiplierTxt.setText('x' + scoreMultiplier);
// Bonus score animation
tween(multiplierTxt, {
scaleX: 2,
scaleY: 2,
tint: 0xFFD700
}, {
duration: 300,
onFinish: function onFinish() {
tween(multiplierTxt, {
scaleX: 1,
scaleY: 1,
tint: 0xFFAA00
}, {
duration: 200
});
}
});
}
// Powerup negative effect: temporary speed boost to hazards (except for time powerups)
if (powerup.constructor !== TimePowerup) {
tempSpeedBoostTimer = 30; // 0.5 seconds
}
powerup.destroy();
powerups.splice(i, 1);
}
}
// Near miss detection and saw blade vs bullet collision
for (var i = 0; i < hazards.length; i++) {
var hazard = hazards[i];
// Near miss detection
if (hazard.nearMissTimer !== undefined && hazard.nearMissTimer <= 0) {
var dx = player.x - hazard.x;
var dy = player.y - hazard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collisionDistance = 50; // Approximate collision distance
if (distance > collisionDistance + 40 && distance < collisionDistance + 60) {
// Near miss detected
LK.getSound('nearmiss').play();
hazard.nearMissTimer = 60; // Cooldown
// White flash on player
var playerGraphics = player.getChildAt(0);
tween(playerGraphics, {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(playerGraphics, {
tint: 0x00ff00
}, {
duration: 50
});
}
});
}
}
// Saw blade vs bullet collision
if (hazard.constructor === SawBlade) {
for (var j = 0; j < hazards.length; j++) {
var otherHazard = hazards[j];
if (otherHazard.constructor === Turret && otherHazard.bullets) {
for (var k = otherHazard.bullets.length - 1; k >= 0; k--) {
var bullet = otherHazard.bullets[k];
if (hazard.intersects(bullet)) {
// Collision detected - destroy both
var currentTime = Math.floor(LK.ticks / 60);
// Combo system
if (currentTime - lastComboTime <= 2) {
// Within 2 seconds
comboCounter++;
} else {
comboCounter = 1;
}
lastComboTime = currentTime;
// Bonus points for combo
var bonusPoints = 10 * comboCounter;
LK.setScore(LK.getScore() + bonusPoints);
scoreTxt.setText(LK.getScore().toString());
// Update destruction statistics
gameStats.totalHazardsDestroyed++;
// Visual combo feedback
if (comboCounter > 1) {
var comboText = game.addChild(new Text2('x' + comboCounter + '!', {
size: 40,
fill: 0xFFD700
}));
comboText.x = hazard.x;
comboText.y = hazard.y;
comboText.anchor.set(0.5, 0.5);
tween(comboText, {
y: comboText.y - 60,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
comboText.destroy();
}
});
}
hazard.destroy();
hazards.splice(i, 1);
bullet.destroy();
otherHazard.bullets.splice(k, 1);
i--; // Adjust index since we removed an element
break;
}
}
}
}
}
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var BouncingWall = Container.expand(function () {
var self = Container.call(this);
var wallGraphics = self.attachAsset('bouncingWall', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveSpeed = 2;
self.isVertical = Math.random() > 0.5;
self.direction = Math.random() > 0.5 ? 1 : -1;
if (!self.isVertical) {
wallGraphics.rotation = Math.PI / 2;
}
self.update = function () {
var tempMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
if (self.isVertical) {
self.x += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.x <= mazeLeft + 50 || self.x >= mazeRight - 50) {
self.direction *= -1;
}
} else {
self.y += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.y <= mazeTop + 50 || self.y >= mazeBottom - 50) {
self.direction *= -1;
}
}
};
return self;
});
var LaserWall = Container.expand(function () {
var self = Container.call(this);
var laserGraphics = self.attachAsset('laserWall', {
anchorX: 0.5,
anchorY: 0.5
});
self.isVertical = Math.random() > 0.5;
if (!self.isVertical) {
laserGraphics.rotation = Math.PI / 2;
}
self.moveSpeed = 3;
self.direction = Math.random() > 0.5 ? 1 : -1;
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
var tempMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
if (self.isVertical) {
self.x += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.x <= mazeLeft + 10 || self.x >= mazeRight - 10) {
self.direction *= -1;
}
} else {
self.y += self.direction * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
if (self.y <= mazeTop + 10 || self.y >= mazeBottom - 10) {
self.direction *= -1;
}
}
// Pulsing effect
var pulse = Math.sin(LK.ticks * 0.15) * 0.3 + 0.7;
laserGraphics.alpha = pulse;
};
return self;
});
var MagneticOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('magneticOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.magneticRange = 200;
self.pulseTimer = 0;
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
// Pulsing magnetic effect
self.pulseTimer++;
var pulse = Math.sin(self.pulseTimer * 0.1) * 0.3 + 1;
orbGraphics.scaleX = pulse;
orbGraphics.scaleY = pulse;
// Electrical arc effect
if (LK.ticks % 20 === 0) {
var electricityTint = Math.random() > 0.5 ? 0xffffff : 0x9966ff;
orbGraphics.tint = electricityTint;
var self_ref = self;
LK.setTimeout(function () {
if (orbGraphics.parent) orbGraphics.tint = 0xffffff;
}, 100);
}
// Apply magnetic force to player if within range
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < self.magneticRange && distance > 0) {
var force = 0.8 * (1 - distance / self.magneticRange);
var forceX = dx / distance * force;
var forceY = dy / distance * force;
// Pull player toward orb
player.x -= forceX * 2;
player.y -= forceY * 2;
}
};
return self;
});
var MultiplierBoost = Container.expand(function () {
var self = Container.call(this);
var boostGraphics = self.attachAsset('multiplierBoost', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 250; // 4.2 seconds
self.floatOffset = 0;
self.update = function () {
self.lifeTimer--;
self.floatOffset++;
// Floating motion
boostGraphics.y = Math.sin(self.floatOffset * 0.1) * 5;
// Golden glow effect
var glow = Math.sin(LK.ticks * 0.2) * 0.3 + 1;
boostGraphics.scaleX = glow;
boostGraphics.scaleY = glow;
if (self.lifeTimer <= 0) {
self.destroy();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.moveDirection = {
x: 0,
y: 0
};
self.shieldActive = false;
self.shieldTimer = 0;
self.update = function () {
// Apply movement
self.x += self.moveDirection.x * self.speed * gameSpeed;
self.y += self.moveDirection.y * self.speed * gameSpeed;
// Handle shield timer
if (self.shieldActive) {
self.shieldTimer--;
if (self.shieldTimer <= 0) {
self.shieldActive = false;
playerGraphics.tint = 0xffffff;
}
}
// Movement animation - bounce effect when moving
if (self.moveDirection.x !== 0 || self.moveDirection.y !== 0) {
var bounce = Math.sin(LK.ticks * 0.4) * 0.1 + 1;
playerGraphics.scaleX = bounce;
playerGraphics.scaleY = bounce;
} else {
playerGraphics.scaleX = 1;
playerGraphics.scaleY = 1;
}
// Keep player within maze bounds
var padding = 30;
if (self.x < mazeLeft + padding) self.x = mazeLeft + padding;
if (self.x > mazeRight - padding) self.x = mazeRight - padding;
if (self.y < mazeTop + padding) self.y = mazeTop + padding;
if (self.y > mazeBottom - padding) self.y = mazeBottom - padding;
// Create trail effect
if (LK.ticks % 3 === 0) {
// Create trail every 3 ticks
var trail = game.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.5,
tint: 0x00ff00
}));
trails.push(trail);
// Tween trail to fade and shrink
tween(trail, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
trail.destroy();
var index = trails.indexOf(trail);
if (index > -1) trails.splice(index, 1);
}
});
}
};
self.activateShield = function () {
self.shieldActive = true;
self.shieldTimer = 60; // 1 second at 60fps
playerGraphics.tint = 0xffaa00;
};
return self;
});
var Powerup = Container.expand(function (type) {
var self = Container.call(this);
self.type = type;
var powerupGraphics = self.attachAsset(type === 'slow' ? 'slowPowerup' : 'shieldPowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 300; // 5 seconds at 60fps
self.pulseTimer = 0;
self.update = function () {
self.lifeTimer--;
self.pulseTimer++;
// Pulsing effect
var scale = 1 + Math.sin(self.pulseTimer * 0.2) * 0.2;
powerupGraphics.scaleX = scale;
powerupGraphics.scaleY = scale;
if (self.lifeTimer <= 0) {
self.destroy();
}
};
return self;
});
var SawBlade = Container.expand(function () {
var self = Container.call(this);
var sawGraphics = self.attachAsset('sawBlade', {
anchorX: 0.5,
anchorY: 0.5
});
self.rotationSpeed = 0.2;
self.moveSpeed = 2;
self.nearMissTimer = 0;
self.direction = {
x: Math.random() - 0.5,
y: Math.random() - 0.5
};
var length = Math.sqrt(self.direction.x * self.direction.x + self.direction.y * self.direction.y);
self.direction.x /= length;
self.direction.y /= length;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
sawGraphics.rotation += self.rotationSpeed;
// Move
var tempMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
self.x += self.direction.x * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
self.y += self.direction.y * self.moveSpeed * gameSpeed * hazardSpeedBoost * tempMultiplier;
// Bounce off maze walls
if (self.x <= mazeLeft + 40 || self.x >= mazeRight - 40) {
self.direction.x *= -1;
}
if (self.y <= mazeTop + 40 || self.y >= mazeBottom - 40) {
self.direction.y *= -1;
}
// Keep within bounds
self.x = Math.max(mazeLeft + 40, Math.min(mazeRight - 40, self.x));
self.y = Math.max(mazeTop + 40, Math.min(mazeBottom - 40, self.y));
};
return self;
});
var SpikeTrap = Container.expand(function () {
var self = Container.call(this);
var spikeGraphics = self.attachAsset('spikeTrap', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 180; // 3 seconds at 60fps
self.isActive = true;
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
self.lifeTimer--;
// Blink when about to disappear
if (self.lifeTimer < 60) {
spikeGraphics.alpha = Math.sin(self.lifeTimer * 0.3) * 0.5 + 0.5;
}
if (self.lifeTimer <= 0) {
self.isActive = false;
}
};
return self;
});
var TimePowerup = Container.expand(function () {
var self = Container.call(this);
var timeGraphics = self.attachAsset('timePowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.lifeTimer = 400; // 6.7 seconds
self.rotationSpeed = 0.05;
self.update = function () {
self.lifeTimer--;
timeGraphics.rotation += self.rotationSpeed;
// Sparkle effect
if (LK.ticks % 15 === 0) {
var sparkle = Math.random() > 0.5 ? 0xffffff : 0xff66ff;
timeGraphics.tint = sparkle;
var self_ref = self;
LK.setTimeout(function () {
if (timeGraphics.parent) timeGraphics.tint = 0xffffff;
}, 200);
}
if (self.lifeTimer <= 0) {
self.destroy();
}
};
return self;
});
var Turret = Container.expand(function () {
var self = Container.call(this);
var turretGraphics = self.attachAsset('turret', {
anchorX: 0.5,
anchorY: 0.5
});
self.fireTimer = 0;
self.bullets = [];
self.nearMissTimer = 0;
self.update = function () {
// Update near miss timer
if (self.nearMissTimer > 0) self.nearMissTimer--;
// Slowly rotate toward player
var dx = player.x - self.x;
var dy = player.y - self.y;
var targetAngle = Math.atan2(dy, dx);
var currentAngle = turretGraphics.rotation;
var angleDiff = targetAngle - currentAngle;
// Normalize angle difference
while (angleDiff > Math.PI) angleDiff -= 2 * Math.PI;
while (angleDiff < -Math.PI) angleDiff += 2 * Math.PI;
// Slowly rotate toward target
turretGraphics.rotation += angleDiff * 0.05;
// Fire bullets every 120 ticks (2 seconds)
self.fireTimer++;
if (self.fireTimer >= 120) {
self.fireTimer = 0;
var bullet = game.addChild(LK.getAsset('laserWall', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
}));
// Set bullet direction toward player
var distance = Math.sqrt(dx * dx + dy * dy);
bullet.speedX = dx / distance * 6 * hazardSpeedBoost;
bullet.speedY = dy / distance * 6 * hazardSpeedBoost;
bullet.lifeTimer = 180; // 3 seconds
self.bullets.push(bullet);
}
// Update bullets
for (var i = self.bullets.length - 1; i >= 0; i--) {
var bullet = self.bullets[i];
var speedMultiplier = tempSpeedBoostTimer > 0 ? 1.5 : 1;
bullet.x += bullet.speedX * gameSpeed * speedMultiplier;
bullet.y += bullet.speedY * gameSpeed * speedMultiplier;
bullet.lifeTimer--;
if (bullet.lifeTimer <= 0 || bullet.x < mazeLeft - 50 || bullet.x > mazeRight + 50 || bullet.y < mazeTop - 50 || bullet.y > mazeBottom + 50) {
bullet.destroy();
self.bullets.splice(i, 1);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// Game variables
var player;
var walls = [];
var hazards = [];
var powerups = [];
var gameSpeed = 1;
var slowMotionTimer = 0;
var scoreMultiplier = 1;
var lastMultiplierIncrease = 0;
var hazardSpawnTimer = 0;
var powerupSpawnTimer = 0;
var hazardSpeedBoost = 1;
var tempSpeedBoostTimer = 0;
var trails = [];
var explosionParticles = [];
var timeSlowActive = false;
var timeSlowDuration = 0;
var magneticOrbs = [];
var achievementTimer = 0;
var comboCounter = 0;
var lastComboTime = 0;
// Persistent game statistics
var gameStats = {
totalGamesPlayed: storage.totalGamesPlayed || 0,
totalScore: storage.totalScore || 0,
bestMultiplier: storage.bestMultiplier || 1,
totalHazardsDestroyed: storage.totalHazardsDestroyed || 0,
achievementsUnlocked: storage.achievementsUnlocked || []
};
// Achievement definitions
var achievements = [{
id: 'first_game',
name: 'First Steps',
desc: 'Play your first game',
target: 1,
stat: 'totalGamesPlayed'
}, {
id: 'score_1000',
name: 'Getting Started',
desc: 'Reach 1000 points',
target: 1000,
stat: 'score'
}, {
id: 'score_5000',
name: 'Skilled Player',
desc: 'Reach 5000 points',
target: 5000,
stat: 'score'
}, {
id: 'multiplier_5',
name: 'Combo Master',
desc: 'Reach 5x multiplier',
target: 5,
stat: 'multiplier'
}, {
id: 'survive_180',
name: 'Survivor',
desc: 'Survive 3 minutes',
target: 180,
stat: 'time'
}, {
id: 'destroy_50',
name: 'Destroyer',
desc: 'Destroy 50 hazards total',
target: 50,
stat: 'totalHazardsDestroyed'
}];
function checkAchievements() {
var currentStats = {
totalGamesPlayed: gameStats.totalGamesPlayed,
score: LK.getScore(),
multiplier: scoreMultiplier,
time: Math.floor(LK.ticks / 60),
totalHazardsDestroyed: gameStats.totalHazardsDestroyed
};
for (var i = 0; i < achievements.length; i++) {
var achievement = achievements[i];
if (gameStats.achievementsUnlocked.indexOf(achievement.id) === -1) {
if (currentStats[achievement.stat] >= achievement.target) {
gameStats.achievementsUnlocked.push(achievement.id);
storage.achievementsUnlocked = gameStats.achievementsUnlocked;
// Achievement notification
LK.getSound('achievement').play();
showAchievementPopup(achievement.name, achievement.desc);
}
}
}
}
function showAchievementPopup(name, desc) {
var popup = game.addChild(LK.getAsset('shieldPowerup', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 200,
scaleX: 2,
scaleY: 2,
tint: 0xFFD700
}));
tween(popup, {
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 3000,
onFinish: function onFinish() {
popup.destroy();
}
});
}
// Maze boundaries
var mazeLeft = 200;
var mazeRight = 1848;
var mazeTop = 200;
var mazeBottom = 2532;
var shrinkSpeed = 0.35;
// UI
var scoreTxt = new Text2('0', {
size: 80,
fill: 0x00FF41
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var multiplierTxt = new Text2('x1', {
size: 60,
fill: 0xFFAA00
});
multiplierTxt.anchor.set(1, 0);
multiplierTxt.y = 100;
LK.gui.topRight.addChild(multiplierTxt);
// High score display
var highScoreTxt = new Text2('HS: 0', {
size: 60,
fill: 0xFFFFFF
});
highScoreTxt.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(highScoreTxt);
// Initialize high score display
highScoreTxt.setText('HS: ' + (storage.highScore || 0));
// Create player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Create initial maze walls
function createMazeWalls() {
// Clear existing walls
for (var i = 0; i < walls.length; i++) {
walls[i].destroy();
}
walls = [];
// Top wall
for (var x = mazeLeft - 50; x <= mazeRight + 50; x += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: mazeTop - 50
}));
walls.push(wall);
}
// Bottom wall
for (var x = mazeLeft - 50; x <= mazeRight + 50; x += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: mazeBottom + 50
}));
walls.push(wall);
}
// Left wall
for (var y = mazeTop - 50; y <= mazeBottom + 50; y += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: mazeLeft - 50,
y: y
}));
walls.push(wall);
}
// Right wall
for (var y = mazeTop - 50; y <= mazeBottom + 50; y += 100) {
var wall = game.addChild(LK.getAsset('wall', {
anchorX: 0.5,
anchorY: 0.5,
x: mazeRight + 50,
y: y
}));
walls.push(wall);
}
}
// Initial wall creation
createMazeWalls();
// Touch controls
var touchStartX = 0;
var touchStartY = 0;
var isDragging = false;
game.down = function (x, y, obj) {
touchStartX = x;
touchStartY = y;
isDragging = true;
};
game.move = function (x, y, obj) {
if (!isDragging) return;
var deltaX = x - touchStartX;
var deltaY = y - touchStartY;
var threshold = 30;
player.moveDirection.x = 0;
player.moveDirection.y = 0;
if (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold) {
if (Math.abs(deltaX) > Math.abs(deltaY)) {
player.moveDirection.x = deltaX > 0 ? 1 : -1;
} else {
player.moveDirection.y = deltaY > 0 ? 1 : -1;
}
}
};
game.up = function (x, y, obj) {
isDragging = false;
player.moveDirection.x = 0;
player.moveDirection.y = 0;
};
// Spawn hazards
function spawnHazard() {
var hazardType;
// Advanced hazard spawning logic based on multiplier
if (scoreMultiplier >= 5 && Math.random() < 0.15) {
hazardType = 5; // MagneticOrb
} else if (scoreMultiplier >= 3 && Math.random() < 0.2) {
hazardType = 4; // BouncingWall
} else {
hazardType = Math.floor(Math.random() * 4);
}
var hazard;
if (hazardType === 0) {
// Saw blade
hazard = new SawBlade();
hazard.x = mazeLeft + Math.random() * (mazeRight - mazeLeft);
hazard.y = mazeTop + Math.random() * (mazeBottom - mazeTop);
} else if (hazardType === 1) {
// Laser wall
hazard = new LaserWall();
if (hazard.isVertical) {
hazard.x = mazeLeft + Math.random() * (mazeRight - mazeLeft);
hazard.y = (mazeTop + mazeBottom) / 2;
} else {
hazard.x = (mazeLeft + mazeRight) / 2;
hazard.y = mazeTop + Math.random() * (mazeBottom - mazeTop);
}
} else if (hazardType === 2) {
// Spike trap
hazard = new SpikeTrap();
hazard.x = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
hazard.y = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
} else if (hazardType === 3) {
// Turret
hazard = new Turret();
hazard.x = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
hazard.y = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
} else if (hazardType === 4) {
// Bouncing wall
hazard = new BouncingWall();
if (hazard.isVertical) {
hazard.x = mazeLeft + Math.random() * (mazeRight - mazeLeft);
hazard.y = (mazeTop + mazeBottom) / 2;
} else {
hazard.x = (mazeLeft + mazeRight) / 2;
hazard.y = mazeTop + Math.random() * (mazeBottom - mazeTop);
}
} else if (hazardType === 5) {
// Magnetic Orb
hazard = new MagneticOrb();
hazard.x = mazeLeft + 120 + Math.random() * (mazeRight - mazeLeft - 240);
hazard.y = mazeTop + 120 + Math.random() * (mazeBottom - mazeTop - 240);
magneticOrbs.push(hazard);
}
hazards.push(hazard);
game.addChild(hazard);
}
// Spawn powerup
function spawnPowerup() {
var rand = Math.random();
var powerup;
if (rand < 0.4) {
powerup = new Powerup('slow');
} else if (rand < 0.7) {
powerup = new Powerup('shield');
} else if (rand < 0.85) {
powerup = new TimePowerup();
} else {
powerup = new MultiplierBoost();
}
powerup.x = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
powerup.y = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
powerups.push(powerup);
game.addChild(powerup);
}
// Play background music
LK.playMusic('bgmusic');
// Main game loop
game.update = function () {
// Handle slow motion
if (slowMotionTimer > 0) {
slowMotionTimer--;
gameSpeed = 0.3;
if (slowMotionTimer <= 0) {
gameSpeed = 1;
}
}
// Handle time slow effect
if (timeSlowDuration > 0) {
timeSlowDuration--;
gameSpeed = 0.1; // Much slower than regular slow motion
hazardSpeedBoost *= 0.5; // Reduce hazard speed as well
if (timeSlowDuration <= 0) {
timeSlowActive = false;
gameSpeed = 1;
hazardSpeedBoost = Math.max(1, hazardSpeedBoost * 2); // Restore hazard speed
}
}
// Emergency teleportation (double tap detection simulation)
if (LK.ticks % 180 === 0 && Math.random() < 0.03) {
// Rare random teleportation for demo
// Find safe teleportation spot
var attempts = 0;
var newX, newY, safe;
do {
newX = mazeLeft + 100 + Math.random() * (mazeRight - mazeLeft - 200);
newY = mazeTop + 100 + Math.random() * (mazeBottom - mazeTop - 200);
safe = true;
// Check if position is safe from hazards
for (var h = 0; h < hazards.length; h++) {
var dx = newX - hazards[h].x;
var dy = newY - hazards[h].y;
if (Math.sqrt(dx * dx + dy * dy) < 120) {
safe = false;
break;
}
}
attempts++;
} while (!safe && attempts < 10);
if (safe) {
LK.getSound('teleport').play();
// Teleportation effect
var oldX = player.x,
oldY = player.y;
tween(player, {
alpha: 0
}, {
duration: 100,
onFinish: function onFinish() {
player.x = newX;
player.y = newY;
tween(player, {
alpha: 1
}, {
duration: 100
});
}
});
}
}
// Achievement checking
achievementTimer++;
if (achievementTimer >= 60) {
// Check every second
checkAchievements();
achievementTimer = 0;
}
// Handle temp speed boost timer
if (tempSpeedBoostTimer > 0) {
tempSpeedBoostTimer--;
}
// Rhythmic difficulty every 60 seconds (3600 ticks)
if (LK.ticks > 0 && LK.ticks % 3600 === 0) {
hazardSpeedBoost *= 1.2;
// Make all laser walls nearly invisible for 1 second
for (var i = 0; i < hazards.length; i++) {
var hazard = hazards[i];
if (hazard.constructor === LaserWall) {
var laserGraphics = hazard.getChildAt(0);
tween(laserGraphics, {
alpha: 0.05
}, {
duration: 1000,
onFinish: function onFinish() {
// Resume normal pulsing after 1 second
}
});
}
}
}
// Shrink maze
mazeLeft += shrinkSpeed * gameSpeed;
mazeRight -= shrinkSpeed * gameSpeed;
mazeTop += shrinkSpeed * gameSpeed;
mazeBottom -= shrinkSpeed * gameSpeed;
// Recreate walls periodically
if (LK.ticks % 30 === 0) {
createMazeWalls();
}
// Check if maze is too small (game over condition)
if (mazeRight - mazeLeft < 200 || mazeBottom - mazeTop < 200) {
// Update all persistent statistics
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
// Update game statistics
gameStats.totalGamesPlayed++;
gameStats.totalScore += currentScore;
gameStats.bestMultiplier = Math.max(gameStats.bestMultiplier, scoreMultiplier);
// Save to storage
storage.totalGamesPlayed = gameStats.totalGamesPlayed;
storage.totalScore = gameStats.totalScore;
storage.bestMultiplier = gameStats.bestMultiplier;
storage.totalHazardsDestroyed = gameStats.totalHazardsDestroyed;
// Final achievement check
checkAchievements();
LK.showGameOver();
return;
}
// Update score
LK.setScore(Math.floor(LK.ticks / 60 * scoreMultiplier));
scoreTxt.setText(LK.getScore().toString());
// Increase multiplier every 30 seconds
var currentTime = Math.floor(LK.ticks / 60);
if (currentTime - lastMultiplierIncrease >= 30) {
scoreMultiplier++;
lastMultiplierIncrease = currentTime;
multiplierTxt.setText('x' + scoreMultiplier);
// Multiplier reward animation
tween(multiplierTxt, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(multiplierTxt, {
scaleX: 1,
scaleY: 1
}, {
duration: 200,
easing: tween.easeIn
});
}
});
}
// Spawn hazards
hazardSpawnTimer++;
var spawnRate = Math.max(60, 180 - scoreMultiplier * 20);
if (hazardSpawnTimer >= spawnRate) {
spawnHazard();
hazardSpawnTimer = 0;
}
// Spawn powerups (rare)
powerupSpawnTimer++;
if (powerupSpawnTimer >= 600 && Math.random() < 0.3) {
// Every 10 seconds, 30% chance
spawnPowerup();
powerupSpawnTimer = 0;
}
// Check collision with walls
if (!player.shieldActive) {
if (player.x <= mazeLeft + 30 || player.x >= mazeRight - 30 || player.y <= mazeTop + 30 || player.y >= mazeBottom - 30) {
// Update high score
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.getSound('collision').play();
LK.showGameOver();
return;
}
}
// Update and check hazard collisions
for (var i = hazards.length - 1; i >= 0; i--) {
var hazard = hazards[i];
if (hazard.isActive === false) {
hazard.destroy();
hazards.splice(i, 1);
continue;
}
// Check collision with turret bullets
if (hazard.bullets) {
for (var j = hazard.bullets.length - 1; j >= 0; j--) {
var bullet = hazard.bullets[j];
if (player.intersects(bullet)) {
if (player.shieldActive) {
// Shield absorbs the hit
player.shieldActive = false;
// Reset multiplier and timer
scoreMultiplier = 1;
lastMultiplierIncrease = Math.floor(LK.ticks / 60);
multiplierTxt.setText('x' + scoreMultiplier);
// Flash multiplier text red then back to orange
tween(multiplierTxt, {
tint: 0xFF0000
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(multiplierTxt, {
tint: 0xFFAA00
}, {
duration: 100,
easing: tween.linear
});
}
});
// Stop any existing tweens on player graphics
tween.stop(player.getChildAt(0), {
tint: true
});
// Create sequential white/yellow blink animation
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffff00
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear
});
}
});
}
});
bullet.destroy();
hazard.bullets.splice(j, 1);
} else {
// Update high score before game over
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.getSound('collision').play();
LK.showGameOver();
return;
}
}
}
}
// Special handling for BouncingWall
if (hazard.constructor === BouncingWall && player.intersects(hazard)) {
// Calculate push force
var dx = player.x - hazard.x;
var dy = player.y - hazard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var forceX = dx / distance * 150;
var forceY = dy / distance * 150;
player.x += forceX;
player.y += forceY;
}
// Score penalty
var newScore = Math.max(0, LK.getScore() - 50);
LK.setScore(newScore);
scoreTxt.setText(LK.getScore().toString());
// Blue flash effect
LK.getSound('collision').play();
var playerGraphics = player.getChildAt(0);
tween(playerGraphics, {
tint: 0x0000FF
}, {
duration: 100,
onFinish: function onFinish() {
tween(playerGraphics, {
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
} else if (player.intersects(hazard)) {
if (player.shieldActive) {
// Shield absorbs the hit
player.shieldActive = false;
// Reset multiplier and timer
scoreMultiplier = 1;
lastMultiplierIncrease = Math.floor(LK.ticks / 60);
multiplierTxt.setText('x' + scoreMultiplier);
// Flash multiplier text red then back to orange
tween(multiplierTxt, {
tint: 0xFF0000
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(multiplierTxt, {
tint: 0xFFAA00
}, {
duration: 100,
easing: tween.linear
});
}
});
// Stop any existing tweens on player graphics
tween.stop(player.getChildAt(0), {
tint: true
});
// Create sequential white/yellow blink animation
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffff00
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(player.getChildAt(0), {
tint: 0xffffff
}, {
duration: 50,
easing: tween.linear
});
}
});
}
});
} else {
// Create explosion effect before game over
for (var p = 0; p < 12; p++) {
var particle = game.addChild(LK.getAsset('player', {
anchorX: 0.5,
anchorY: 0.5,
x: player.x,
y: player.y,
scaleX: 0.2,
scaleY: 0.2,
tint: Math.random() > 0.5 ? 0xFF0000 : 0xFFFF00
}));
var speedX = (Math.random() - 0.5) * 20;
var speedY = (Math.random() - 0.5) * 20;
explosionParticles.push(particle);
tween(particle, {
x: particle.x + speedX,
y: particle.y + speedY,
scaleX: 0,
scaleY: 0,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
particle.destroy();
var index = explosionParticles.indexOf(particle);
if (index > -1) explosionParticles.splice(index, 1);
}
});
}
// Update high score before game over
var currentScore = LK.getScore();
var highScore = storage.highScore || 0;
if (currentScore > highScore) {
storage.highScore = currentScore;
}
LK.getSound('collision').play();
LK.showGameOver();
return;
}
}
}
// Update and check powerup collisions
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (player.intersects(powerup)) {
LK.getSound('powerup').play();
if (powerup.type === 'slow') {
slowMotionTimer = 180; // 3 seconds
} else if (powerup.type === 'shield') {
player.activateShield();
} else if (powerup.constructor === TimePowerup) {
// Ultra slow motion effect
timeSlowActive = true;
timeSlowDuration = 240; // 4 seconds
} else if (powerup.constructor === MultiplierBoost) {
// Double score multiplier temporarily
scoreMultiplier += 2;
multiplierTxt.setText('x' + scoreMultiplier);
// Bonus score animation
tween(multiplierTxt, {
scaleX: 2,
scaleY: 2,
tint: 0xFFD700
}, {
duration: 300,
onFinish: function onFinish() {
tween(multiplierTxt, {
scaleX: 1,
scaleY: 1,
tint: 0xFFAA00
}, {
duration: 200
});
}
});
}
// Powerup negative effect: temporary speed boost to hazards (except for time powerups)
if (powerup.constructor !== TimePowerup) {
tempSpeedBoostTimer = 30; // 0.5 seconds
}
powerup.destroy();
powerups.splice(i, 1);
}
}
// Near miss detection and saw blade vs bullet collision
for (var i = 0; i < hazards.length; i++) {
var hazard = hazards[i];
// Near miss detection
if (hazard.nearMissTimer !== undefined && hazard.nearMissTimer <= 0) {
var dx = player.x - hazard.x;
var dy = player.y - hazard.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var collisionDistance = 50; // Approximate collision distance
if (distance > collisionDistance + 40 && distance < collisionDistance + 60) {
// Near miss detected
LK.getSound('nearmiss').play();
hazard.nearMissTimer = 60; // Cooldown
// White flash on player
var playerGraphics = player.getChildAt(0);
tween(playerGraphics, {
tint: 0xFFFFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(playerGraphics, {
tint: 0x00ff00
}, {
duration: 50
});
}
});
}
}
// Saw blade vs bullet collision
if (hazard.constructor === SawBlade) {
for (var j = 0; j < hazards.length; j++) {
var otherHazard = hazards[j];
if (otherHazard.constructor === Turret && otherHazard.bullets) {
for (var k = otherHazard.bullets.length - 1; k >= 0; k--) {
var bullet = otherHazard.bullets[k];
if (hazard.intersects(bullet)) {
// Collision detected - destroy both
var currentTime = Math.floor(LK.ticks / 60);
// Combo system
if (currentTime - lastComboTime <= 2) {
// Within 2 seconds
comboCounter++;
} else {
comboCounter = 1;
}
lastComboTime = currentTime;
// Bonus points for combo
var bonusPoints = 10 * comboCounter;
LK.setScore(LK.getScore() + bonusPoints);
scoreTxt.setText(LK.getScore().toString());
// Update destruction statistics
gameStats.totalHazardsDestroyed++;
// Visual combo feedback
if (comboCounter > 1) {
var comboText = game.addChild(new Text2('x' + comboCounter + '!', {
size: 40,
fill: 0xFFD700
}));
comboText.x = hazard.x;
comboText.y = hazard.y;
comboText.anchor.set(0.5, 0.5);
tween(comboText, {
y: comboText.y - 60,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
comboText.destroy();
}
});
}
hazard.destroy();
hazards.splice(i, 1);
bullet.destroy();
otherHazard.bullets.splice(k, 1);
i--; // Adjust index since we removed an element
break;
}
}
}
}
}
}
};