/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Building class
var Building = Container.expand(function () {
var self = Container.call(this);
var bldg = self.attachAsset('building', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 18;
self.direction = 0; // in radians, 0 = right
self.driver = null; // Player or null
self.ai = false;
self.aiTarget = null;
self.update = function () {
// Store previous position for collision revert
var prevX = self.x;
var prevY = self.y;
// Cars do not move; skip player and AI movement logic
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
break;
}
}
// Clamp to city bounds
if (self.x < 90) self.x = 90;
if (self.x > 2048 - 90) self.x = 2048 - 90;
if (self.y < 100) self.y = 100;
if (self.y > 2732 - 90) self.y = 2732 - 90;
};
return self;
});
// Mission marker class
var MissionMarker = Container.expand(function () {
var self = Container.call(this);
var marker = self.attachAsset('mission', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = true;
return self;
});
// Pedestrian class
var Pedestrian = Container.expand(function () {
var self = Container.call(this);
var pedSprite = self.attachAsset('pedestrian', {
anchorX: 0.5,
anchorY: 0.5
});
// Pedestrians only move left and right in order
self.speed = 4 + Math.random() * 4;
self.direction = Math.random() < 0.5 ? 0 : Math.PI; // 0 = right, PI = left
self.update = function () {
var prevX = self.x;
var prevY = self.y;
// Move only in X direction (left/right)
self.x += Math.cos(self.direction) * self.speed;
// No Y movement
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
// Reverse direction on building collision
self.direction = self.direction === 0 ? Math.PI : 0;
break;
}
}
// Bounce off city bounds (left/right only)
if (self.x < 60) {
self.x = 60;
self.direction = 0; // move right
}
if (self.x > 2048 - 60) {
self.x = 2048 - 60;
self.direction = Math.PI; // move left
}
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.inCar = false;
self.car = null;
self.direction = {
x: 0,
y: 0
}; // For movement
self.update = function () {
if (!self.inCar) {
// Store previous position
var prevX = self.x;
var prevY = self.y;
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
break;
}
}
// Clamp to city bounds
if (self.x < 50) self.x = 50;
if (self.x > 2048 - 50) self.x = 2048 - 50;
if (self.y < 100) self.y = 100;
if (self.y > 2732 - 50) self.y = 2732 - 50;
} else if (self.car) {
// Follow car position
self.x = self.car.x;
self.y = self.car.y;
}
};
return self;
});
// Police bullet class
var PoliceBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
self.speed = 16;
self.direction = 0;
self.lifetime = 60; // frames
self.update = function () {
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
self.lifetime--;
// Remove if out of bounds or expired
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732 || self.lifetime <= 0) {
if (typeof policeBullets !== "undefined") {
for (var i = 0; i < policeBullets.length; i++) {
if (policeBullets[i] === self) {
policeBullets.splice(i, 1);
break;
}
}
}
self.destroy();
return;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Police car class
var PoliceCar = Container.expand(function () {
var self = Container.call(this);
var policeSprite = self.attachAsset('police', {
anchorX: 0.5,
anchorY: 0.5
});
// (Gun asset removed from police hand)
self.speed = 4;
self.direction = 0;
self.target = null; // Player or car
self.sirenOn = false;
self.bulletCooldown = 0;
self.update = function () {
if (self.target) {
var prevX = self.x;
var prevY = self.y;
var tx = self.target.x - self.x;
var ty = self.target.y - self.y;
var dist = Math.sqrt(tx * tx + ty * ty);
// Enhanced AI behavior based on distance and wanted level
var aggressionLevel = Math.min(wantedLevel, 5);
var enhancedSpeed = self.speed + aggressionLevel * 1.5; // Faster at higher wanted levels
if (dist > 10) {
// Smart pathfinding around buildings
var directPath = true;
var targetDirection = Math.atan2(ty, tx);
// Check if direct path is blocked by buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
var building = buildings[i];
var bx = building.x - self.x;
var by = building.y - self.y;
var buildingDist = Math.sqrt(bx * bx + by * by);
// If building is between police and target, find alternate path
if (buildingDist < 300) {
var angleToBldg = Math.atan2(by, bx);
var angleDiff = Math.abs(targetDirection - angleToBldg);
if (angleDiff < Math.PI / 3) {
// Building blocks direct path
directPath = false;
// Choose left or right path around building
var leftAngle = angleToBldg - Math.PI / 2;
var rightAngle = angleToBldg + Math.PI / 2;
// Pick the angle that gets closer to target
var leftDx = Math.cos(leftAngle);
var leftDy = Math.sin(leftAngle);
var rightDx = Math.cos(rightAngle);
var rightDy = Math.sin(rightAngle);
if (leftDx * tx + leftDy * ty > rightDx * tx + rightDy * ty) {
self.direction = leftAngle;
} else {
self.direction = rightAngle;
}
break;
}
}
}
if (directPath) {
self.direction = targetDirection;
}
// Predictive targeting - aim where player will be
if (self.target === player && !player.inCar) {
var predictionTime = dist / enhancedSpeed;
var predictedX = player.x + player.direction.x * player.speed * predictionTime;
var predictedY = player.y + player.direction.y * player.speed * predictionTime;
var ptx = predictedX - self.x;
var pty = predictedY - self.y;
if (Math.sqrt(ptx * ptx + pty * pty) < dist) {
self.direction = Math.atan2(pty, ptx);
}
}
// Move with enhanced speed
self.x += Math.cos(self.direction) * enhancedSpeed;
self.y += Math.sin(self.direction) * enhancedSpeed;
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
// Try alternative movement when stuck
self.direction += (Math.random() - 0.5) * Math.PI / 2;
break;
}
}
}
// Enhanced shooting behavior
var shootingRange = 500 + aggressionLevel * 100; // Longer range at higher wanted
var baseCooldown = Math.max(30, 90 - aggressionLevel * 15); // Faster shooting at higher wanted
// Lead target for better accuracy
var aimDirection = self.direction;
if (dist < shootingRange && self.bulletCooldown <= 0 && typeof policeBullets !== "undefined") {
// Predictive aiming for moving targets
if (self.target === player && (player.direction.x !== 0 || player.direction.y !== 0)) {
var bulletSpeed = 16;
var timeToHit = dist / bulletSpeed;
var futureX = player.x + player.direction.x * player.speed * timeToHit;
var futureY = player.y + player.direction.y * player.speed * timeToHit;
aimDirection = Math.atan2(futureY - self.y, futureX - self.x);
}
// Burst fire at high wanted levels
var burstCount = aggressionLevel >= 3 ? 2 : 1;
for (var b = 0; b < burstCount; b++) {
var bullet = new PoliceBullet();
bullet.x = self.x + Math.cos(aimDirection) * 70;
bullet.y = self.y + Math.sin(aimDirection) * 70;
bullet.direction = aimDirection + (b > 0 ? (Math.random() - 0.5) * 0.3 : 0); // Slight spread for burst
policeBullets.push(bullet);
if (typeof game !== "undefined") game.addChild(bullet);
}
self.bulletCooldown = baseCooldown + Math.floor(Math.random() * 30);
}
if (self.bulletCooldown > 0) self.bulletCooldown--;
}
// Clamp to city bounds
if (self.x < 90) self.x = 90;
if (self.x > 2048 - 90) self.x = 2048 - 90;
if (self.y < 100) self.y = 100;
if (self.y > 2732 - 90) self.y = 2732 - 90;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Enhanced police coordination
function updatePoliceCoordination() {
if (policeCars.length > 1 && wantedLevel >= 3) {
// Coordinate police to surround player
for (var i = 0; i < policeCars.length; i++) {
var police = policeCars[i];
var angleOffset = i / policeCars.length * Math.PI * 2;
var surroundDistance = 300;
// Calculate flanking position
var targetX = player.x + Math.cos(angleOffset) * surroundDistance;
var targetY = player.y + Math.sin(angleOffset) * surroundDistance;
// Adjust police target to flanking position instead of direct pursuit
if (Math.random() < 0.3) {
// 30% chance to use flanking behavior
var flankTx = targetX - police.x;
var flankTy = targetY - police.y;
var flankDist = Math.sqrt(flankTx * flankTx + flankTy * flankTy);
if (flankDist > 50) {
police.direction = Math.atan2(flankTy, flankTx);
}
}
}
}
}
// Police spawn logic
// Mission marker: yellow ellipse
// Building: dark gray box
// Road: gray box
// Pedestrian: green ellipse
// Police car: white box with blue
// Car: blue box
// Player character: red box
// City layout
// Arrow key assets
var buildings = [];
var cars = [];
var policeCars = [];
var pedestrians = [];
var missionMarkers = [];
var score = 0;
var wantedLevel = 0;
var missionActive = false;
var missionTarget = null;
var missionText = null;
var dragNode = null;
var policeBullets = [];
var lastTouch = {
x: 0,
y: 0
};
var policeSirenTimer = 0;
// Life bar variables
var life = 900;
var maxLife = 900;
var bulletsHit = 0;
// Helper to update health text
function updateHealthText() {
if (typeof healthText !== "undefined") {
healthText.setText('Sağlık: ' + life);
}
if (typeof updateHearts === "function") updateHearts();
}
// Life bar UI (bottom left)
var lifeBarBg = LK.getAsset('mission', {
anchorX: 0,
anchorY: 1,
scaleX: 4.5,
scaleY: 0.7,
x: 60,
y: 2732 - 60,
tint: 0x222222
});
var lifeBar = LK.getAsset('mission', {
anchorX: 0,
anchorY: 1,
scaleX: 4.4,
scaleY: 0.6,
x: 70,
y: 2732 - 70,
tint: 0x4caf50
});
game.addChild(lifeBarBg);
game.addChild(lifeBar);
// Health text at the bottom left
var healthText = new Text2('Sağlık: 900', {
size: 110,
fill: "#fff"
});
healthText.anchor.set(0, 1);
healthText.x = 80;
healthText.y = 2732 - 10;
game.addChild(healthText);
// --- Health indicator as heart assets (bottom left) ---
var heartIcons = [];
function updateHearts() {
// Remove old hearts
for (var i = 0; i < heartIcons.length; i++) {
if (heartIcons[i].parent) heartIcons[i].parent.removeChild(heartIcons[i]);
}
heartIcons = [];
// Show 3 hearts, full or empty based on health
var heartsToShow = 3;
var fullHearts = Math.max(0, Math.min(3, Math.ceil(life / (maxLife / 3))));
for (var i = 0; i < heartsToShow; i++) {
var assetId = i < fullHearts ? 'heart_full' : 'heart_empty';
var heart = LK.getAsset(assetId, {
anchorX: 0,
anchorY: 1,
x: 40 + i * 100,
y: 2732 - 320,
scaleX: 1.1,
scaleY: 1.1
});
game.addChild(heart);
heartIcons.push(heart);
}
}
updateHearts();
// Add road background
var road = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(road);
// Add buildings only in columns along the left and right edges (no top/bottom rows)
// Left column
for (var j = 0; j < 10; j++) {
var b = new Building();
b.x = 150;
// Add a small random space between houses, but don't overdo it
var yOffset = 200 + j * 210 + Math.floor(Math.random() * 30); // up to 30px random gap
b.y = yOffset;
buildings.push(b);
game.addChild(b);
}
// Right column
for (var j = 0; j < 10; j++) {
var b = new Building();
b.x = 2048 - 150;
// Add a small random space between houses, but don't overdo it
var yOffset = 200 + j * 210 + Math.floor(Math.random() * 30); // up to 30px random gap
b.y = yOffset;
buildings.push(b);
game.addChild(b);
}
// Helper: get a random position not inside any building
function getValidSpawnPosition(minX, maxX, minY, maxY, margin, entityWidth, entityHeight) {
var tries = 0;
var x, y, tempObj;
margin = margin || 0;
entityWidth = entityWidth || 60;
entityHeight = entityHeight || 60;
do {
x = minX + Math.random() * (maxX - minX);
y = minY + Math.random() * (maxY - minY);
// Create a temp object to check intersection
tempObj = {
x: x,
y: y,
width: entityWidth,
height: entityHeight,
intersects: function intersects(b) {
// Simple AABB check
var ax1 = this.x - this.width / 2,
ay1 = this.y - this.height / 2,
ax2 = this.x + this.width / 2,
ay2 = this.y + this.height / 2;
var bx1 = b.x - 135,
by1 = b.y - 135,
bx2 = b.x + 135,
by2 = b.y + 135;
return !(ax2 < bx1 || ax1 > bx2 || ay2 < by1 || ay1 > by2);
}
};
var inside = false;
for (var i = 0; i < buildings.length; i++) {
if (tempObj.intersects(buildings[i])) {
inside = true;
break;
}
}
tries++;
if (tries > 100) break; // fallback, shouldn't happen
} while (inside);
return {
x: x,
y: y
};
}
// Add mission marker
function spawnMission() {
if (missionTarget) {
missionTarget.destroy();
}
var marker = new MissionMarker();
var pos = getValidSpawnPosition(400, 1600, 400, 2200, 0, 80, 80);
marker.x = pos.x;
marker.y = pos.y;
missionMarkers = [marker];
missionTarget = marker;
game.addChild(marker);
missionActive = true;
if (missionText) {
missionText.setText("Go to the yellow marker!");
}
}
spawnMission();
// Add player
var player = new Player();
var playerPos = getValidSpawnPosition(300, 1748, 2732 - 600, 2732 - 100, 0, 120, 120);
player.x = playerPos.x;
player.y = playerPos.y;
game.addChild(player);
// Add cars (AI)
for (var i = 0; i < 5; i++) {
var tryCount = 0;
var valid = false;
var carPos;
while (!valid && tryCount < 30) {
carPos = getValidSpawnPosition(400, 1600, 400, 2200, 0, 180, 90);
valid = true;
// Check not surrounded by other cars (no car within 220px in both x and y)
for (var j = 0; j < cars.length; j++) {
var other = cars[j];
if (Math.abs(carPos.x - other.x) < 220 && Math.abs(carPos.y - other.y) < 220) {
valid = false;
break;
}
}
tryCount++;
}
var c = new Car();
c.x = carPos.x;
c.y = carPos.y;
c.ai = true;
var tgt = getValidSpawnPosition(400, 1600, 400, 2200, 0, 180, 90);
c.aiTarget = {
x: tgt.x,
y: tgt.y
};
cars.push(c);
game.addChild(c);
}
// Add police car (starts inactive)
function spawnPolice() {
var pc = new PoliceCar();
var policePos = getValidSpawnPosition(200, 1800, 200, 2200, 0, 130, 130);
pc.x = policePos.x;
pc.y = policePos.y;
pc.target = player;
policeCars.push(pc);
game.addChild(pc);
}
function removeAllPolice() {
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].destroy();
}
policeCars = [];
}
// Add pedestrians
for (var i = 0; i < 7; i++) {
var p = new Pedestrian();
var pedPos = getValidSpawnPosition(200, 1800, 200, 2400, 0, 120, 120);
p.x = pedPos.x;
p.y = pedPos.y;
pedestrians.push(p);
game.addChild(p);
}
// Score display
var scoreTxt = new Text2('Puan: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Wanted level display
var wantedTxt = new Text2('Aranma: 0', {
size: 70,
fill: 0xFF4444
});
wantedTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(wantedTxt);
wantedTxt.y = 100;
// Mission text
missionText = new Text2('Sarı işarete git!', {
size: 70,
fill: 0xFFE100
});
missionText.anchor.set(0.5, 0);
LK.gui.top.addChild(missionText);
missionText.y = 200;
// Touch controls
var moveTouchId = null;
var moveStart = null;
var moveDir = {
x: 0,
y: 0
};
// Helper: get direction from drag
function getDir(from, to) {
var dx = to.x - from.x;
var dy = to.y - from.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len < 30) return {
x: 0,
y: 0
};
return {
x: dx / len,
y: dy / len
};
}
// --- Arrow Key Touch Controls ---
// Arrow button size and spacing
var arrowBtnSize = 230; // larger
var arrowBtnSpacing = 60; // more spacing for bigger buttons
var arrowBtnAlpha = 0.7;
var arrowBtnTint = 0xeeeeee;
var arrowBtnTintActive = 0x4caf50;
// Arrow button containers
var arrowBtns = {
up: null,
down: null,
left: null,
right: null
};
var arrowBtnStates = {
up: false,
down: false,
left: false,
right: false
};
// Helper: create an arrow button using asset
function createArrowBtn(direction, x, y, rotation) {
var assetId = '';
if (direction === "up") assetId = 'arrow_up';
if (direction === "down") assetId = 'arrow_down';
if (direction === "left") assetId = 'arrow_left';
if (direction === "right") assetId = 'arrow_right';
var btn = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.45,
scaleY: 1.45,
x: x,
y: y,
tint: arrowBtnTint
});
btn.alpha = arrowBtnAlpha;
// Touch handlers
btn.down = function (x, y, obj) {
arrowBtnStates[direction] = true;
btn.tint = arrowBtnTintActive;
btn.alpha = 1;
updateArrowDirection();
};
btn.up = function (x, y, obj) {
arrowBtnStates[direction] = false;
btn.tint = arrowBtnTint;
btn.alpha = arrowBtnAlpha;
updateArrowDirection();
};
// For touch move off the button
btn.out = btn.up;
return btn;
}
// Place arrow buttons further to the left, spaced out
var arrowY = 2732 - 220;
var arrowXRight = 2048 - 420; // move further left from right margin
arrowBtns.left = createArrowBtn("left", arrowXRight - arrowBtnSize - arrowBtnSpacing, arrowY, 0);
arrowBtns.down = createArrowBtn("down", arrowXRight, arrowY, 0);
arrowBtns.right = createArrowBtn("right", arrowXRight + arrowBtnSize + arrowBtnSpacing, arrowY, 0);
arrowBtns.up = createArrowBtn("up", arrowXRight, arrowY - arrowBtnSize - arrowBtnSpacing, 0);
// Add to game
game.addChild(arrowBtns.left);
game.addChild(arrowBtns.down);
game.addChild(arrowBtns.right);
game.addChild(arrowBtns.up);
// Update player direction based on arrow keys
function updateArrowDirection() {
if (player.inCar) {
player.direction = {
x: 0,
y: 0
};
return;
}
var dx = 0,
dy = 0;
if (arrowBtnStates.left) dx -= 1;
if (arrowBtnStates.right) dx += 1;
if (arrowBtnStates.up) dy -= 1;
if (arrowBtnStates.down) dy += 1;
// Normalize
if (dx !== 0 || dy !== 0) {
var len = Math.sqrt(dx * dx + dy * dy);
player.direction = {
x: dx / len,
y: dy / len
};
} else {
player.direction = {
x: 0,
y: 0
};
}
}
// When any arrow is pressed, prevent normal touch movement
for (var dir in arrowBtns) {
(function (direction) {
var btn = arrowBtns[direction];
btn.down = function (x, y, obj) {
arrowBtnStates[direction] = true;
btn.tint = arrowBtnTintActive;
btn.alpha = 1;
updateArrowDirection();
};
btn.up = function (x, y, obj) {
arrowBtnStates[direction] = false;
btn.tint = arrowBtnTint;
btn.alpha = arrowBtnAlpha;
updateArrowDirection();
};
btn.out = btn.up;
})(dir);
}
// Touch down: start movement or enter/exit car
game.down = function (x, y, obj) {
lastTouch.x = x;
lastTouch.y = y;
// If player is near a car and not in car, enter car
if (!player.inCar) {
for (var i = 0; i < cars.length; i++) {
var c = cars[i];
if (!c.driver && Math.abs(player.x - c.x) < 120 && Math.abs(player.y - c.y) < 90) {
// Enter car
player.inCar = true;
player.car = c;
c.driver = player;
LK.getSound('car_enter').play();
return;
}
}
} else if (player.inCar) {
// Exit car
var c = player.car;
player.inCar = false;
player.car = null;
if (c) c.driver = null;
LK.getSound('car_exit').play();
return;
}
// Set player direction toward touch/cursor
// If any arrow key is pressed, ignore normal touch movement
if (!arrowBtnStates.left && !arrowBtnStates.right && !arrowBtnStates.up && !arrowBtnStates.down) {
if (!player.inCar) {
var dx = x - player.x;
var dy = y - player.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 10) {
player.direction = {
x: dx / len,
y: dy / len
};
} else {
player.direction = {
x: 0,
y: 0
};
}
}
moveTouchId = true;
}
};
// Touch move: update direction
game.move = function (x, y, obj) {
lastTouch.x = x;
lastTouch.y = y;
if (moveTouchId && !player.inCar && !arrowBtnStates.left && !arrowBtnStates.right && !arrowBtnStates.up && !arrowBtnStates.down) {
var dx = x - player.x;
var dy = y - player.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 10) {
player.direction = {
x: dx / len,
y: dy / len
};
} else {
player.direction = {
x: 0,
y: 0
};
}
}
};
// Touch up: stop movement
game.up = function (x, y, obj) {
moveTouchId = null;
if (!arrowBtnStates.left && !arrowBtnStates.right && !arrowBtnStates.up && !arrowBtnStates.down) {
player.direction = {
x: 0,
y: 0
};
}
};
// Main update loop
game.update = function () {
// Update player
player.update();
// Update cars
for (var i = 0; i < cars.length; i++) {
cars[i].update();
// AI: cars do not move, so do not pick new targets
}
// Update police coordination
updatePoliceCoordination();
// Update police
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].update();
// Siren sound (more frequent at higher wanted levels)
var sirenFreq = Math.max(30, 90 - wantedLevel * 15);
if (wantedLevel > 0 && LK.ticks % sirenFreq === 0) {
LK.getSound('police_siren').play();
}
// Police catch player
// (No game over when police touch player or player's car)
if (policeCars[i].intersects(player)) {
// Optionally, you could add a visual effect or wanted level increase here
}
// Police catch player's car
if (player.inCar && policeCars[i].intersects(player.car)) {
// Optionally, you could add a visual effect or wanted level increase here
}
}
// Update police bullets and check collision with player
for (var i = policeBullets.length - 1; i >= 0; i--) {
var bullet = policeBullets[i];
bullet.update();
// Only check collision if bullet is still in game
if (bullet && player && bullet.intersects(player)) {
bulletsHit++;
// Remove bullet on hit
policeBullets.splice(i, 1);
bullet.destroy();
// Flash effect
LK.effects.flashScreen(0xff0000, 400);
// Decrease health by 300 for each bullet bought (hit)
life = Math.max(0, life - 300);
updateHealthText();
// Update life bar width and color
var lifeFrac = life / maxLife;
lifeBar.scaleX = 4.4 * lifeFrac;
if (lifeFrac > 0.6) {
lifeBar.tint = 0x4caf50;
} else if (lifeFrac > 0.3) {
lifeBar.tint = 0xffe100;
} else {
lifeBar.tint = 0xff4444;
}
// Game over if 3 bullets hit or health is 0
if (bulletsHit >= 3 || life <= 0) {
LK.showGameOver();
return;
}
}
}
// Update pedestrians
for (var i = 0; i < pedestrians.length; i++) {
pedestrians[i].update();
// If player or car hits pedestrian, increase wanted level
if (!player.inCar && player.intersects(pedestrians[i])) {
wantedLevel = Math.min(5, wantedLevel + 1);
LK.effects.flashObject(pedestrians[i], 0xff0000, 500);
}
if (player.inCar && player.car && player.car.intersects(pedestrians[i])) {
wantedLevel = Math.min(5, wantedLevel + 1);
LK.effects.flashObject(pedestrians[i], 0xff0000, 500);
}
}
// Update mission marker
if (missionActive && missionTarget && player.intersects(missionTarget)) {
// Complete mission
LK.getSound('mission_complete').play();
score += 100;
scoreTxt.setText('Puan: ' + score);
missionActive = false;
missionTarget.destroy();
missionTarget = null;
missionText.setText("Görev Tamamlandı!");
// Next mission after short delay
LK.setTimeout(function () {
missionText.setText("Sarı işarete git!");
spawnMission();
}, 1200);
}
// Update wanted level
wantedTxt.setText('Aranma: ' + wantedLevel);
// Enhanced police spawn logic with scaling
var maxPolice = Math.min(wantedLevel, 4); // Up to 4 police cars at max wanted level
if (wantedLevel > 0 && policeCars.length < maxPolice) {
// Spawn additional police more frequently at higher wanted levels
if (LK.ticks % (180 - wantedLevel * 20) === 0) {
spawnPolice();
}
}
if (wantedLevel === 0 && policeCars.length > 0) {
removeAllPolice();
}
// Wanted level decay
if (wantedLevel > 0 && LK.ticks % 180 === 0) {
wantedLevel--;
}
// Win condition: score
if (score >= 1000) {
LK.showYouWin();
return;
}
// Reset life bar if game restarted (life > maxLife means new game)
if (life > maxLife) {
life = maxLife;
bulletsHit = 0;
lifeBar.scaleX = 4.4;
lifeBar.tint = 0x4caf50;
updateHealthText();
if (typeof updateHearts === "function") updateHearts();
}
};
// Prevent elements in top left 100x100
// (All GUI elements are centered or offset downward)
// --- Language selection UI ---
// Add a language icon texture at bottom left
// Use pedestrian icon as placeholder
var langIcon = LK.getAsset('lang_icon', {
anchorX: 0,
anchorY: 1,
x: 20,
y: 2732 - 20,
scaleX: 1,
scaleY: 1
});
game.addChild(langIcon);
// Language popup container (hidden by default)
var langPopup = new Container();
langPopup.visible = false;
langPopup.x = 2048 / 2;
langPopup.y = 2732 / 2;
// Popup background
var popupBg = LK.getAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 7,
scaleY: 4,
tint: 0x222222
});
langPopup.addChild(popupBg);
// "Select Language" text
var langTitle = new Text2("Select Language", {
size: 90,
fill: "#fff"
});
langTitle.anchor.set(0.5, 0);
langTitle.y = -180;
langPopup.addChild(langTitle);
// English button
var enBtnBg = LK.getAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.5,
scaleY: 1.2,
tint: 0xeeeeee,
y: -30
});
langPopup.addChild(enBtnBg);
var enBtnText = new Text2("English", {
size: 80,
fill: "#222"
});
enBtnText.anchor.set(0.5, 0.5);
enBtnText.y = -30;
langPopup.addChild(enBtnText);
// Turkish button
var trBtnBg = LK.getAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.5,
scaleY: 1.2,
tint: 0xeeeeee,
y: 100
});
langPopup.addChild(trBtnBg);
var trBtnText = new Text2("Türkçe", {
size: 80,
fill: "#222"
});
trBtnText.anchor.set(0.5, 0.5);
trBtnText.y = 100;
langPopup.addChild(trBtnText);
game.addChild(langPopup);
// Language state
var language = "tr"; // default Turkish
function setLanguage(lang) {
language = lang;
if (lang === "tr") {
scoreTxt.setText('Puan: ' + score);
wantedTxt.setText('Aranma: ' + wantedLevel);
missionText.setText(missionActive ? "Sarı işarete git!" : "Görev Tamamlandı!");
healthText.setText('Sağlık: ' + life);
langTitle.setText("Dil Seçimi");
enBtnText.setText("İngilizce");
trBtnText.setText("Türkçe");
} else {
scoreTxt.setText('Score: ' + score);
wantedTxt.setText('Wanted: ' + wantedLevel);
missionText.setText(missionActive ? "Go to the yellow marker!" : "Mission Complete!");
healthText.setText('Health: ' + life);
langTitle.setText("Select Language");
enBtnText.setText("English");
trBtnText.setText("Turkish");
}
}
// Show popup on lang icon press
langIcon.down = function (x, y, obj) {
langPopup.visible = true;
};
// Hide popup when clicking outside (optional, not required by prompt)
// Add down handler to popup background to prevent propagation
popupBg.down = function (x, y, obj) {};
// English button press
enBtnBg.down = function (x, y, obj) {
setLanguage("en");
langPopup.visible = false;
};
enBtnText.down = enBtnBg.down;
// Turkish button press
trBtnBg.down = function (x, y, obj) {
setLanguage("tr");
langPopup.visible = false;
};
trBtnText.down = trBtnBg.down;
// On game start, set initial language
setLanguage(language);
// End of MVP code for GTA7: Micro City Mayhem; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Building class
var Building = Container.expand(function () {
var self = Container.call(this);
var bldg = self.attachAsset('building', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
// Car class
var Car = Container.expand(function () {
var self = Container.call(this);
var carSprite = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 18;
self.direction = 0; // in radians, 0 = right
self.driver = null; // Player or null
self.ai = false;
self.aiTarget = null;
self.update = function () {
// Store previous position for collision revert
var prevX = self.x;
var prevY = self.y;
// Cars do not move; skip player and AI movement logic
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
break;
}
}
// Clamp to city bounds
if (self.x < 90) self.x = 90;
if (self.x > 2048 - 90) self.x = 2048 - 90;
if (self.y < 100) self.y = 100;
if (self.y > 2732 - 90) self.y = 2732 - 90;
};
return self;
});
// Mission marker class
var MissionMarker = Container.expand(function () {
var self = Container.call(this);
var marker = self.attachAsset('mission', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = true;
return self;
});
// Pedestrian class
var Pedestrian = Container.expand(function () {
var self = Container.call(this);
var pedSprite = self.attachAsset('pedestrian', {
anchorX: 0.5,
anchorY: 0.5
});
// Pedestrians only move left and right in order
self.speed = 4 + Math.random() * 4;
self.direction = Math.random() < 0.5 ? 0 : Math.PI; // 0 = right, PI = left
self.update = function () {
var prevX = self.x;
var prevY = self.y;
// Move only in X direction (left/right)
self.x += Math.cos(self.direction) * self.speed;
// No Y movement
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
// Reverse direction on building collision
self.direction = self.direction === 0 ? Math.PI : 0;
break;
}
}
// Bounce off city bounds (left/right only)
if (self.x < 60) {
self.x = 60;
self.direction = 0; // move right
}
if (self.x > 2048 - 60) {
self.x = 2048 - 60;
self.direction = Math.PI; // move left
}
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.inCar = false;
self.car = null;
self.direction = {
x: 0,
y: 0
}; // For movement
self.update = function () {
if (!self.inCar) {
// Store previous position
var prevX = self.x;
var prevY = self.y;
self.x += self.direction.x * self.speed;
self.y += self.direction.y * self.speed;
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
break;
}
}
// Clamp to city bounds
if (self.x < 50) self.x = 50;
if (self.x > 2048 - 50) self.x = 2048 - 50;
if (self.y < 100) self.y = 100;
if (self.y > 2732 - 50) self.y = 2732 - 50;
} else if (self.car) {
// Follow car position
self.x = self.car.x;
self.y = self.car.y;
}
};
return self;
});
// Police bullet class
var PoliceBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
self.speed = 16;
self.direction = 0;
self.lifetime = 60; // frames
self.update = function () {
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
self.lifetime--;
// Remove if out of bounds or expired
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732 || self.lifetime <= 0) {
if (typeof policeBullets !== "undefined") {
for (var i = 0; i < policeBullets.length; i++) {
if (policeBullets[i] === self) {
policeBullets.splice(i, 1);
break;
}
}
}
self.destroy();
return;
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// Police car class
var PoliceCar = Container.expand(function () {
var self = Container.call(this);
var policeSprite = self.attachAsset('police', {
anchorX: 0.5,
anchorY: 0.5
});
// (Gun asset removed from police hand)
self.speed = 4;
self.direction = 0;
self.target = null; // Player or car
self.sirenOn = false;
self.bulletCooldown = 0;
self.update = function () {
if (self.target) {
var prevX = self.x;
var prevY = self.y;
var tx = self.target.x - self.x;
var ty = self.target.y - self.y;
var dist = Math.sqrt(tx * tx + ty * ty);
// Enhanced AI behavior based on distance and wanted level
var aggressionLevel = Math.min(wantedLevel, 5);
var enhancedSpeed = self.speed + aggressionLevel * 1.5; // Faster at higher wanted levels
if (dist > 10) {
// Smart pathfinding around buildings
var directPath = true;
var targetDirection = Math.atan2(ty, tx);
// Check if direct path is blocked by buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
var building = buildings[i];
var bx = building.x - self.x;
var by = building.y - self.y;
var buildingDist = Math.sqrt(bx * bx + by * by);
// If building is between police and target, find alternate path
if (buildingDist < 300) {
var angleToBldg = Math.atan2(by, bx);
var angleDiff = Math.abs(targetDirection - angleToBldg);
if (angleDiff < Math.PI / 3) {
// Building blocks direct path
directPath = false;
// Choose left or right path around building
var leftAngle = angleToBldg - Math.PI / 2;
var rightAngle = angleToBldg + Math.PI / 2;
// Pick the angle that gets closer to target
var leftDx = Math.cos(leftAngle);
var leftDy = Math.sin(leftAngle);
var rightDx = Math.cos(rightAngle);
var rightDy = Math.sin(rightAngle);
if (leftDx * tx + leftDy * ty > rightDx * tx + rightDy * ty) {
self.direction = leftAngle;
} else {
self.direction = rightAngle;
}
break;
}
}
}
if (directPath) {
self.direction = targetDirection;
}
// Predictive targeting - aim where player will be
if (self.target === player && !player.inCar) {
var predictionTime = dist / enhancedSpeed;
var predictedX = player.x + player.direction.x * player.speed * predictionTime;
var predictedY = player.y + player.direction.y * player.speed * predictionTime;
var ptx = predictedX - self.x;
var pty = predictedY - self.y;
if (Math.sqrt(ptx * ptx + pty * pty) < dist) {
self.direction = Math.atan2(pty, ptx);
}
}
// Move with enhanced speed
self.x += Math.cos(self.direction) * enhancedSpeed;
self.y += Math.sin(self.direction) * enhancedSpeed;
// Prevent passing through buildings
for (var i = 0; typeof buildings !== "undefined" && i < buildings.length; i++) {
if (self.intersects(buildings[i])) {
self.x = prevX;
self.y = prevY;
// Try alternative movement when stuck
self.direction += (Math.random() - 0.5) * Math.PI / 2;
break;
}
}
}
// Enhanced shooting behavior
var shootingRange = 500 + aggressionLevel * 100; // Longer range at higher wanted
var baseCooldown = Math.max(30, 90 - aggressionLevel * 15); // Faster shooting at higher wanted
// Lead target for better accuracy
var aimDirection = self.direction;
if (dist < shootingRange && self.bulletCooldown <= 0 && typeof policeBullets !== "undefined") {
// Predictive aiming for moving targets
if (self.target === player && (player.direction.x !== 0 || player.direction.y !== 0)) {
var bulletSpeed = 16;
var timeToHit = dist / bulletSpeed;
var futureX = player.x + player.direction.x * player.speed * timeToHit;
var futureY = player.y + player.direction.y * player.speed * timeToHit;
aimDirection = Math.atan2(futureY - self.y, futureX - self.x);
}
// Burst fire at high wanted levels
var burstCount = aggressionLevel >= 3 ? 2 : 1;
for (var b = 0; b < burstCount; b++) {
var bullet = new PoliceBullet();
bullet.x = self.x + Math.cos(aimDirection) * 70;
bullet.y = self.y + Math.sin(aimDirection) * 70;
bullet.direction = aimDirection + (b > 0 ? (Math.random() - 0.5) * 0.3 : 0); // Slight spread for burst
policeBullets.push(bullet);
if (typeof game !== "undefined") game.addChild(bullet);
}
self.bulletCooldown = baseCooldown + Math.floor(Math.random() * 30);
}
if (self.bulletCooldown > 0) self.bulletCooldown--;
}
// Clamp to city bounds
if (self.x < 90) self.x = 90;
if (self.x > 2048 - 90) self.x = 2048 - 90;
if (self.y < 100) self.y = 100;
if (self.y > 2732 - 90) self.y = 2732 - 90;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// Enhanced police coordination
function updatePoliceCoordination() {
if (policeCars.length > 1 && wantedLevel >= 3) {
// Coordinate police to surround player
for (var i = 0; i < policeCars.length; i++) {
var police = policeCars[i];
var angleOffset = i / policeCars.length * Math.PI * 2;
var surroundDistance = 300;
// Calculate flanking position
var targetX = player.x + Math.cos(angleOffset) * surroundDistance;
var targetY = player.y + Math.sin(angleOffset) * surroundDistance;
// Adjust police target to flanking position instead of direct pursuit
if (Math.random() < 0.3) {
// 30% chance to use flanking behavior
var flankTx = targetX - police.x;
var flankTy = targetY - police.y;
var flankDist = Math.sqrt(flankTx * flankTx + flankTy * flankTy);
if (flankDist > 50) {
police.direction = Math.atan2(flankTy, flankTx);
}
}
}
}
}
// Police spawn logic
// Mission marker: yellow ellipse
// Building: dark gray box
// Road: gray box
// Pedestrian: green ellipse
// Police car: white box with blue
// Car: blue box
// Player character: red box
// City layout
// Arrow key assets
var buildings = [];
var cars = [];
var policeCars = [];
var pedestrians = [];
var missionMarkers = [];
var score = 0;
var wantedLevel = 0;
var missionActive = false;
var missionTarget = null;
var missionText = null;
var dragNode = null;
var policeBullets = [];
var lastTouch = {
x: 0,
y: 0
};
var policeSirenTimer = 0;
// Life bar variables
var life = 900;
var maxLife = 900;
var bulletsHit = 0;
// Helper to update health text
function updateHealthText() {
if (typeof healthText !== "undefined") {
healthText.setText('Sağlık: ' + life);
}
if (typeof updateHearts === "function") updateHearts();
}
// Life bar UI (bottom left)
var lifeBarBg = LK.getAsset('mission', {
anchorX: 0,
anchorY: 1,
scaleX: 4.5,
scaleY: 0.7,
x: 60,
y: 2732 - 60,
tint: 0x222222
});
var lifeBar = LK.getAsset('mission', {
anchorX: 0,
anchorY: 1,
scaleX: 4.4,
scaleY: 0.6,
x: 70,
y: 2732 - 70,
tint: 0x4caf50
});
game.addChild(lifeBarBg);
game.addChild(lifeBar);
// Health text at the bottom left
var healthText = new Text2('Sağlık: 900', {
size: 110,
fill: "#fff"
});
healthText.anchor.set(0, 1);
healthText.x = 80;
healthText.y = 2732 - 10;
game.addChild(healthText);
// --- Health indicator as heart assets (bottom left) ---
var heartIcons = [];
function updateHearts() {
// Remove old hearts
for (var i = 0; i < heartIcons.length; i++) {
if (heartIcons[i].parent) heartIcons[i].parent.removeChild(heartIcons[i]);
}
heartIcons = [];
// Show 3 hearts, full or empty based on health
var heartsToShow = 3;
var fullHearts = Math.max(0, Math.min(3, Math.ceil(life / (maxLife / 3))));
for (var i = 0; i < heartsToShow; i++) {
var assetId = i < fullHearts ? 'heart_full' : 'heart_empty';
var heart = LK.getAsset(assetId, {
anchorX: 0,
anchorY: 1,
x: 40 + i * 100,
y: 2732 - 320,
scaleX: 1.1,
scaleY: 1.1
});
game.addChild(heart);
heartIcons.push(heart);
}
}
updateHearts();
// Add road background
var road = LK.getAsset('road', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
game.addChild(road);
// Add buildings only in columns along the left and right edges (no top/bottom rows)
// Left column
for (var j = 0; j < 10; j++) {
var b = new Building();
b.x = 150;
// Add a small random space between houses, but don't overdo it
var yOffset = 200 + j * 210 + Math.floor(Math.random() * 30); // up to 30px random gap
b.y = yOffset;
buildings.push(b);
game.addChild(b);
}
// Right column
for (var j = 0; j < 10; j++) {
var b = new Building();
b.x = 2048 - 150;
// Add a small random space between houses, but don't overdo it
var yOffset = 200 + j * 210 + Math.floor(Math.random() * 30); // up to 30px random gap
b.y = yOffset;
buildings.push(b);
game.addChild(b);
}
// Helper: get a random position not inside any building
function getValidSpawnPosition(minX, maxX, minY, maxY, margin, entityWidth, entityHeight) {
var tries = 0;
var x, y, tempObj;
margin = margin || 0;
entityWidth = entityWidth || 60;
entityHeight = entityHeight || 60;
do {
x = minX + Math.random() * (maxX - minX);
y = minY + Math.random() * (maxY - minY);
// Create a temp object to check intersection
tempObj = {
x: x,
y: y,
width: entityWidth,
height: entityHeight,
intersects: function intersects(b) {
// Simple AABB check
var ax1 = this.x - this.width / 2,
ay1 = this.y - this.height / 2,
ax2 = this.x + this.width / 2,
ay2 = this.y + this.height / 2;
var bx1 = b.x - 135,
by1 = b.y - 135,
bx2 = b.x + 135,
by2 = b.y + 135;
return !(ax2 < bx1 || ax1 > bx2 || ay2 < by1 || ay1 > by2);
}
};
var inside = false;
for (var i = 0; i < buildings.length; i++) {
if (tempObj.intersects(buildings[i])) {
inside = true;
break;
}
}
tries++;
if (tries > 100) break; // fallback, shouldn't happen
} while (inside);
return {
x: x,
y: y
};
}
// Add mission marker
function spawnMission() {
if (missionTarget) {
missionTarget.destroy();
}
var marker = new MissionMarker();
var pos = getValidSpawnPosition(400, 1600, 400, 2200, 0, 80, 80);
marker.x = pos.x;
marker.y = pos.y;
missionMarkers = [marker];
missionTarget = marker;
game.addChild(marker);
missionActive = true;
if (missionText) {
missionText.setText("Go to the yellow marker!");
}
}
spawnMission();
// Add player
var player = new Player();
var playerPos = getValidSpawnPosition(300, 1748, 2732 - 600, 2732 - 100, 0, 120, 120);
player.x = playerPos.x;
player.y = playerPos.y;
game.addChild(player);
// Add cars (AI)
for (var i = 0; i < 5; i++) {
var tryCount = 0;
var valid = false;
var carPos;
while (!valid && tryCount < 30) {
carPos = getValidSpawnPosition(400, 1600, 400, 2200, 0, 180, 90);
valid = true;
// Check not surrounded by other cars (no car within 220px in both x and y)
for (var j = 0; j < cars.length; j++) {
var other = cars[j];
if (Math.abs(carPos.x - other.x) < 220 && Math.abs(carPos.y - other.y) < 220) {
valid = false;
break;
}
}
tryCount++;
}
var c = new Car();
c.x = carPos.x;
c.y = carPos.y;
c.ai = true;
var tgt = getValidSpawnPosition(400, 1600, 400, 2200, 0, 180, 90);
c.aiTarget = {
x: tgt.x,
y: tgt.y
};
cars.push(c);
game.addChild(c);
}
// Add police car (starts inactive)
function spawnPolice() {
var pc = new PoliceCar();
var policePos = getValidSpawnPosition(200, 1800, 200, 2200, 0, 130, 130);
pc.x = policePos.x;
pc.y = policePos.y;
pc.target = player;
policeCars.push(pc);
game.addChild(pc);
}
function removeAllPolice() {
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].destroy();
}
policeCars = [];
}
// Add pedestrians
for (var i = 0; i < 7; i++) {
var p = new Pedestrian();
var pedPos = getValidSpawnPosition(200, 1800, 200, 2400, 0, 120, 120);
p.x = pedPos.x;
p.y = pedPos.y;
pedestrians.push(p);
game.addChild(p);
}
// Score display
var scoreTxt = new Text2('Puan: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Wanted level display
var wantedTxt = new Text2('Aranma: 0', {
size: 70,
fill: 0xFF4444
});
wantedTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(wantedTxt);
wantedTxt.y = 100;
// Mission text
missionText = new Text2('Sarı işarete git!', {
size: 70,
fill: 0xFFE100
});
missionText.anchor.set(0.5, 0);
LK.gui.top.addChild(missionText);
missionText.y = 200;
// Touch controls
var moveTouchId = null;
var moveStart = null;
var moveDir = {
x: 0,
y: 0
};
// Helper: get direction from drag
function getDir(from, to) {
var dx = to.x - from.x;
var dy = to.y - from.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len < 30) return {
x: 0,
y: 0
};
return {
x: dx / len,
y: dy / len
};
}
// --- Arrow Key Touch Controls ---
// Arrow button size and spacing
var arrowBtnSize = 230; // larger
var arrowBtnSpacing = 60; // more spacing for bigger buttons
var arrowBtnAlpha = 0.7;
var arrowBtnTint = 0xeeeeee;
var arrowBtnTintActive = 0x4caf50;
// Arrow button containers
var arrowBtns = {
up: null,
down: null,
left: null,
right: null
};
var arrowBtnStates = {
up: false,
down: false,
left: false,
right: false
};
// Helper: create an arrow button using asset
function createArrowBtn(direction, x, y, rotation) {
var assetId = '';
if (direction === "up") assetId = 'arrow_up';
if (direction === "down") assetId = 'arrow_down';
if (direction === "left") assetId = 'arrow_left';
if (direction === "right") assetId = 'arrow_right';
var btn = LK.getAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.45,
scaleY: 1.45,
x: x,
y: y,
tint: arrowBtnTint
});
btn.alpha = arrowBtnAlpha;
// Touch handlers
btn.down = function (x, y, obj) {
arrowBtnStates[direction] = true;
btn.tint = arrowBtnTintActive;
btn.alpha = 1;
updateArrowDirection();
};
btn.up = function (x, y, obj) {
arrowBtnStates[direction] = false;
btn.tint = arrowBtnTint;
btn.alpha = arrowBtnAlpha;
updateArrowDirection();
};
// For touch move off the button
btn.out = btn.up;
return btn;
}
// Place arrow buttons further to the left, spaced out
var arrowY = 2732 - 220;
var arrowXRight = 2048 - 420; // move further left from right margin
arrowBtns.left = createArrowBtn("left", arrowXRight - arrowBtnSize - arrowBtnSpacing, arrowY, 0);
arrowBtns.down = createArrowBtn("down", arrowXRight, arrowY, 0);
arrowBtns.right = createArrowBtn("right", arrowXRight + arrowBtnSize + arrowBtnSpacing, arrowY, 0);
arrowBtns.up = createArrowBtn("up", arrowXRight, arrowY - arrowBtnSize - arrowBtnSpacing, 0);
// Add to game
game.addChild(arrowBtns.left);
game.addChild(arrowBtns.down);
game.addChild(arrowBtns.right);
game.addChild(arrowBtns.up);
// Update player direction based on arrow keys
function updateArrowDirection() {
if (player.inCar) {
player.direction = {
x: 0,
y: 0
};
return;
}
var dx = 0,
dy = 0;
if (arrowBtnStates.left) dx -= 1;
if (arrowBtnStates.right) dx += 1;
if (arrowBtnStates.up) dy -= 1;
if (arrowBtnStates.down) dy += 1;
// Normalize
if (dx !== 0 || dy !== 0) {
var len = Math.sqrt(dx * dx + dy * dy);
player.direction = {
x: dx / len,
y: dy / len
};
} else {
player.direction = {
x: 0,
y: 0
};
}
}
// When any arrow is pressed, prevent normal touch movement
for (var dir in arrowBtns) {
(function (direction) {
var btn = arrowBtns[direction];
btn.down = function (x, y, obj) {
arrowBtnStates[direction] = true;
btn.tint = arrowBtnTintActive;
btn.alpha = 1;
updateArrowDirection();
};
btn.up = function (x, y, obj) {
arrowBtnStates[direction] = false;
btn.tint = arrowBtnTint;
btn.alpha = arrowBtnAlpha;
updateArrowDirection();
};
btn.out = btn.up;
})(dir);
}
// Touch down: start movement or enter/exit car
game.down = function (x, y, obj) {
lastTouch.x = x;
lastTouch.y = y;
// If player is near a car and not in car, enter car
if (!player.inCar) {
for (var i = 0; i < cars.length; i++) {
var c = cars[i];
if (!c.driver && Math.abs(player.x - c.x) < 120 && Math.abs(player.y - c.y) < 90) {
// Enter car
player.inCar = true;
player.car = c;
c.driver = player;
LK.getSound('car_enter').play();
return;
}
}
} else if (player.inCar) {
// Exit car
var c = player.car;
player.inCar = false;
player.car = null;
if (c) c.driver = null;
LK.getSound('car_exit').play();
return;
}
// Set player direction toward touch/cursor
// If any arrow key is pressed, ignore normal touch movement
if (!arrowBtnStates.left && !arrowBtnStates.right && !arrowBtnStates.up && !arrowBtnStates.down) {
if (!player.inCar) {
var dx = x - player.x;
var dy = y - player.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 10) {
player.direction = {
x: dx / len,
y: dy / len
};
} else {
player.direction = {
x: 0,
y: 0
};
}
}
moveTouchId = true;
}
};
// Touch move: update direction
game.move = function (x, y, obj) {
lastTouch.x = x;
lastTouch.y = y;
if (moveTouchId && !player.inCar && !arrowBtnStates.left && !arrowBtnStates.right && !arrowBtnStates.up && !arrowBtnStates.down) {
var dx = x - player.x;
var dy = y - player.y;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 10) {
player.direction = {
x: dx / len,
y: dy / len
};
} else {
player.direction = {
x: 0,
y: 0
};
}
}
};
// Touch up: stop movement
game.up = function (x, y, obj) {
moveTouchId = null;
if (!arrowBtnStates.left && !arrowBtnStates.right && !arrowBtnStates.up && !arrowBtnStates.down) {
player.direction = {
x: 0,
y: 0
};
}
};
// Main update loop
game.update = function () {
// Update player
player.update();
// Update cars
for (var i = 0; i < cars.length; i++) {
cars[i].update();
// AI: cars do not move, so do not pick new targets
}
// Update police coordination
updatePoliceCoordination();
// Update police
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].update();
// Siren sound (more frequent at higher wanted levels)
var sirenFreq = Math.max(30, 90 - wantedLevel * 15);
if (wantedLevel > 0 && LK.ticks % sirenFreq === 0) {
LK.getSound('police_siren').play();
}
// Police catch player
// (No game over when police touch player or player's car)
if (policeCars[i].intersects(player)) {
// Optionally, you could add a visual effect or wanted level increase here
}
// Police catch player's car
if (player.inCar && policeCars[i].intersects(player.car)) {
// Optionally, you could add a visual effect or wanted level increase here
}
}
// Update police bullets and check collision with player
for (var i = policeBullets.length - 1; i >= 0; i--) {
var bullet = policeBullets[i];
bullet.update();
// Only check collision if bullet is still in game
if (bullet && player && bullet.intersects(player)) {
bulletsHit++;
// Remove bullet on hit
policeBullets.splice(i, 1);
bullet.destroy();
// Flash effect
LK.effects.flashScreen(0xff0000, 400);
// Decrease health by 300 for each bullet bought (hit)
life = Math.max(0, life - 300);
updateHealthText();
// Update life bar width and color
var lifeFrac = life / maxLife;
lifeBar.scaleX = 4.4 * lifeFrac;
if (lifeFrac > 0.6) {
lifeBar.tint = 0x4caf50;
} else if (lifeFrac > 0.3) {
lifeBar.tint = 0xffe100;
} else {
lifeBar.tint = 0xff4444;
}
// Game over if 3 bullets hit or health is 0
if (bulletsHit >= 3 || life <= 0) {
LK.showGameOver();
return;
}
}
}
// Update pedestrians
for (var i = 0; i < pedestrians.length; i++) {
pedestrians[i].update();
// If player or car hits pedestrian, increase wanted level
if (!player.inCar && player.intersects(pedestrians[i])) {
wantedLevel = Math.min(5, wantedLevel + 1);
LK.effects.flashObject(pedestrians[i], 0xff0000, 500);
}
if (player.inCar && player.car && player.car.intersects(pedestrians[i])) {
wantedLevel = Math.min(5, wantedLevel + 1);
LK.effects.flashObject(pedestrians[i], 0xff0000, 500);
}
}
// Update mission marker
if (missionActive && missionTarget && player.intersects(missionTarget)) {
// Complete mission
LK.getSound('mission_complete').play();
score += 100;
scoreTxt.setText('Puan: ' + score);
missionActive = false;
missionTarget.destroy();
missionTarget = null;
missionText.setText("Görev Tamamlandı!");
// Next mission after short delay
LK.setTimeout(function () {
missionText.setText("Sarı işarete git!");
spawnMission();
}, 1200);
}
// Update wanted level
wantedTxt.setText('Aranma: ' + wantedLevel);
// Enhanced police spawn logic with scaling
var maxPolice = Math.min(wantedLevel, 4); // Up to 4 police cars at max wanted level
if (wantedLevel > 0 && policeCars.length < maxPolice) {
// Spawn additional police more frequently at higher wanted levels
if (LK.ticks % (180 - wantedLevel * 20) === 0) {
spawnPolice();
}
}
if (wantedLevel === 0 && policeCars.length > 0) {
removeAllPolice();
}
// Wanted level decay
if (wantedLevel > 0 && LK.ticks % 180 === 0) {
wantedLevel--;
}
// Win condition: score
if (score >= 1000) {
LK.showYouWin();
return;
}
// Reset life bar if game restarted (life > maxLife means new game)
if (life > maxLife) {
life = maxLife;
bulletsHit = 0;
lifeBar.scaleX = 4.4;
lifeBar.tint = 0x4caf50;
updateHealthText();
if (typeof updateHearts === "function") updateHearts();
}
};
// Prevent elements in top left 100x100
// (All GUI elements are centered or offset downward)
// --- Language selection UI ---
// Add a language icon texture at bottom left
// Use pedestrian icon as placeholder
var langIcon = LK.getAsset('lang_icon', {
anchorX: 0,
anchorY: 1,
x: 20,
y: 2732 - 20,
scaleX: 1,
scaleY: 1
});
game.addChild(langIcon);
// Language popup container (hidden by default)
var langPopup = new Container();
langPopup.visible = false;
langPopup.x = 2048 / 2;
langPopup.y = 2732 / 2;
// Popup background
var popupBg = LK.getAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 7,
scaleY: 4,
tint: 0x222222
});
langPopup.addChild(popupBg);
// "Select Language" text
var langTitle = new Text2("Select Language", {
size: 90,
fill: "#fff"
});
langTitle.anchor.set(0.5, 0);
langTitle.y = -180;
langPopup.addChild(langTitle);
// English button
var enBtnBg = LK.getAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.5,
scaleY: 1.2,
tint: 0xeeeeee,
y: -30
});
langPopup.addChild(enBtnBg);
var enBtnText = new Text2("English", {
size: 80,
fill: "#222"
});
enBtnText.anchor.set(0.5, 0.5);
enBtnText.y = -30;
langPopup.addChild(enBtnText);
// Turkish button
var trBtnBg = LK.getAsset('mission', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.5,
scaleY: 1.2,
tint: 0xeeeeee,
y: 100
});
langPopup.addChild(trBtnBg);
var trBtnText = new Text2("Türkçe", {
size: 80,
fill: "#222"
});
trBtnText.anchor.set(0.5, 0.5);
trBtnText.y = 100;
langPopup.addChild(trBtnText);
game.addChild(langPopup);
// Language state
var language = "tr"; // default Turkish
function setLanguage(lang) {
language = lang;
if (lang === "tr") {
scoreTxt.setText('Puan: ' + score);
wantedTxt.setText('Aranma: ' + wantedLevel);
missionText.setText(missionActive ? "Sarı işarete git!" : "Görev Tamamlandı!");
healthText.setText('Sağlık: ' + life);
langTitle.setText("Dil Seçimi");
enBtnText.setText("İngilizce");
trBtnText.setText("Türkçe");
} else {
scoreTxt.setText('Score: ' + score);
wantedTxt.setText('Wanted: ' + wantedLevel);
missionText.setText(missionActive ? "Go to the yellow marker!" : "Mission Complete!");
healthText.setText('Health: ' + life);
langTitle.setText("Select Language");
enBtnText.setText("English");
trBtnText.setText("Turkish");
}
}
// Show popup on lang icon press
langIcon.down = function (x, y, obj) {
langPopup.visible = true;
};
// Hide popup when clicking outside (optional, not required by prompt)
// Add down handler to popup background to prevent propagation
popupBg.down = function (x, y, obj) {};
// English button press
enBtnBg.down = function (x, y, obj) {
setLanguage("en");
langPopup.visible = false;
};
enBtnText.down = enBtnBg.down;
// Turkish button press
trBtnBg.down = function (x, y, obj) {
setLanguage("tr");
langPopup.visible = false;
};
trBtnText.down = trBtnBg.down;
// On game start, set initial language
setLanguage(language);
// End of MVP code for GTA7: Micro City Mayhem;
the modern gun. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
delete background
delete background
delete background
Draw a modern road viewed from above. In-Game asset. 2d. High contrast. No shadows
delete background
arrow pointing up. In-Game asset. 2d. High contrast. No shadows
arrow pointing down. In-Game asset. 2d. High contrast. No shadows
arrow pointing right. In-Game asset. 2d. High contrast. No shadows
arrow pointing left. In-Game asset. 2d. High contrast. No shadows
delete background
delete background