/****
* Classes
****/
//var tween = LK.import("@upit/tween.v1");
// Civilian car class (obstacle)
var Car = Container.expand(function () {
var self = Container.call(this);
self.speed = 8 + Math.random() * 6;
self.direction = Math.random() < 0.5 ? 1 : -1; // 1: right, -1: left
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Update: move horizontally
self.update = function () {
self.x += self.speed * self.direction;
};
return self;
});
// Health pack class
var HealthPack = Container.expand(function () {
var self = Container.call(this);
var healthSprite = self.attachAsset('health', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Loot class
var Loot = Container.expand(function () {
var self = Container.call(this);
var lootSprite = self.attachAsset('loot', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
self.isInVehicle = false;
self.health = 30;
self.speed = 12;
self.vehicleSpeed = 22;
self.invincible = false;
self.invincibleTimer = 0;
// Attach player asset
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach vehicle asset (hidden by default)
var vehicleSprite = self.attachAsset('stolenCar', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Switch to vehicle mode
self.enterVehicle = function () {
self.isInVehicle = true;
playerSprite.alpha = 0;
vehicleSprite.alpha = 1;
};
// Exit vehicle mode
self.exitVehicle = function () {
self.isInVehicle = false;
playerSprite.alpha = 1;
vehicleSprite.alpha = 0;
};
// Take damage (with invincibility frames)
self.takeDamage = function () {
if (self.invincible) {
return;
}
self.health -= 1;
self.invincible = true;
self.invincibleTimer = 60; // 1 second at 60fps
LK.effects.flashObject(self, 0xff0000, 400);
};
// Heal
self.heal = function () {
if (!self.maxHealth) self.maxHealth = 30;
if (self.health < self.maxHealth) {
self.health += 1;
}
};
// Update (invincibility timer)
self.update = function () {
if (self.invincible) {
self.invincibleTimer--;
if (self.invincibleTimer <= 0) {
self.invincible = false;
}
}
};
return self;
});
// Police car class
var Police = Container.expand(function () {
var self = Container.call(this);
self.speed = 10 + Math.random() * 4;
self.target = null;
var policeSprite = self.attachAsset('police', {
anchorX: 0.5,
anchorY: 0.5
});
// Set target (player)
self.setTarget = function (target) {
self.target = target;
};
// Update: chase player
self.update = function () {
if (!self.target) {
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xe0e0e0
});
/****
* Game Code
****/
// --- Global variables ---
// City background (large, light gray)
// Player (thief) - red ellipse
// Police car - blue box
// Civilian car - gray box
// Loot - yellow ellipse
// Health pack - green ellipse
// Stolen vehicle (player) - orange box
var player;
var policeCars = [];
var cars = [];
var loots = [];
var healthPacks = [];
var score = 0;
var heistObjective = 5; // Number of loot to collect per level
var lootCollected = 0;
var level = 1;
var dragNode = null;
var lastMoveX = 0,
lastMoveY = 0;
// --- Background ---
var cityBg = LK.getAsset('cityBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(cityBg);
// --- Score and Health UI ---
var scoreTxt = new Text2('Score: 0 | HP: 30', {
size: 90,
fill: "#222"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var healthTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xD83318
});
healthTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthTxt);
healthTxt.y = 100;
// --- Objective UI ---
var objTxt = new Text2('Loot: 0/5', {
size: 70,
fill: "#444"
});
objTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(objTxt);
objTxt.y = 200;
// --- Helper functions ---
function updateScoreUI() {
if (player) {
scoreTxt.setText('Score: ' + score + ' | HP: ' + player.health);
} else {
scoreTxt.setText('Score: ' + score);
}
}
function updateHealthUI() {
var hearts = '';
var maxHearts = player.maxHealth ? player.maxHealth : 30;
for (var i = 0; i < player.health && i < maxHearts; i++) {
hearts += '♥';
}
healthTxt.setText(hearts + ' (' + player.health + ')');
updateScoreUI();
}
function updateObjectiveUI() {
objTxt.setText('Loot: ' + lootCollected + '/' + heistObjective);
}
function spawnLoot() {
var loot = new Loot();
// Place randomly, not too close to edges or top left
loot.x = 200 + Math.random() * (2048 - 400);
loot.y = 300 + Math.random() * (2732 - 600);
loots.push(loot);
game.addChild(loot);
}
function spawnHealthPack() {
var hp = new HealthPack();
hp.x = 200 + Math.random() * (2048 - 400);
hp.y = 300 + Math.random() * (2732 - 600);
healthPacks.push(hp);
game.addChild(hp);
}
function spawnPolice() {
var police = new Police();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// Top
police.x = 200 + Math.random() * (2048 - 400);
police.y = 0;
} else if (edge === 1) {
// Bottom
police.x = 200 + Math.random() * (2048 - 400);
police.y = 2732;
} else if (edge === 2) {
// Left
police.x = 0;
police.y = 300 + Math.random() * (2732 - 600);
} else {
// Right
police.x = 2048;
police.y = 300 + Math.random() * (2732 - 600);
}
police.setTarget(player);
policeCars.push(police);
game.addChild(police);
}
function spawnCar() {
var car = new Car();
// Randomly choose top or bottom lane
var laneY = 500 + Math.floor(Math.random() * 8) * 250;
car.y = laneY;
if (car.direction === 1) {
car.x = -100;
} else {
car.x = 2048 + 100;
}
cars.push(car);
game.addChild(car);
}
function resetLevel() {
// Remove all loot, police, cars, health packs
for (var i = 0; i < loots.length; i++) {
loots[i].destroy();
}
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].destroy();
}
for (var i = 0; i < cars.length; i++) {
cars[i].destroy();
}
for (var i = 0; i < healthPacks.length; i++) {
healthPacks[i].destroy();
}
loots = [];
policeCars = [];
cars = [];
healthPacks = [];
lootCollected = 0;
heistObjective = 5 + (level - 1) * 2;
updateObjectiveUI();
// Spawn loot
for (var i = 0; i < heistObjective; i++) {
spawnLoot();
}
// Spawn police immediately at level start
for (var i = 0; i < Math.min(2 + level, 6); i++) {
spawnPolice();
}
// Spawn cars
for (var i = 0; i < 6 + level * 2; i++) {
spawnCar();
}
// Spawn health pack
if (level > 1) {
spawnHealthPack();
}
// Reset player position and state
player.x = 2048 / 2;
player.y = 2732 - 300;
player.exitVehicle();
// Increase max health by 1 for each completed level, up to a reasonable cap (e.g. 100)
if (!player.maxHealth) {
player.maxHealth = 30;
}
if (level > 1) {
player.maxHealth += 1;
if (player.maxHealth > 100) player.maxHealth = 100;
}
player.health = player.maxHealth;
updateHealthUI();
}
// --- Initialize player ---
player = new Player();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
// --- Start first level ---
resetLevel();
updateScoreUI();
// --- Touch/drag controls ---
function clamp(val, min, max) {
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
}
function handleMove(x, y, obj) {
if (!dragNode) {
return;
}
// Clamp to game area, avoid top left 100x100
var px = clamp(x, 100 + 50, 2048 - 50);
var py = clamp(y, 100 + 50, 2732 - 50);
// Move player smoothly
var speed = dragNode.isInVehicle ? dragNode.vehicleSpeed : dragNode.speed;
var dx = px - dragNode.x;
var dy = py - dragNode.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > speed) {
dragNode.x += dx / dist * speed;
dragNode.y += dy / dist * speed;
} else {
dragNode.x = px;
dragNode.y = py;
}
lastMoveX = px;
lastMoveY = py;
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if not in top left 100x100
if (x < 100 && y < 100) {
return;
}
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Main game update loop ---
game.update = function () {
// Update player (invincibility, etc)
player.update();
// Update police
for (var i = policeCars.length - 1; i >= 0; i--) {
var p = policeCars[i];
p.update();
// Check collision with player
if (p.intersects(player)) {
if (!player.invincible) {
player.takeDamage();
updateHealthUI();
if (player.health <= 0) {
LK.effects.flashScreen(0x0000ff, 1000);
LK.showGameOver();
return;
}
}
}
}
// Update cars (obstacles)
for (var i = cars.length - 1; i >= 0; i--) {
var c = cars[i];
c.update();
// Remove if off screen
if (c.direction === 1 && c.x > 2048 + 200 || c.direction === -1 && c.x < -200) {
c.destroy();
cars.splice(i, 1);
continue;
}
// Collision with player
if (c.intersects(player)) {
if (!player.isInVehicle && !player.invincible) {
player.takeDamage();
updateHealthUI();
if (player.health <= 0) {
LK.effects.flashScreen(0x0000ff, 1000);
LK.showGameOver();
return;
}
}
}
// Steal vehicle: if player is not in vehicle and collides, enter vehicle
if (!player.isInVehicle && c.intersects(player)) {
player.enterVehicle();
// Remove the car
c.destroy();
cars.splice(i, 1);
continue;
}
}
// Update loot
for (var i = loots.length - 1; i >= 0; i--) {
var l = loots[i];
if (l.intersects(player)) {
score += 10;
lootCollected += 1;
updateScoreUI();
updateObjectiveUI();
l.destroy();
loots.splice(i, 1);
// Win level if all loot collected
if (lootCollected >= heistObjective) {
level += 1;
LK.effects.flashScreen(0x44de83, 800);
LK.setTimeout(function () {
resetLevel();
}, 900);
return;
}
}
}
// Update health packs
for (var i = healthPacks.length - 1; i >= 0; i--) {
var hp = healthPacks[i];
if (hp.intersects(player)) {
player.heal();
updateHealthUI();
hp.destroy();
healthPacks.splice(i, 1);
}
}
// Player exits vehicle after a while (simulate "stolen car runs out of gas")
if (player.isInVehicle) {
if (!player.vehicleTimer) {
player.vehicleTimer = 180;
} // 3 seconds
player.vehicleTimer--;
if (player.vehicleTimer <= 0) {
player.exitVehicle();
player.vehicleTimer = null;
}
}
// Spawn new cars and police at intervals
if (LK.ticks % (90 - Math.min(level * 5, 60)) === 0) {
spawnCar();
}
if (LK.ticks % (300 - Math.min(level * 10, 200)) === 0) {
spawnPolice();
}
// Occasionally spawn health pack
if (LK.ticks % 900 === 0 && healthPacks.length < 1 && player.health < 3) {
spawnHealthPack();
}
}; /****
* Classes
****/
//var tween = LK.import("@upit/tween.v1");
// Civilian car class (obstacle)
var Car = Container.expand(function () {
var self = Container.call(this);
self.speed = 8 + Math.random() * 6;
self.direction = Math.random() < 0.5 ? 1 : -1; // 1: right, -1: left
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Update: move horizontally
self.update = function () {
self.x += self.speed * self.direction;
};
return self;
});
// Health pack class
var HealthPack = Container.expand(function () {
var self = Container.call(this);
var healthSprite = self.attachAsset('health', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Loot class
var Loot = Container.expand(function () {
var self = Container.call(this);
var lootSprite = self.attachAsset('loot', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
self.isInVehicle = false;
self.health = 30;
self.speed = 12;
self.vehicleSpeed = 22;
self.invincible = false;
self.invincibleTimer = 0;
// Attach player asset
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach vehicle asset (hidden by default)
var vehicleSprite = self.attachAsset('stolenCar', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Switch to vehicle mode
self.enterVehicle = function () {
self.isInVehicle = true;
playerSprite.alpha = 0;
vehicleSprite.alpha = 1;
};
// Exit vehicle mode
self.exitVehicle = function () {
self.isInVehicle = false;
playerSprite.alpha = 1;
vehicleSprite.alpha = 0;
};
// Take damage (with invincibility frames)
self.takeDamage = function () {
if (self.invincible) {
return;
}
self.health -= 1;
self.invincible = true;
self.invincibleTimer = 60; // 1 second at 60fps
LK.effects.flashObject(self, 0xff0000, 400);
};
// Heal
self.heal = function () {
if (!self.maxHealth) self.maxHealth = 30;
if (self.health < self.maxHealth) {
self.health += 1;
}
};
// Update (invincibility timer)
self.update = function () {
if (self.invincible) {
self.invincibleTimer--;
if (self.invincibleTimer <= 0) {
self.invincible = false;
}
}
};
return self;
});
// Police car class
var Police = Container.expand(function () {
var self = Container.call(this);
self.speed = 10 + Math.random() * 4;
self.target = null;
var policeSprite = self.attachAsset('police', {
anchorX: 0.5,
anchorY: 0.5
});
// Set target (player)
self.setTarget = function (target) {
self.target = target;
};
// Update: chase player
self.update = function () {
if (!self.target) {
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xe0e0e0
});
/****
* Game Code
****/
// --- Global variables ---
// City background (large, light gray)
// Player (thief) - red ellipse
// Police car - blue box
// Civilian car - gray box
// Loot - yellow ellipse
// Health pack - green ellipse
// Stolen vehicle (player) - orange box
var player;
var policeCars = [];
var cars = [];
var loots = [];
var healthPacks = [];
var score = 0;
var heistObjective = 5; // Number of loot to collect per level
var lootCollected = 0;
var level = 1;
var dragNode = null;
var lastMoveX = 0,
lastMoveY = 0;
// --- Background ---
var cityBg = LK.getAsset('cityBg', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(cityBg);
// --- Score and Health UI ---
var scoreTxt = new Text2('Score: 0 | HP: 30', {
size: 90,
fill: "#222"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var healthTxt = new Text2('♥♥♥', {
size: 90,
fill: 0xD83318
});
healthTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthTxt);
healthTxt.y = 100;
// --- Objective UI ---
var objTxt = new Text2('Loot: 0/5', {
size: 70,
fill: "#444"
});
objTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(objTxt);
objTxt.y = 200;
// --- Helper functions ---
function updateScoreUI() {
if (player) {
scoreTxt.setText('Score: ' + score + ' | HP: ' + player.health);
} else {
scoreTxt.setText('Score: ' + score);
}
}
function updateHealthUI() {
var hearts = '';
var maxHearts = player.maxHealth ? player.maxHealth : 30;
for (var i = 0; i < player.health && i < maxHearts; i++) {
hearts += '♥';
}
healthTxt.setText(hearts + ' (' + player.health + ')');
updateScoreUI();
}
function updateObjectiveUI() {
objTxt.setText('Loot: ' + lootCollected + '/' + heistObjective);
}
function spawnLoot() {
var loot = new Loot();
// Place randomly, not too close to edges or top left
loot.x = 200 + Math.random() * (2048 - 400);
loot.y = 300 + Math.random() * (2732 - 600);
loots.push(loot);
game.addChild(loot);
}
function spawnHealthPack() {
var hp = new HealthPack();
hp.x = 200 + Math.random() * (2048 - 400);
hp.y = 300 + Math.random() * (2732 - 600);
healthPacks.push(hp);
game.addChild(hp);
}
function spawnPolice() {
var police = new Police();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
// Top
police.x = 200 + Math.random() * (2048 - 400);
police.y = 0;
} else if (edge === 1) {
// Bottom
police.x = 200 + Math.random() * (2048 - 400);
police.y = 2732;
} else if (edge === 2) {
// Left
police.x = 0;
police.y = 300 + Math.random() * (2732 - 600);
} else {
// Right
police.x = 2048;
police.y = 300 + Math.random() * (2732 - 600);
}
police.setTarget(player);
policeCars.push(police);
game.addChild(police);
}
function spawnCar() {
var car = new Car();
// Randomly choose top or bottom lane
var laneY = 500 + Math.floor(Math.random() * 8) * 250;
car.y = laneY;
if (car.direction === 1) {
car.x = -100;
} else {
car.x = 2048 + 100;
}
cars.push(car);
game.addChild(car);
}
function resetLevel() {
// Remove all loot, police, cars, health packs
for (var i = 0; i < loots.length; i++) {
loots[i].destroy();
}
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].destroy();
}
for (var i = 0; i < cars.length; i++) {
cars[i].destroy();
}
for (var i = 0; i < healthPacks.length; i++) {
healthPacks[i].destroy();
}
loots = [];
policeCars = [];
cars = [];
healthPacks = [];
lootCollected = 0;
heistObjective = 5 + (level - 1) * 2;
updateObjectiveUI();
// Spawn loot
for (var i = 0; i < heistObjective; i++) {
spawnLoot();
}
// Spawn police immediately at level start
for (var i = 0; i < Math.min(2 + level, 6); i++) {
spawnPolice();
}
// Spawn cars
for (var i = 0; i < 6 + level * 2; i++) {
spawnCar();
}
// Spawn health pack
if (level > 1) {
spawnHealthPack();
}
// Reset player position and state
player.x = 2048 / 2;
player.y = 2732 - 300;
player.exitVehicle();
// Increase max health by 1 for each completed level, up to a reasonable cap (e.g. 100)
if (!player.maxHealth) {
player.maxHealth = 30;
}
if (level > 1) {
player.maxHealth += 1;
if (player.maxHealth > 100) player.maxHealth = 100;
}
player.health = player.maxHealth;
updateHealthUI();
}
// --- Initialize player ---
player = new Player();
player.x = 2048 / 2;
player.y = 2732 - 300;
game.addChild(player);
// --- Start first level ---
resetLevel();
updateScoreUI();
// --- Touch/drag controls ---
function clamp(val, min, max) {
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
}
function handleMove(x, y, obj) {
if (!dragNode) {
return;
}
// Clamp to game area, avoid top left 100x100
var px = clamp(x, 100 + 50, 2048 - 50);
var py = clamp(y, 100 + 50, 2732 - 50);
// Move player smoothly
var speed = dragNode.isInVehicle ? dragNode.vehicleSpeed : dragNode.speed;
var dx = px - dragNode.x;
var dy = py - dragNode.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > speed) {
dragNode.x += dx / dist * speed;
dragNode.y += dy / dist * speed;
} else {
dragNode.x = px;
dragNode.y = py;
}
lastMoveX = px;
lastMoveY = py;
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Only allow drag if not in top left 100x100
if (x < 100 && y < 100) {
return;
}
dragNode = player;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragNode = null;
};
// --- Main game update loop ---
game.update = function () {
// Update player (invincibility, etc)
player.update();
// Update police
for (var i = policeCars.length - 1; i >= 0; i--) {
var p = policeCars[i];
p.update();
// Check collision with player
if (p.intersects(player)) {
if (!player.invincible) {
player.takeDamage();
updateHealthUI();
if (player.health <= 0) {
LK.effects.flashScreen(0x0000ff, 1000);
LK.showGameOver();
return;
}
}
}
}
// Update cars (obstacles)
for (var i = cars.length - 1; i >= 0; i--) {
var c = cars[i];
c.update();
// Remove if off screen
if (c.direction === 1 && c.x > 2048 + 200 || c.direction === -1 && c.x < -200) {
c.destroy();
cars.splice(i, 1);
continue;
}
// Collision with player
if (c.intersects(player)) {
if (!player.isInVehicle && !player.invincible) {
player.takeDamage();
updateHealthUI();
if (player.health <= 0) {
LK.effects.flashScreen(0x0000ff, 1000);
LK.showGameOver();
return;
}
}
}
// Steal vehicle: if player is not in vehicle and collides, enter vehicle
if (!player.isInVehicle && c.intersects(player)) {
player.enterVehicle();
// Remove the car
c.destroy();
cars.splice(i, 1);
continue;
}
}
// Update loot
for (var i = loots.length - 1; i >= 0; i--) {
var l = loots[i];
if (l.intersects(player)) {
score += 10;
lootCollected += 1;
updateScoreUI();
updateObjectiveUI();
l.destroy();
loots.splice(i, 1);
// Win level if all loot collected
if (lootCollected >= heistObjective) {
level += 1;
LK.effects.flashScreen(0x44de83, 800);
LK.setTimeout(function () {
resetLevel();
}, 900);
return;
}
}
}
// Update health packs
for (var i = healthPacks.length - 1; i >= 0; i--) {
var hp = healthPacks[i];
if (hp.intersects(player)) {
player.heal();
updateHealthUI();
hp.destroy();
healthPacks.splice(i, 1);
}
}
// Player exits vehicle after a while (simulate "stolen car runs out of gas")
if (player.isInVehicle) {
if (!player.vehicleTimer) {
player.vehicleTimer = 180;
} // 3 seconds
player.vehicleTimer--;
if (player.vehicleTimer <= 0) {
player.exitVehicle();
player.vehicleTimer = null;
}
}
// Spawn new cars and police at intervals
if (LK.ticks % (90 - Math.min(level * 5, 60)) === 0) {
spawnCar();
}
if (LK.ticks % (300 - Math.min(level * 10, 200)) === 0) {
spawnPolice();
}
// Occasionally spawn health pack
if (LK.ticks % 900 === 0 && healthPacks.length < 1 && player.health < 3) {
spawnHealthPack();
}
};