/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bullet class var Bullet = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 48; self.dir = 0; self.owner = null; // 'player' or 'police' self.update = function () { self.x += Math.cos(self.dir) * self.speed; self.y += Math.sin(self.dir) * self.speed; }; return self; }); // Car (stealable) var Car = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('car', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0; self.maxSpeed = 5; // much slower self.acceleration = 0.2; // slower acceleration self.deceleration = 0.3; // slower deceleration self.direction = 0; // radians self.driver = null; // player if inside self.isPolice = false; self.targetDir = 0; // for smooth steering self.update = function () { if (self.driver) { // Smoothly turn toward target direction (set by dragDir) var diff = self.targetDir - self.direction; // Normalize angle to [-PI, PI] while (diff > Math.PI) diff -= 2 * Math.PI; while (diff < -Math.PI) diff += 2 * Math.PI; self.direction += diff * 0.2; // smooth steering // Gradually accelerate to maxSpeed if (self.speed < self.maxSpeed) { self.speed += self.acceleration; if (self.speed > self.maxSpeed) self.speed = self.maxSpeed; } // Move in direction self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; } else { // If no driver, slow down to stop if (self.speed > 0) { self.speed -= self.deceleration; if (self.speed < 0) self.speed = 0; self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; } } }; return self; }); // CityBlock: road or building var CityBlock = Container.expand(function () { var self = Container.call(this); self.type = 'road'; // or 'building' self.setType = function (type) { self.type = type; if (self.asset) { self.removeChild(self.asset); } if (type === 'road') { self.asset = self.attachAsset('road', { anchorX: 0.5, anchorY: 0.5 }); } else { self.asset = self.attachAsset('building', { anchorX: 0.5, anchorY: 0.5 }); } }; return self; }); var Civilian = Container.expand(function () { var self = Container.call(this); // Pick a random color asset for this civilian var civAssetIdx = 1 + Math.floor(Math.random() * 6); var civAssetName = 'civilian' + civAssetIdx; self.asset = self.attachAsset(civAssetName, { anchorX: 0.5, anchorY: 0.5 }); self.asset.alpha = 0.7; // Add 'npc' label above the civilian self.npcLabel = new Text2('npc', { size: 38, fill: 0xFFFFFF }); self.npcLabel.anchor.set(0.5, 1); self.npcLabel.y = -40; self.addChild(self.npcLabel); self.speed = 2 + Math.random() * 2; self.direction = Math.random() * Math.PI * 2; self.update = function () { // Move in direction, bounce off buildings var nx = self.x + Math.cos(self.direction) * self.speed; var ny = self.y + Math.sin(self.direction) * self.speed; var onRoad = false; for (var i = 0; i < cityBlocks.length; i++) { var b = cityBlocks[i]; if (b.type === 'road') { var left = b.x - blockSize / 2; var right = b.x + blockSize / 2; var top = b.y - blockSize / 2; var bottom = b.y + blockSize / 2; if (nx >= left && nx <= right && ny >= top && ny <= bottom) { onRoad = true; break; } } } if (onRoad) { self.x = nx; self.y = ny; } else { self.direction = Math.random() * Math.PI * 2; } }; return self; }); // Spawn 6 civilians at random road blocks (not near player or police) // Mission marker var Mission = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('mission', { anchorX: 0.5, anchorY: 0.5 }); self.active = true; return self; }); // Player character var Player = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Add 'you' label above the player self.youLabel = new Text2('you', { size: 38, fill: 0xFFFFFF }); self.youLabel.anchor.set(0.5, 1); self.youLabel.y = -80; self.addChild(self.youLabel); self.speed = 16; self.inCar = null; // reference to car if inside self.direction = 0; // radians, 0 = right // Add gun to player's hand self.gun = self.attachAsset('gun', { anchorX: 0.1, anchorY: 0.5 }); self.gun.x = 60; // offset from player center self.gun.y = 0; self.gun.visible = true; self.lastShotDir = 0; // radians self.update = function () { // If in car, follow car and hide player sprite if (self.inCar) { self.x = self.inCar.x; self.y = self.inCar.y; self.visible = false; self.gun.visible = false; self.youLabel.visible = false; } else { self.visible = true; self.gun.visible = true; self.youLabel.visible = true; // Gun follows player hand and points at last shot direction self.gun.x = 60 * Math.cos(self.lastShotDir); self.gun.y = 60 * Math.sin(self.lastShotDir); self.gun.rotation = self.lastShotDir; } }; return self; }); // Police car var PoliceCar = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('police', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0; self.maxSpeed = 2.5; // much slower (reduced for slower police) self.acceleration = 0.08; // slower acceleration self.deceleration = 0.15; // slower deceleration self.direction = 0; self.target = null; // player or car self.isPolice = true; self.targetDir = 0; self.update = function () { if (self.target) { // Simple chase logic var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.targetDir = Math.atan2(dy, dx); // Smoothly turn toward target direction var diff = self.targetDir - self.direction; while (diff > Math.PI) diff -= 2 * Math.PI; while (diff < -Math.PI) diff += 2 * Math.PI; self.direction += diff * 0.2; // Accelerate to maxSpeed if (self.speed < self.maxSpeed) { self.speed += self.acceleration; if (self.speed > self.maxSpeed) self.speed = self.maxSpeed; } self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; } else { self.speed = 0; } } else { // If no target, slow down to stop if (self.speed > 0) { self.speed -= self.deceleration; if (self.speed < 0) self.speed = 0; self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; } } }; return self; }); // Police shooter class (policeman on foot) var PoliceShooter = Container.expand(function () { var self = Container.call(this); self.asset = self.attachAsset('police', { anchorX: 0.5, anchorY: 0.5 }); // Add 'POLIS' label above the police shooter self.polisLabel = new Text2('POLIS', { size: 38, fill: 0xFFFFFF }); self.polisLabel.anchor.set(0.5, 1); self.polisLabel.y = -80; self.addChild(self.polisLabel); // Add gun to police shooter's hand self.gun = self.attachAsset('gun', { anchorX: 0.1, anchorY: 0.5 }); self.gun.x = 60; self.gun.y = 0; self.gun.visible = true; self.addChild(self.gun); self.speed = 5; // reduced for slower police self.target = null; self.lastShotTick = 0; self.lastShotDir = 0; // direction of last shot self.update = function () { // If not aggro (no target), patrol randomly on roads if (!self.target) { if (typeof self.patrolTarget === "undefined" || !self.patrolTarget) { // Pick a random road block as patrol target var roadBlocks = []; for (var k = 0; k < cityBlocks.length; k++) { if (cityBlocks[k].type === 'road') roadBlocks.push(cityBlocks[k]); } if (roadBlocks.length > 0) { var idx = Math.floor(Math.random() * roadBlocks.length); self.patrolTarget = { x: roadBlocks[idx].x + (Math.random() - 0.5) * 100, y: roadBlocks[idx].y + (Math.random() - 0.5) * 100 }; } } if (self.patrolTarget) { var dx = self.patrolTarget.x - self.x; var dy = self.patrolTarget.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.direction = Math.atan2(dy, dx); self.x += Math.cos(self.direction) * self.speed * 0.5; self.y += Math.sin(self.direction) * self.speed * 0.5; } else { // Arrived at patrol target, pick a new one next frame self.patrolTarget = null; } // Gun points in patrol direction self.lastShotDir = self.direction; self.gun.x = 60 * Math.cos(self.lastShotDir); self.gun.y = 60 * Math.sin(self.lastShotDir); self.gun.rotation = self.lastShotDir; } } else { // Move toward player if target exists var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 120) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } // Gun points at player self.lastShotDir = Math.atan2(dy, dx); self.gun.x = 60 * Math.cos(self.lastShotDir); self.gun.y = 60 * Math.sin(self.lastShotDir); self.gun.rotation = self.lastShotDir; } // Firing handled in game.update }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ // No title, no description // Set backgroundColor to vibrant green for lush map backgroundColor: 0x1ecb2a }); /**** * Game Code ****/ // --- Market Setup --- var marketEntrance = new Container(); marketEntrance.asset = marketEntrance.attachAsset('building', { anchorX: 0.5, anchorY: 0.5 }); marketEntrance.asset.width = 300; marketEntrance.asset.height = 300; marketEntrance.x = (gridCols - 1) / 2 * blockSize + blockSize / 2 + 300; // rightmost road col + offset marketEntrance.y = 0 * blockSize + blockSize / 2; marketEntrance.isMarketEntrance = true; game.addChild(marketEntrance); // Market interior (hidden until entered) var marketInterior = new Container(); marketInterior.asset = marketInterior.attachAsset('building', { anchorX: 0.5, anchorY: 0.5 }); marketInterior.asset.width = 800; marketInterior.asset.height = 800; marketInterior.x = 2048 / 2; marketInterior.y = 2732 / 2; marketInterior.visible = false; game.addChild(marketInterior); // Market buy ammo button var marketBuyBtn = new Text2("₺50'ye 100 mermi al", { size: 120, fill: "#fff" }); marketBuyBtn.anchor.set(0.5, 0.5); marketBuyBtn.x = marketInterior.x; marketBuyBtn.y = marketInterior.y + 100; marketBuyBtn.visible = false; game.addChild(marketBuyBtn); // Market exit (door) var marketExit = new Container(); marketExit.asset = marketExit.attachAsset('road', { anchorX: 0.5, anchorY: 0.5 }); marketExit.asset.width = 200; marketExit.asset.height = 100; marketExit.x = marketInterior.x; marketExit.y = marketInterior.y + 350; marketExit.visible = false; marketExit.isMarketExit = true; game.addChild(marketExit); // Track if player is in market var playerInMarket = false; // marketStartPos will be initialized after player is created var marketStartPos; // --- Money on ground --- // Police car - white box with blue // Car - yellow box // Player (hero) - red ellipse // Building - blue box // City block (road) - gray box // --- Fixed City Map Setup --- var blockSize = 400; var cityBlocks = []; var cityBlockMap = {}; // key: "col,row" -> CityBlock // Fixed grid size var gridCols = 9; var gridRows = 13; // Use a simple procedural pattern: every even col/row is road, others are building function getBlockType(col, row) { // Make a simple city: roads every 2 blocks, rest are buildings if (col % 2 === 0 || row % 2 === 0) return 'road'; return 'building'; } // Helper to get or create a city block at (col, row) function getOrCreateCityBlock(col, row) { // Clamp to fixed grid if (col < -Math.floor(gridCols / 2) || col > Math.floor(gridCols / 2) || row < -Math.floor(gridRows / 2) || row > Math.floor(gridRows / 2)) { return null; } var key = col + ',' + row; if (cityBlockMap[key]) return cityBlockMap[key]; var block = new CityBlock(); var type = getBlockType(col, row); block.setType(type); block.x = col * blockSize + blockSize / 2; block.y = row * blockSize + blockSize / 2; cityBlockMap[key] = block; cityBlocks.push(block); game.addChild(block); return block; } // Generate fixed grid (centered at 0,0) var initialCols = gridCols, initialRows = gridRows; var centerCol = 0, centerRow = 0; for (var row = -Math.floor(initialRows / 2); row <= Math.floor(initialRows / 2); row++) { for (var col = -Math.floor(initialCols / 2); col <= Math.floor(initialCols / 2); col++) { getOrCreateCityBlock(col, row); } } // Helper to get all road blocks in a region (for spawning) function getRoadBlocksInRegion(colMin, colMax, rowMin, rowMax) { var blocks = []; for (var row = rowMin; row <= rowMax; row++) { for (var col = colMin; col <= colMax; col++) { var block = getOrCreateCityBlock(col, row); if (block && block.type === 'road') blocks.push(block); } } return blocks; } // Helper to get nearest road block col/row for a given x/y function getColRowForXY(x, y) { var col = Math.round((x - blockSize / 2) / blockSize); var row = Math.round((y - blockSize / 2) / blockSize); // Clamp to grid col = Math.max(-Math.floor(gridCols / 2), Math.min(Math.floor(gridCols / 2), col)); row = Math.max(-Math.floor(gridRows / 2), Math.min(Math.floor(gridRows / 2), row)); return { col: col, row: row }; } // --- Player Setup --- // Spawn player at center of the infinite world (col=0, row=0) var player = new Player(); player.x = 0 * blockSize + blockSize / 2; player.y = 0 * blockSize + blockSize / 2; game.addChild(player); // Now that player is defined, initialize marketStartPos marketStartPos = { x: player.x, y: player.y }; // --- Civilians Setup --- var civilians = []; // Spawn 6 civilians at random road blocks (not near player or police) for (var i = 0; i < 6; i++) { // Use a region around player for infinite world var playerColRow = getColRowForXY(player.x, player.y); var roadBlocks = getRoadBlocksInRegion(playerColRow.col - 7, playerColRow.col + 7, playerColRow.row - 7, playerColRow.row + 7); // Avoid spawn near player var filtered = []; for (var j = 0; j < roadBlocks.length; j++) { var b = roadBlocks[j]; var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2)); if (distToPlayer > 400) filtered.push(b); } if (filtered.length > 0) { var idx = Math.floor(Math.random() * filtered.length); var civ = new Civilian(); civ.x = filtered[idx].x + (Math.random() - 0.5) * 100; civ.y = filtered[idx].y + (Math.random() - 0.5) * 100; civilians.push(civ); game.addChild(civ); } } // --- Cars Setup --- var cars = []; function spawnCar(x, y) { var car = new Car(); car.x = x; car.y = y; car.direction = Math.random() * Math.PI * 2; cars.push(car); game.addChild(car); return car; } // Place a few cars on roads using fixed world var roadBlocksForCars = []; for (var i = 0; i < cityBlocks.length; i++) { if (cityBlocks[i].type === 'road') { // Remove player's starting block if (!(cityBlocks[i].x === player.x && cityBlocks[i].y === player.y)) { roadBlocksForCars.push(cityBlocks[i]); } } } // Shuffle for (var k = roadBlocksForCars.length - 1; k > 0; k--) { var j = Math.floor(Math.random() * (k + 1)); var temp = roadBlocksForCars[k]; roadBlocksForCars[k] = roadBlocksForCars[j]; roadBlocksForCars[j] = temp; } // Spawn up to 4 cars in distinct available road blocks var carsToSpawn = 4; for (var i = 0; i < Math.min(carsToSpawn, roadBlocksForCars.length); i++) { var block = roadBlocksForCars[i]; spawnCar(block.x, block.y); } // --- Police Setup --- var policeCars = []; function spawnPolice(x, y) { var police = new PoliceCar(); police.x = x; police.y = y; police.direction = 0; // Police do not chase the player anymore police.target = null; policeCars.push(police); game.addChild(police); return police; } // Start with three police cars at different road blocks var policeStartBlocks = []; for (var i = 0; i < cityBlocks.length; i++) { if (cityBlocks[i].type === 'road') { policeStartBlocks.push(cityBlocks[i]); } } if (policeStartBlocks.length > 0) { // Always spawn one at the top left of region var policeStartX = cityBlocks[0].x; var policeStartY = cityBlocks[0].y; spawnPolice(policeStartX, policeStartY); // Pick two more random road blocks (not the same as above) var used = {}; used[policeStartX + ',' + policeStartY] = true; for (var p = 0; p < 2; p++) { var idx = Math.floor(Math.random() * policeStartBlocks.length); var b = policeStartBlocks[idx]; var key = b.x + ',' + b.y; if (!used[key]) { spawnPolice(b.x, b.y); used[key] = true; } } } // --- Mission Setup --- var mission = new Mission(); // Place mission at far corner of grid mission.x = cityBlocks[cityBlocks.length - 1].x; mission.y = cityBlocks[cityBlocks.length - 1].y; game.addChild(mission); // --- Money on ground --- var moneyPickups = []; function spawnMoney(x, y) { var m = new Container(); m.asset = m.attachAsset('mission', { anchorX: 0.5, anchorY: 0.5 }); m.x = x; m.y = y; m.value = 50; moneyPickups.push(m); game.addChild(m); return m; } // Place 15 money pickups at random road blocks in grid var roadBlocksForMoney = []; for (var i = 0; i < cityBlocks.length; i++) { if (cityBlocks[i].type === 'road') { roadBlocksForMoney.push(cityBlocks[i]); } } for (var i = 0; i < 15; i++) { if (roadBlocksForMoney.length > 0) { var idx = Math.floor(Math.random() * roadBlocksForMoney.length); var b = roadBlocksForMoney[idx]; var mx = b.x + (Math.random() - 0.5) * (blockSize - 100); var my = b.y + (Math.random() - 0.5) * (blockSize - 100); spawnMoney(mx, my); } } // --- Score and Wanted Level --- var score = 0; var wantedLevel = 1; // 1-5 var scoreTxt = new Text2('Score: 0', { size: 90, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var wantedTxt = new Text2('★', { size: 80, fill: "#ff0" }); wantedTxt.anchor.set(1, 0); // anchor to top right wantedTxt.x = 0; wantedTxt.y = 0; LK.gui.topRight.addChild(wantedTxt); // (Removed 'elinde akm var' text below the wanted stars) // --- Timer --- var timeLeft = 60; // seconds var timerTxt = new Text2('60', { size: 80, fill: "#fff" }); timerTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timerTxt); // --- Touch Controls --- var dragDir = null; // {dx,dy} var dragStart = null; var dragNode = null; var lastTouchX = 0, lastTouchY = 0; // --- Shooting & Health --- var bullets = []; var policeBullets = []; var playerHealth = 5; // --- Ammo system --- var playerAmmo = 100; var ammoTxt = new Text2('Ammo: 100', { size: 80, fill: "#fff" }); // Anchor to top right (same as wanted stars) ammoTxt.anchor.set(1, 0); // Place just below the wanted stars (wantedTxt is at y=0, height ~80) ammoTxt.x = 0; ammoTxt.y = wantedTxt.y + wantedTxt.height + 10; // 10px below stars LK.gui.topRight.addChild(ammoTxt); function updateAmmoDisplay() { ammoTxt.setText('Ammo: ' + playerAmmo); } // Money system var playerMoney = 0; var moneyTxt = new Text2('₺ 0', { size: 120, fill: 0x44FF44 }); moneyTxt.anchor.set(0, 0); moneyTxt.x = 100; moneyTxt.y = 90; LK.gui.addChild(moneyTxt); var healthTxt = new Text2('♥♥♥♥♥', { size: 80, fill: 0xFF4444 }); healthTxt.anchor.set(0, 0); // anchor top left // Place health bar at (100,0) to avoid platform menu healthTxt.x = 100; healthTxt.y = 0; LK.gui.addChild(healthTxt); // Track if police are aggressive (chasing) or passive var policeAggro = false; // Helper to update health display function updateHealthDisplay() { var hearts = ''; for (var i = 0; i < playerHealth; i++) hearts += '♥'; healthTxt.setText(hearts); } function updateMoneyDisplay() { moneyTxt.setText('₺ ' + playerMoney); } var policeShooters = []; // Spawn a few police shooters at random road blocks, not near player (fixed world) for (var i = 0; i < 8; i++) { var roadBlocks = []; for (var j = 0; j < cityBlocks.length; j++) { if (cityBlocks[j].type === 'road') { var b = cityBlocks[j]; var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2)); if (distToPlayer > 600) roadBlocks.push(b); } } if (roadBlocks.length > 0) { var idx = Math.floor(Math.random() * roadBlocks.length); var shooter = new PoliceShooter(); shooter.x = roadBlocks[idx].x + (Math.random() - 0.5) * 100; shooter.y = roadBlocks[idx].y + (Math.random() - 0.5) * 100; shooter.target = null; // Do not target player at start shooter.wasShotAt = false; // Only shoot if player shoots at them policeShooters.push(shooter); game.addChild(shooter); } } // --- Helper Functions --- function isOnRoad(x, y) { // Returns true if (x,y) is on a road block (fixed world) var colRow = getColRowForXY(x, y); var block = getOrCreateCityBlock(colRow.col, colRow.row); if (block && block.type === 'road') { var left = block.x - blockSize / 2; var right = block.x + blockSize / 2; var top = block.y - blockSize / 2; var bottom = block.y + blockSize / 2; if (x >= left && x <= right && y >= top && y <= bottom) { return true; } } return false; } function clampToRoad(x, y) { // Snap to nearest road block center (fixed world) var colRow = getColRowForXY(x, y); var block = getOrCreateCityBlock(colRow.col, colRow.row); if (block && block.type === 'road') { return { x: block.x, y: block.y }; } // If not road, search nearby for (var r = -1; r <= 1; r++) { for (var c = -1; c <= 1; c++) { var b = getOrCreateCityBlock(colRow.col + c, colRow.row + r); if (b && b.type === 'road') return { x: b.x, y: b.y }; } } // Fallback: return original return { x: x, y: y }; } function updateWantedDisplay() { var stars = ''; for (var i = 0; i < wantedLevel; i++) stars += '★'; wantedTxt.setText(stars); // Default color var color = "#ff0"; // If 5 stars, flash red/blue if (wantedLevel === 5) { // Show "wandet" text for 3 seconds only on the frame wantedLevel becomes 5 if (typeof wandetTextShown === "undefined") wandetTextShown = false; if (!wandetTextShown) { // Remove previous if exists if (typeof wandetText !== "undefined" && wandetText.parent) { wandetText.parent.removeChild(wandetText); wandetText = undefined; } wandetText = new Text2("wandet", { size: 160, fill: 0xFF2222 }); wandetText.anchor.set(0.5, 0); wandetText.x = 2048 / 2; wandetText.y = 500; LK.gui.addChild(wandetText); wandetTextShown = true; LK.setTimeout(function () { if (wandetText && wandetText.parent) { wandetText.parent.removeChild(wandetText); wandetText = undefined; } wandetTextShown = false; }, 3000); } // Alternate color every 20 ticks (about 1/3 second at 60fps) if (LK.ticks % 40 < 20) { color = "#ff2222"; // red } else { color = "#2288ff"; // blue } } if (wantedTxt && wantedTxt.style && typeof wantedTxt.style.fill !== "undefined") { wantedTxt.style.fill = color; } } function addScore(amt) { score += amt; scoreTxt.setText('Score: ' + score); LK.setScore(score); } function resetMission() { // Place mission at random road block (fixed world) var roadBlocks = []; for (var i = 0; i < cityBlocks.length; i++) { if (cityBlocks[i].type === 'road') { var b = cityBlocks[i]; var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2)); if (distToPlayer > 400) roadBlocks.push(b); } } if (roadBlocks.length > 0) { var idx = Math.floor(Math.random() * roadBlocks.length); mission.x = roadBlocks[idx].x; mission.y = roadBlocks[idx].y; mission.active = true; mission.visible = true; } } // --- Game Touch Controls --- // Drag to move (swipe in direction to move) game.down = function (x, y, obj) { // Don't allow drag from top left 100x100 if (x < 100 && y < 100) return; // --- SHOOTING LOGIC --- // If not dragging, treat as tap to shoot if (!dragNode && !dragDir && !player.inCar) { if (typeof playerAmmo === "undefined") playerAmmo = 0; if (playerAmmo > 0) { // Player shoots toward (x, y) var b = new Bullet(); b.x = player.x; b.y = player.y; b.dir = Math.atan2(y - b.y, x - b.x); b.owner = 'player'; bullets.push(b); game.addChild(b); // Update player's gun direction to point at shot player.lastShotDir = b.dir; playerAmmo--; if (typeof updateAmmoDisplay === "function") updateAmmoDisplay(); } else { // Out of ammo: show "mermi bitti" for 1 second at center-top if (typeof ammoEmptyText !== "undefined" && ammoEmptyText.parent) { ammoEmptyText.parent.removeChild(ammoEmptyText); ammoEmptyText = undefined; } ammoEmptyText = new Text2("mermi bitti", { size: 120, fill: 0xFF2222 }); ammoEmptyText.anchor.set(0.5, 0); ammoEmptyText.x = 2048 / 2; ammoEmptyText.y = 320; LK.gui.addChild(ammoEmptyText); LK.setTimeout(function () { if (ammoEmptyText && ammoEmptyText.parent) { ammoEmptyText.parent.removeChild(ammoEmptyText); ammoEmptyText = undefined; } }, 1000); } // No police aggro or wanted level increase when shooting police } // --- DRAG LOGIC (movement) --- dragStart = { x: x, y: y }; lastTouchX = x; lastTouchY = y; dragDir = null; dragNode = player; }; game.move = function (x, y, obj) { if (!dragNode) return; var dx = x - dragStart.x; var dy = y - dragStart.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 30) { dragDir = { dx: dx, dy: dy }; } lastTouchX = x; lastTouchY = y; }; game.up = function (x, y, obj) { dragNode = null; dragDir = null; dragStart = null; }; // --- Car Enter/Exit Logic --- // Track tap times for triple-tap exit var carTapTimes = []; game.on('down', function (x, y, obj) { // If player is not in car, check if tap is on car and player is close enough if (!player.inCar) { for (var i = 0; i < cars.length; i++) { var car = cars[i]; var dx = car.x - x; var dy = car.y - y; // Tap must be on car if (Math.abs(dx) < 80 && Math.abs(dy) < 50) { // Player must be close to car to enter if (Math.abs(player.x - car.x) < 120 && Math.abs(player.y - car.y) < 80) { player.inCar = car; car.driver = player; // Do not set car.speed here; Car.update will handle acceleration car.direction = 0; // Increase wanted level wantedLevel = Math.min(5, wantedLevel + 1); updateWantedDisplay(); // Police target car for (var j = 0; j < policeCars.length; j++) { policeCars[j].target = car; } // Reset tap times for exit carTapTimes = []; break; } } } } else { // Player is in car: require triple-tap within 1 second to exit var now = Date.now(); carTapTimes.push(now); // Only keep last 3 taps if (carTapTimes.length > 3) carTapTimes.shift(); if (carTapTimes.length === 3) { // Check if 3 taps are within 1 second if (carTapTimes[2] - carTapTimes[0] < 1000) { var car = player.inCar; player.inCar = null; car.driver = null; // Do not set car.speed to 0; Car.update will handle deceleration // Place player next to car player.x = car.x + 80; player.y = car.y; // Police target player for (var j = 0; j < policeCars.length; j++) { policeCars[j].target = player; } carTapTimes = []; } } } }); // --- Game Update Loop --- game.update = function () { // Timer if (LK.ticks % 60 === 0 && timeLeft > 0) { timeLeft--; timerTxt.setText(timeLeft + ''); // Do not end the game when timer reaches zero } // --- Market logic --- // If not in market, check for market entrance if (!playerInMarket) { // Check if player is at market entrance (right edge) if (Math.abs(player.x - marketEntrance.x) < 120 && Math.abs(player.y - marketEntrance.y) < 120) { // Teleport to market interior playerInMarket = true; marketStartPos.x = player.x; marketStartPos.y = player.y; player.x = marketInterior.x; player.y = marketInterior.y - 100; marketInterior.visible = true; marketBuyBtn.visible = true; marketExit.visible = true; // Hide city elements for (var i = 0; i < cityBlocks.length; i++) cityBlocks[i].visible = false; for (var i = 0; i < cars.length; i++) cars[i].visible = false; for (var i = 0; i < civilians.length; i++) civilians[i].visible = false; for (var i = 0; i < policeCars.length; i++) policeCars[i].visible = false; for (var i = 0; i < policeShooters.length; i++) policeShooters[i].visible = false; for (var i = 0; i < moneyPickups.length; i++) moneyPickups[i].visible = false; if (typeof gunPickups !== "undefined") for (var i = 0; i < gunPickups.length; i++) gunPickups[i].visible = false; mission.visible = false; marketEntrance.visible = false; } } else { // In market: only allow movement inside market area if (dragDir) { var angle = Math.atan2(dragDir.dy, dragDir.dx); var nx = player.x + Math.cos(angle) * player.speed; var ny = player.y + Math.sin(angle) * player.speed; // Clamp to market interior bounds var left = marketInterior.x - 350, right = marketInterior.x + 350; var top = marketInterior.y - 350, bottom = marketInterior.y + 350; if (nx > left && nx < right && ny > top && ny < bottom) { player.x = nx; player.y = ny; } } // Check for ammo buy (touch on buy button) if (Math.abs(player.x - marketBuyBtn.x) < 200 && Math.abs(player.y - marketBuyBtn.y) < 100) { if (playerMoney >= 50 && playerAmmo < 100) { playerMoney -= 50; playerAmmo = 100; updateMoneyDisplay(); updateAmmoDisplay(); // Show "100 mermi alındı" text if (typeof marketBuyText !== "undefined" && marketBuyText.parent) { marketBuyText.parent.removeChild(marketBuyText); marketBuyText = undefined; } marketBuyText = new Text2("100 mermi alındı", { size: 120, fill: 0x44FF44 }); marketBuyText.anchor.set(0.5, 0); marketBuyText.x = 2048 / 2; marketBuyText.y = 320; LK.gui.addChild(marketBuyText); LK.setTimeout(function () { if (marketBuyText && marketBuyText.parent) { marketBuyText.parent.removeChild(marketBuyText); marketBuyText = undefined; } }, 1000); } } // Check for exit (touch on exit door) if (Math.abs(player.x - marketExit.x) < 120 && Math.abs(player.y - marketExit.y) < 80) { // Teleport back to start player.x = marketStartPos.x; player.y = marketStartPos.y; playerInMarket = false; marketInterior.visible = false; marketBuyBtn.visible = false; marketExit.visible = false; // Show city elements again for (var i = 0; i < cityBlocks.length; i++) cityBlocks[i].visible = true; for (var i = 0; i < cars.length; i++) cars[i].visible = true; for (var i = 0; i < civilians.length; i++) civilians[i].visible = true; for (var i = 0; i < policeCars.length; i++) policeCars[i].visible = true; for (var i = 0; i < policeShooters.length; i++) policeShooters[i].visible = true; for (var i = 0; i < moneyPickups.length; i++) moneyPickups[i].visible = true; if (typeof gunPickups !== "undefined") for (var i = 0; i < gunPickups.length; i++) gunPickups[i].visible = true; mission.visible = true; marketEntrance.visible = true; } } // Player movement if (!player.inCar && dragDir && !playerInMarket) { // Move player in drag direction, but only on road var angle = Math.atan2(dragDir.dy, dragDir.dx); var nx = player.x + Math.cos(angle) * player.speed; var ny = player.y + Math.sin(angle) * player.speed; if (isOnRoad(nx, ny)) { player.x = nx; player.y = ny; } // No infinite world: no need to generate new blocks in update } // Car movement (if player in car) for (var i = 0; i < cars.length; i++) { var car = cars[i]; // If player is driving this car, set car's targetDir to drag direction if (car.driver === player && dragDir) { var angle = Math.atan2(dragDir.dy, dragDir.dx); car.targetDir = angle; } car.update(); // Clamp car to road if (car.driver) { if (!isOnRoad(car.x, car.y)) { // Stop car at edge car.speed = 0; } } } // Police movement for (var i = 0; i < policeCars.length; i++) { var police = policeCars[i]; // Police patrol logic: move randomly on roads if not chasing if (!policeAggro) { if (typeof police.patrolTarget === "undefined" || !police.patrolTarget) { // Pick a random road block as patrol target var roadBlocks = []; for (var k = 0; k < cityBlocks.length; k++) { if (cityBlocks[k].type === 'road') roadBlocks.push(cityBlocks[k]); } if (roadBlocks.length > 0) { var idx = Math.floor(Math.random() * roadBlocks.length); police.patrolTarget = { x: roadBlocks[idx].x + (Math.random() - 0.5) * 100, y: roadBlocks[idx].y + (Math.random() - 0.5) * 100 }; } } if (police.patrolTarget) { var dx = police.patrolTarget.x - police.x; var dy = police.patrolTarget.y - police.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { police.direction = Math.atan2(dy, dx); police.speed = police.maxSpeed * 0.5; police.x += Math.cos(police.direction) * police.speed; police.y += Math.sin(police.direction) * police.speed; } else { // Arrived at patrol target, pick a new one next frame police.patrolTarget = null; police.speed = 0; } } } else { police.update(); } // Clamp police to road if (!isOnRoad(police.x, police.y)) { police.speed = 0; } // Police catch player var target = police.target; if (policeAggro && target && Math.abs(police.x - target.x) < 80 && Math.abs(police.y - target.y) < 80) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); return; } } // Police shooters update for (var i = policeShooters.length - 1; i >= 0; i--) { var shooter = policeShooters[i]; shooter.update(); // Remove if off map if (shooter.x < 0 || shooter.x > 2048 || shooter.y < 0 || shooter.y > 2732) { shooter.destroy(); policeShooters.splice(i, 1); continue; } // --- Police shooter fires at player only if aggro (wasShotAt or policeAggro) --- // Only fire if player is within 700px, not in car, and shooter is aggro if (shooter.wasShotAt || policeAggro) { var px = player.inCar ? player.inCar.x : player.x; var py = player.inCar ? player.inCar.y : player.y; var dx = px - shooter.x; var dy = py - shooter.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 700 && LK.ticks - shooter.lastShotTick > 60) { // Fire bullet toward player var b = new Bullet(); b.x = shooter.x; b.y = shooter.y; b.dir = Math.atan2(dy, dx); b.owner = 'police'; policeBullets.push(b); game.addChild(b); shooter.lastShotTick = LK.ticks; } } } // Bullets update (player) for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; if (b && typeof b.update === "function") { b.update(); } // Remove if off screen if (b && typeof b.x === "number" && typeof b.y === "number" && (b.x < 0 || b.x > 2048 || b.y < 0 || b.y > 2732)) { b.destroy(); bullets.splice(i, 1); continue; } // --- Police car kill check --- for (var j = policeCars.length - 1; j >= 0; j--) { var police = policeCars[j]; // --- Car bullet hit logic --- if (police && b && typeof police.x === "number" && typeof police.y === "number" && typeof b.x === "number" && typeof b.y === "number" && Math.abs(police.x - b.x) < 60 && Math.abs(police.y - b.y) < 60) { // Track bullet hits on this car if (typeof police.bulletHitCount === "undefined") police.bulletHitCount = 0; police.bulletHitCount++; // Remove bullet b.destroy(); bullets.splice(i, 1); // If hit 30 times, destroy car and show "arba patladı" if (police.bulletHitCount >= 30) { // Drop a gun at police car's position var gunDrop = new Container(); gunDrop.asset = gunDrop.attachAsset('gun', { anchorX: 0.5, anchorY: 0.5 }); // Add 'gun' label above the dropped gun gunDrop.gunLabel = new Text2('gun', { size: 38, fill: 0xFFFFFF }); gunDrop.gunLabel.anchor.set(0.5, 1); gunDrop.gunLabel.y = -30; gunDrop.addChild(gunDrop.gunLabel); gunDrop.x = police.x; gunDrop.y = police.y; gunDrop.isGunPickup = true; game.addChild(gunDrop); if (typeof gunPickups === "undefined") gunPickups = []; gunPickups.push(gunDrop); police.destroy(); policeCars.splice(j, 1); // Show "arba patladı" text for 2 seconds at center of screen if (typeof carExplodedText !== "undefined" && carExplodedText.parent) { carExplodedText.parent.removeChild(carExplodedText); carExplodedText = undefined; } carExplodedText = new Text2("arba patladı", { size: 160, fill: 0xFF2222 }); carExplodedText.anchor.set(0.5, 0.5); carExplodedText.x = 2048 / 2; carExplodedText.y = 2732 / 2; LK.gui.addChild(carExplodedText); LK.setTimeout(function () { if (carExplodedText && carExplodedText.parent) { carExplodedText.parent.removeChild(carExplodedText); carExplodedText = undefined; } }, 2000); addScore(20); // Increase wanted level by 1 (max 5) when police car is killed wantedLevel = Math.min(5, wantedLevel + 1); updateWantedDisplay(); // Only aggro police if wantedLevel is 5 if (wantedLevel === 5 && typeof spawned5StarPolice === "undefined") { for (var p = 0; p < policeShooters.length; p++) { policeShooters[p].wasShotAt = true; policeShooters[p].target = player; } for (var p = 0; p < policeCars.length; p++) { policeCars[p].target = player; } policeAggro = true; // Spawn 20 police cars at random road blocks (not near player) var roadBlocks = []; for (var i = 0; i < cityBlocks.length; i++) { if (cityBlocks[i].type === 'road') { var distToPlayer = Math.sqrt(Math.pow(cityBlocks[i].x - player.x, 2) + Math.pow(cityBlocks[i].y - player.y, 2)); if (distToPlayer > 400) { roadBlocks.push(cityBlocks[i]); } } } for (var s = 0; s < 20; s++) { if (roadBlocks.length > 0) { var idx = Math.floor(Math.random() * roadBlocks.length); var b = roadBlocks[idx]; var newPolice = spawnPolice(b.x, b.y); newPolice.target = player; // Remove this block from roadBlocks to avoid double-spawn at same spot roadBlocks.splice(idx, 1); } } // --- Spawn 40 police shooters at random road blocks, not near player --- for (var spawnCount = 0; spawnCount < 40; spawnCount++) { var shooterBlocks = []; for (var k = 0; k < cityBlocks.length; k++) { var b2 = cityBlocks[k]; if (b2.type === 'road') { var distToPlayer = Math.sqrt(Math.pow(b2.x - player.x, 2) + Math.pow(b2.y - player.y, 2)); if (distToPlayer > 600) { shooterBlocks.push(b2); } } } if (shooterBlocks.length > 0) { var idx2 = Math.floor(Math.random() * shooterBlocks.length); var newShooter = new PoliceShooter(); newShooter.x = shooterBlocks[idx2].x + (Math.random() - 0.5) * 100; newShooter.y = shooterBlocks[idx2].y + (Math.random() - 0.5) * 100; newShooter.target = player; newShooter.wasShotAt = true; policeShooters.push(newShooter); game.addChild(newShooter); shooterBlocks.splice(idx2, 1); } } spawned5StarPolice = true; } break; } // If not destroyed, do not break, allow other cars to be checked continue; } // --- END Car bullet hit logic --- } // --- Police shooter kill check --- for (var j = policeShooters.length - 1; j >= 0; j--) { var shooter = policeShooters[j]; if (shooter && b && typeof shooter.x === "number" && typeof shooter.y === "number" && typeof b.x === "number" && typeof b.y === "number" && Math.abs(shooter.x - b.x) < 60 && Math.abs(shooter.y - b.y) < 60) { // Mark this shooter as shot at, so it will shoot back shooter.wasShotAt = true; // --- Police shooter health logic --- // Instantly kill police shooter with one bullet b.destroy(); bullets.splice(i, 1); // Police shooter killed! // Drop a gun at police shooter's position var gunDrop = new Container(); gunDrop.asset = gunDrop.attachAsset('gun', { anchorX: 0.5, anchorY: 0.5 }); // Add 'gun' label above the dropped gun gunDrop.gunLabel = new Text2('gun', { size: 38, fill: 0xFFFFFF }); gunDrop.gunLabel.anchor.set(0.5, 1); gunDrop.gunLabel.y = -30; gunDrop.addChild(gunDrop.gunLabel); gunDrop.x = shooter.x; gunDrop.y = shooter.y; gunDrop.isGunPickup = true; game.addChild(gunDrop); if (typeof gunPickups === "undefined") gunPickups = []; gunPickups.push(gunDrop); shooter.destroy(); policeShooters.splice(j, 1); // --- Spawn TWO new police shooters at random road blocks, not near player --- for (var spawnCount = 0; spawnCount < 2; spawnCount++) { var roadBlocks = []; for (var k = 0; k < cityBlocks.length; k++) { var b2 = cityBlocks[k]; if (b2.type === 'road') { var distToPlayer = Math.sqrt(Math.pow(b2.x - player.x, 2) + Math.pow(b2.y - player.y, 2)); if (distToPlayer > 600) { roadBlocks.push(b2); } } } if (roadBlocks.length > 0) { var idx2 = Math.floor(Math.random() * roadBlocks.length); var newShooter = new PoliceShooter(); newShooter.x = roadBlocks[idx2].x + (Math.random() - 0.5) * 100; newShooter.y = roadBlocks[idx2].y + (Math.random() - 0.5) * 100; if (policeAggro) { newShooter.target = player; newShooter.wasShotAt = true; } else { newShooter.target = null; newShooter.wasShotAt = false; } policeShooters.push(newShooter); game.addChild(newShooter); } } addScore(15); // Increase wanted level by 1 (max 5) when police shooter is killed wantedLevel = Math.min(5, wantedLevel + 1); updateWantedDisplay(); break; // If not dead, do not break, allow other shooters to be checked } } } // Bullets update (police) for (var i = policeBullets.length - 1; i >= 0; i--) { var b = policeBullets[i]; if (b && typeof b.update === "function") { b.update(); } // Remove if off screen if (b && typeof b.x === "number" && typeof b.y === "number" && (b.x < 0 || b.x > 2048 || b.y < 0 || b.y > 2732)) { b.destroy(); policeBullets.splice(i, 1); continue; } // Check hit player var px = player.inCar ? player.inCar.x : player.x; var py = player.inCar ? player.inCar.y : player.y; if (Math.abs(px - b.x) < 60 && Math.abs(py - b.y) < 60) { // Police bullet hits player: decrease health if not in car if (!player.inCar && playerHealth > 0) { playerHealth--; updateHealthDisplay(); // Optional: flash screen on hit LK.effects.flashScreen(0xff0000, 200); // Game over if health reaches 0 if (playerHealth <= 0) { LK.showGameOver(); return; } } // If player is in car, do not take damage b.destroy(); policeBullets.splice(i, 1); } } // Player update player.update(); // Civilians update and check for being shot by player or run over by car for (var i = civilians.length - 1; i >= 0; i--) { var civ = civilians[i]; if (civ && typeof civ.update === "function") { civ.update(); } var killed = false; // Check if hit by any player bullet for (var j = bullets.length - 1; j >= 0; j--) { var b = bullets[j]; if (Math.abs(civ.x - b.x) < 60 && Math.abs(civ.y - b.y) < 60) { killed = true; // Remove bullet b.destroy(); bullets.splice(j, 1); break; } } // Check if run over by any car driven by player if (!killed) { for (var c = 0; c < cars.length; c++) { var car = cars[c]; if (car.driver === player) { // Use a slightly larger hitbox for car if (Math.abs(civ.x - car.x) < 90 && Math.abs(civ.y - car.y) < 70) { killed = true; break; } } } } if (killed) { // Civilian killed // Drop money at civilian's position spawnMoney(civ.x, civ.y); // Respawn civilian at random road block (fixed world) var roadBlocks = []; for (var k = 0; k < cityBlocks.length; k++) { if (cityBlocks[k].type === 'road') { var b = cityBlocks[k]; var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2)); if (distToPlayer > 400) roadBlocks.push(b); } } if (roadBlocks.length > 0) { var idx = Math.floor(Math.random() * roadBlocks.length); var newCiv = new Civilian(); newCiv.x = roadBlocks[idx].x + (Math.random() - 0.5) * 100; newCiv.y = roadBlocks[idx].y + (Math.random() - 0.5) * 100; civilians.push(newCiv); game.addChild(newCiv); } civ.destroy(); civilians.splice(i, 1); addScore(10); // Increase wanted level by 1 every time player kills an NPC, max 5 wantedLevel = Math.min(5, wantedLevel + 1); updateWantedDisplay(); // Only aggro police if wantedLevel is 5 if (wantedLevel === 5) { for (var p = 0; p < policeShooters.length; p++) { policeShooters[p].wasShotAt = true; policeShooters[p].target = player; } for (var p = 0; p < policeCars.length; p++) { policeCars[p].target = player; } policeAggro = true; } // No break here: allow this to happen every time an NPC is killed continue; } // --- Police run over by car logic --- // Player can run over police shooters and police cars when driving a car for (var c = 0; c < cars.length; c++) { var car = cars[c]; if (car.driver === player) { // Run over police shooters for (var s = policeShooters.length - 1; s >= 0; s--) { var shooter = policeShooters[s]; if (Math.abs(shooter.x - car.x) < 90 && Math.abs(shooter.y - car.y) < 70) { // Police shooter killed by car // Drop a gun at police shooter's position var gunDrop = new Container(); gunDrop.asset = gunDrop.attachAsset('gun', { anchorX: 0.5, anchorY: 0.5 }); gunDrop.gunLabel = new Text2('gun', { size: 38, fill: 0xFFFFFF }); gunDrop.gunLabel.anchor.set(0.5, 1); gunDrop.gunLabel.y = -30; gunDrop.addChild(gunDrop.gunLabel); gunDrop.x = shooter.x; gunDrop.y = shooter.y; gunDrop.isGunPickup = true; game.addChild(gunDrop); if (typeof gunPickups === "undefined") gunPickups = []; gunPickups.push(gunDrop); shooter.destroy(); policeShooters.splice(s, 1); // Spawn TWO new police shooters at random road blocks, not near player for (var spawnCount = 0; spawnCount < 2; spawnCount++) { var roadBlocks = []; for (var k = 0; k < cityBlocks.length; k++) { var b2 = cityBlocks[k]; if (b2.type === 'road') { var distToPlayer = Math.sqrt(Math.pow(b2.x - player.x, 2) + Math.pow(b2.y - player.y, 2)); if (distToPlayer > 600) { roadBlocks.push(b2); } } } if (roadBlocks.length > 0) { var idx2 = Math.floor(Math.random() * roadBlocks.length); var newShooter = new PoliceShooter(); newShooter.x = roadBlocks[idx2].x + (Math.random() - 0.5) * 100; newShooter.y = roadBlocks[idx2].y + (Math.random() - 0.5) * 100; if (policeAggro) { newShooter.target = player; newShooter.wasShotAt = true; } else { newShooter.target = null; newShooter.wasShotAt = false; } policeShooters.push(newShooter); game.addChild(newShooter); } } addScore(15); // Increase wanted level by 1 (max 5) when police shooter is killed wantedLevel = Math.min(5, wantedLevel + 1); updateWantedDisplay(); // Only aggro police if wantedLevel is 5 if (wantedLevel === 5) { for (var p = 0; p < policeShooters.length; p++) { policeShooters[p].wasShotAt = true; policeShooters[p].target = player; } for (var p = 0; p < policeCars.length; p++) { policeCars[p].target = player; } policeAggro = true; } // Only allow one run over per car per frame break; } } // Run over police cars for (var pc = policeCars.length - 1; pc >= 0; pc--) { var police = policeCars[pc]; if (Math.abs(police.x - car.x) < 90 && Math.abs(police.y - car.y) < 70) { // Police car killed by car // Drop a gun at police car's position var gunDrop = new Container(); gunDrop.asset = gunDrop.attachAsset('gun', { anchorX: 0.5, anchorY: 0.5 }); gunDrop.gunLabel = new Text2('gun', { size: 38, fill: 0xFFFFFF }); gunDrop.gunLabel.anchor.set(0.5, 1); gunDrop.gunLabel.y = -30; gunDrop.addChild(gunDrop.gunLabel); gunDrop.x = police.x; gunDrop.y = police.y; gunDrop.isGunPickup = true; game.addChild(gunDrop); if (typeof gunPickups === "undefined") gunPickups = []; gunPickups.push(gunDrop); police.destroy(); policeCars.splice(pc, 1); addScore(20); // Increase wanted level by 1 (max 5) when police car is killed wantedLevel = Math.min(5, wantedLevel + 1); updateWantedDisplay(); // Only aggro police if wantedLevel is 5 if (wantedLevel === 5 && typeof spawned5StarPolice === "undefined") { for (var p = 0; p < policeShooters.length; p++) { policeShooters[p].wasShotAt = true; policeShooters[p].target = player; } for (var p = 0; p < policeCars.length; p++) { policeCars[p].target = player; } policeAggro = true; // Spawn 5 police cars at random road blocks (not near player) var roadBlocks = []; for (var i = 0; i < cityBlocks.length; i++) { if (cityBlocks[i].type === 'road') { var distToPlayer = Math.sqrt(Math.pow(cityBlocks[i].x - player.x, 2) + Math.pow(cityBlocks[i].y - player.y, 2)); if (distToPlayer > 400) { roadBlocks.push(cityBlocks[i]); } } } for (var s = 0; s < 5; s++) { if (roadBlocks.length > 0) { var idx = Math.floor(Math.random() * roadBlocks.length); var b = roadBlocks[idx]; var newPolice = spawnPolice(b.x, b.y); newPolice.target = player; // Remove this block from roadBlocks to avoid double-spawn at same spot roadBlocks.splice(idx, 1); } } spawned5StarPolice = true; } // Only allow one run over per car per frame break; } } } } } // Money pickup check var px = player.inCar ? player.inCar.x : player.x; var py = player.inCar ? player.inCar.y : player.y; for (var i = moneyPickups.length - 1; i >= 0; i--) { var m = moneyPickups[i]; if (Math.abs(m.x - px) < 60 && Math.abs(m.y - py) < 60) { playerMoney += m.value; updateMoneyDisplay(); m.destroy(); moneyPickups.splice(i, 1); // Show "+50 money" text for 1 second at center-top of screen if (typeof moneyPickupText !== "undefined" && moneyPickupText.parent) { moneyPickupText.parent.removeChild(moneyPickupText); moneyPickupText = undefined; } moneyPickupText = new Text2("+50 money", { size: 120, fill: 0x44FF44 }); moneyPickupText.anchor.set(0.5, 0); moneyPickupText.x = 2048 / 2; moneyPickupText.y = 320; LK.gui.addChild(moneyPickupText); LK.setTimeout(function () { if (moneyPickupText && moneyPickupText.parent) { moneyPickupText.parent.removeChild(moneyPickupText); moneyPickupText = undefined; } }, 1000); } } // Gun pickup check if (typeof gunPickups === "undefined") gunPickups = []; for (var i = gunPickups.length - 1; i >= 0; i--) { var g = gunPickups[i]; if (Math.abs(g.x - px) < 60 && Math.abs(g.y - py) < 60) { // Player picks up gun: restore ammo to 100 playerAmmo = 100; if (typeof updateAmmoDisplay === "function") updateAmmoDisplay(); g.destroy(); gunPickups.splice(i, 1); // Show "+1 gun" text for 1 second at center-top of screen if (typeof gunPickupText !== "undefined" && gunPickupText.parent) { gunPickupText.parent.removeChild(gunPickupText); gunPickupText = undefined; } gunPickupText = new Text2("+1 gun", { size: 120, fill: 0xFFF700 }); gunPickupText.anchor.set(0.5, 0); // Place at top center, but not in top left 100x100 gunPickupText.x = 2048 / 2; gunPickupText.y = 180; LK.gui.addChild(gunPickupText); // Remove after 1 second LK.setTimeout(function () { if (gunPickupText && gunPickupText.parent) { gunPickupText.parent.removeChild(gunPickupText); gunPickupText = undefined; } }, 1000); // Optionally: add logic for ammo, weapon upgrade, etc. } } // Mission check if (mission.active) { var px = player.inCar ? player.inCar.x : player.x; var py = player.inCar ? player.inCar.y : player.y; if (Math.abs(mission.x - px) < 80 && Math.abs(mission.y - py) < 80) { // Complete mission addScore(10 * wantedLevel); mission.active = false; mission.visible = false; // Increase wanted level, spawn more police wantedLevel = Math.min(5, wantedLevel + 1); updateWantedDisplay(); if (wantedLevel > policeCars.length) { // Spawn new police at random road var roadBlocks = []; for (var i = 0; i < cityBlocks.length; i++) { if (cityBlocks[i].type === 'road') roadBlocks.push(cityBlocks[i]); } var idx = Math.floor(Math.random() * roadBlocks.length); var b = roadBlocks[idx]; spawnPolice(b.x, b.y); } // Next mission after short delay LK.setTimeout(function () { resetMission(); }, 1200); } } // Win condition removed: game never ends when score >= 100 }; // --- Start State --- addScore(0); updateWantedDisplay(); timerTxt.setText(timeLeft + ''); resetMission(); playerHealth = 5; updateHealthDisplay(); playerMoney = 0; updateMoneyDisplay(); policeAggro = false; var policeHitCount = 0; ; ;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bullet class
var Bullet = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 48;
self.dir = 0;
self.owner = null; // 'player' or 'police'
self.update = function () {
self.x += Math.cos(self.dir) * self.speed;
self.y += Math.sin(self.dir) * self.speed;
};
return self;
});
// Car (stealable)
var Car = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0;
self.maxSpeed = 5; // much slower
self.acceleration = 0.2; // slower acceleration
self.deceleration = 0.3; // slower deceleration
self.direction = 0; // radians
self.driver = null; // player if inside
self.isPolice = false;
self.targetDir = 0; // for smooth steering
self.update = function () {
if (self.driver) {
// Smoothly turn toward target direction (set by dragDir)
var diff = self.targetDir - self.direction;
// Normalize angle to [-PI, PI]
while (diff > Math.PI) diff -= 2 * Math.PI;
while (diff < -Math.PI) diff += 2 * Math.PI;
self.direction += diff * 0.2; // smooth steering
// Gradually accelerate to maxSpeed
if (self.speed < self.maxSpeed) {
self.speed += self.acceleration;
if (self.speed > self.maxSpeed) self.speed = self.maxSpeed;
}
// Move in direction
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
} else {
// If no driver, slow down to stop
if (self.speed > 0) {
self.speed -= self.deceleration;
if (self.speed < 0) self.speed = 0;
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
}
}
};
return self;
});
// CityBlock: road or building
var CityBlock = Container.expand(function () {
var self = Container.call(this);
self.type = 'road'; // or 'building'
self.setType = function (type) {
self.type = type;
if (self.asset) {
self.removeChild(self.asset);
}
if (type === 'road') {
self.asset = self.attachAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
self.asset = self.attachAsset('building', {
anchorX: 0.5,
anchorY: 0.5
});
}
};
return self;
});
var Civilian = Container.expand(function () {
var self = Container.call(this);
// Pick a random color asset for this civilian
var civAssetIdx = 1 + Math.floor(Math.random() * 6);
var civAssetName = 'civilian' + civAssetIdx;
self.asset = self.attachAsset(civAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.asset.alpha = 0.7;
// Add 'npc' label above the civilian
self.npcLabel = new Text2('npc', {
size: 38,
fill: 0xFFFFFF
});
self.npcLabel.anchor.set(0.5, 1);
self.npcLabel.y = -40;
self.addChild(self.npcLabel);
self.speed = 2 + Math.random() * 2;
self.direction = Math.random() * Math.PI * 2;
self.update = function () {
// Move in direction, bounce off buildings
var nx = self.x + Math.cos(self.direction) * self.speed;
var ny = self.y + Math.sin(self.direction) * self.speed;
var onRoad = false;
for (var i = 0; i < cityBlocks.length; i++) {
var b = cityBlocks[i];
if (b.type === 'road') {
var left = b.x - blockSize / 2;
var right = b.x + blockSize / 2;
var top = b.y - blockSize / 2;
var bottom = b.y + blockSize / 2;
if (nx >= left && nx <= right && ny >= top && ny <= bottom) {
onRoad = true;
break;
}
}
}
if (onRoad) {
self.x = nx;
self.y = ny;
} else {
self.direction = Math.random() * Math.PI * 2;
}
};
return self;
});
// Spawn 6 civilians at random road blocks (not near player or police)
// Mission marker
var Mission = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('mission', {
anchorX: 0.5,
anchorY: 0.5
});
self.active = true;
return self;
});
// Player character
var Player = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Add 'you' label above the player
self.youLabel = new Text2('you', {
size: 38,
fill: 0xFFFFFF
});
self.youLabel.anchor.set(0.5, 1);
self.youLabel.y = -80;
self.addChild(self.youLabel);
self.speed = 16;
self.inCar = null; // reference to car if inside
self.direction = 0; // radians, 0 = right
// Add gun to player's hand
self.gun = self.attachAsset('gun', {
anchorX: 0.1,
anchorY: 0.5
});
self.gun.x = 60; // offset from player center
self.gun.y = 0;
self.gun.visible = true;
self.lastShotDir = 0; // radians
self.update = function () {
// If in car, follow car and hide player sprite
if (self.inCar) {
self.x = self.inCar.x;
self.y = self.inCar.y;
self.visible = false;
self.gun.visible = false;
self.youLabel.visible = false;
} else {
self.visible = true;
self.gun.visible = true;
self.youLabel.visible = true;
// Gun follows player hand and points at last shot direction
self.gun.x = 60 * Math.cos(self.lastShotDir);
self.gun.y = 60 * Math.sin(self.lastShotDir);
self.gun.rotation = self.lastShotDir;
}
};
return self;
});
// Police car
var PoliceCar = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('police', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 0;
self.maxSpeed = 2.5; // much slower (reduced for slower police)
self.acceleration = 0.08; // slower acceleration
self.deceleration = 0.15; // slower deceleration
self.direction = 0;
self.target = null; // player or car
self.isPolice = true;
self.targetDir = 0;
self.update = function () {
if (self.target) {
// Simple chase logic
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.targetDir = Math.atan2(dy, dx);
// Smoothly turn toward target direction
var diff = self.targetDir - self.direction;
while (diff > Math.PI) diff -= 2 * Math.PI;
while (diff < -Math.PI) diff += 2 * Math.PI;
self.direction += diff * 0.2;
// Accelerate to maxSpeed
if (self.speed < self.maxSpeed) {
self.speed += self.acceleration;
if (self.speed > self.maxSpeed) self.speed = self.maxSpeed;
}
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
} else {
self.speed = 0;
}
} else {
// If no target, slow down to stop
if (self.speed > 0) {
self.speed -= self.deceleration;
if (self.speed < 0) self.speed = 0;
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
}
}
};
return self;
});
// Police shooter class (policeman on foot)
var PoliceShooter = Container.expand(function () {
var self = Container.call(this);
self.asset = self.attachAsset('police', {
anchorX: 0.5,
anchorY: 0.5
});
// Add 'POLIS' label above the police shooter
self.polisLabel = new Text2('POLIS', {
size: 38,
fill: 0xFFFFFF
});
self.polisLabel.anchor.set(0.5, 1);
self.polisLabel.y = -80;
self.addChild(self.polisLabel);
// Add gun to police shooter's hand
self.gun = self.attachAsset('gun', {
anchorX: 0.1,
anchorY: 0.5
});
self.gun.x = 60;
self.gun.y = 0;
self.gun.visible = true;
self.addChild(self.gun);
self.speed = 5; // reduced for slower police
self.target = null;
self.lastShotTick = 0;
self.lastShotDir = 0; // direction of last shot
self.update = function () {
// If not aggro (no target), patrol randomly on roads
if (!self.target) {
if (typeof self.patrolTarget === "undefined" || !self.patrolTarget) {
// Pick a random road block as patrol target
var roadBlocks = [];
for (var k = 0; k < cityBlocks.length; k++) {
if (cityBlocks[k].type === 'road') roadBlocks.push(cityBlocks[k]);
}
if (roadBlocks.length > 0) {
var idx = Math.floor(Math.random() * roadBlocks.length);
self.patrolTarget = {
x: roadBlocks[idx].x + (Math.random() - 0.5) * 100,
y: roadBlocks[idx].y + (Math.random() - 0.5) * 100
};
}
}
if (self.patrolTarget) {
var dx = self.patrolTarget.x - self.x;
var dy = self.patrolTarget.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.direction = Math.atan2(dy, dx);
self.x += Math.cos(self.direction) * self.speed * 0.5;
self.y += Math.sin(self.direction) * self.speed * 0.5;
} else {
// Arrived at patrol target, pick a new one next frame
self.patrolTarget = null;
}
// Gun points in patrol direction
self.lastShotDir = self.direction;
self.gun.x = 60 * Math.cos(self.lastShotDir);
self.gun.y = 60 * Math.sin(self.lastShotDir);
self.gun.rotation = self.lastShotDir;
}
} else {
// Move toward player if target exists
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 120) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
}
// Gun points at player
self.lastShotDir = Math.atan2(dy, dx);
self.gun.x = 60 * Math.cos(self.lastShotDir);
self.gun.y = 60 * Math.sin(self.lastShotDir);
self.gun.rotation = self.lastShotDir;
}
// Firing handled in game.update
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
// No title, no description
// Set backgroundColor to vibrant green for lush map
backgroundColor: 0x1ecb2a
});
/****
* Game Code
****/
// --- Market Setup ---
var marketEntrance = new Container();
marketEntrance.asset = marketEntrance.attachAsset('building', {
anchorX: 0.5,
anchorY: 0.5
});
marketEntrance.asset.width = 300;
marketEntrance.asset.height = 300;
marketEntrance.x = (gridCols - 1) / 2 * blockSize + blockSize / 2 + 300; // rightmost road col + offset
marketEntrance.y = 0 * blockSize + blockSize / 2;
marketEntrance.isMarketEntrance = true;
game.addChild(marketEntrance);
// Market interior (hidden until entered)
var marketInterior = new Container();
marketInterior.asset = marketInterior.attachAsset('building', {
anchorX: 0.5,
anchorY: 0.5
});
marketInterior.asset.width = 800;
marketInterior.asset.height = 800;
marketInterior.x = 2048 / 2;
marketInterior.y = 2732 / 2;
marketInterior.visible = false;
game.addChild(marketInterior);
// Market buy ammo button
var marketBuyBtn = new Text2("₺50'ye 100 mermi al", {
size: 120,
fill: "#fff"
});
marketBuyBtn.anchor.set(0.5, 0.5);
marketBuyBtn.x = marketInterior.x;
marketBuyBtn.y = marketInterior.y + 100;
marketBuyBtn.visible = false;
game.addChild(marketBuyBtn);
// Market exit (door)
var marketExit = new Container();
marketExit.asset = marketExit.attachAsset('road', {
anchorX: 0.5,
anchorY: 0.5
});
marketExit.asset.width = 200;
marketExit.asset.height = 100;
marketExit.x = marketInterior.x;
marketExit.y = marketInterior.y + 350;
marketExit.visible = false;
marketExit.isMarketExit = true;
game.addChild(marketExit);
// Track if player is in market
var playerInMarket = false;
// marketStartPos will be initialized after player is created
var marketStartPos;
// --- Money on ground ---
// Police car - white box with blue
// Car - yellow box
// Player (hero) - red ellipse
// Building - blue box
// City block (road) - gray box
// --- Fixed City Map Setup ---
var blockSize = 400;
var cityBlocks = [];
var cityBlockMap = {}; // key: "col,row" -> CityBlock
// Fixed grid size
var gridCols = 9;
var gridRows = 13;
// Use a simple procedural pattern: every even col/row is road, others are building
function getBlockType(col, row) {
// Make a simple city: roads every 2 blocks, rest are buildings
if (col % 2 === 0 || row % 2 === 0) return 'road';
return 'building';
}
// Helper to get or create a city block at (col, row)
function getOrCreateCityBlock(col, row) {
// Clamp to fixed grid
if (col < -Math.floor(gridCols / 2) || col > Math.floor(gridCols / 2) || row < -Math.floor(gridRows / 2) || row > Math.floor(gridRows / 2)) {
return null;
}
var key = col + ',' + row;
if (cityBlockMap[key]) return cityBlockMap[key];
var block = new CityBlock();
var type = getBlockType(col, row);
block.setType(type);
block.x = col * blockSize + blockSize / 2;
block.y = row * blockSize + blockSize / 2;
cityBlockMap[key] = block;
cityBlocks.push(block);
game.addChild(block);
return block;
}
// Generate fixed grid (centered at 0,0)
var initialCols = gridCols,
initialRows = gridRows;
var centerCol = 0,
centerRow = 0;
for (var row = -Math.floor(initialRows / 2); row <= Math.floor(initialRows / 2); row++) {
for (var col = -Math.floor(initialCols / 2); col <= Math.floor(initialCols / 2); col++) {
getOrCreateCityBlock(col, row);
}
}
// Helper to get all road blocks in a region (for spawning)
function getRoadBlocksInRegion(colMin, colMax, rowMin, rowMax) {
var blocks = [];
for (var row = rowMin; row <= rowMax; row++) {
for (var col = colMin; col <= colMax; col++) {
var block = getOrCreateCityBlock(col, row);
if (block && block.type === 'road') blocks.push(block);
}
}
return blocks;
}
// Helper to get nearest road block col/row for a given x/y
function getColRowForXY(x, y) {
var col = Math.round((x - blockSize / 2) / blockSize);
var row = Math.round((y - blockSize / 2) / blockSize);
// Clamp to grid
col = Math.max(-Math.floor(gridCols / 2), Math.min(Math.floor(gridCols / 2), col));
row = Math.max(-Math.floor(gridRows / 2), Math.min(Math.floor(gridRows / 2), row));
return {
col: col,
row: row
};
}
// --- Player Setup ---
// Spawn player at center of the infinite world (col=0, row=0)
var player = new Player();
player.x = 0 * blockSize + blockSize / 2;
player.y = 0 * blockSize + blockSize / 2;
game.addChild(player);
// Now that player is defined, initialize marketStartPos
marketStartPos = {
x: player.x,
y: player.y
};
// --- Civilians Setup ---
var civilians = [];
// Spawn 6 civilians at random road blocks (not near player or police)
for (var i = 0; i < 6; i++) {
// Use a region around player for infinite world
var playerColRow = getColRowForXY(player.x, player.y);
var roadBlocks = getRoadBlocksInRegion(playerColRow.col - 7, playerColRow.col + 7, playerColRow.row - 7, playerColRow.row + 7);
// Avoid spawn near player
var filtered = [];
for (var j = 0; j < roadBlocks.length; j++) {
var b = roadBlocks[j];
var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2));
if (distToPlayer > 400) filtered.push(b);
}
if (filtered.length > 0) {
var idx = Math.floor(Math.random() * filtered.length);
var civ = new Civilian();
civ.x = filtered[idx].x + (Math.random() - 0.5) * 100;
civ.y = filtered[idx].y + (Math.random() - 0.5) * 100;
civilians.push(civ);
game.addChild(civ);
}
}
// --- Cars Setup ---
var cars = [];
function spawnCar(x, y) {
var car = new Car();
car.x = x;
car.y = y;
car.direction = Math.random() * Math.PI * 2;
cars.push(car);
game.addChild(car);
return car;
}
// Place a few cars on roads using fixed world
var roadBlocksForCars = [];
for (var i = 0; i < cityBlocks.length; i++) {
if (cityBlocks[i].type === 'road') {
// Remove player's starting block
if (!(cityBlocks[i].x === player.x && cityBlocks[i].y === player.y)) {
roadBlocksForCars.push(cityBlocks[i]);
}
}
}
// Shuffle
for (var k = roadBlocksForCars.length - 1; k > 0; k--) {
var j = Math.floor(Math.random() * (k + 1));
var temp = roadBlocksForCars[k];
roadBlocksForCars[k] = roadBlocksForCars[j];
roadBlocksForCars[j] = temp;
}
// Spawn up to 4 cars in distinct available road blocks
var carsToSpawn = 4;
for (var i = 0; i < Math.min(carsToSpawn, roadBlocksForCars.length); i++) {
var block = roadBlocksForCars[i];
spawnCar(block.x, block.y);
}
// --- Police Setup ---
var policeCars = [];
function spawnPolice(x, y) {
var police = new PoliceCar();
police.x = x;
police.y = y;
police.direction = 0;
// Police do not chase the player anymore
police.target = null;
policeCars.push(police);
game.addChild(police);
return police;
}
// Start with three police cars at different road blocks
var policeStartBlocks = [];
for (var i = 0; i < cityBlocks.length; i++) {
if (cityBlocks[i].type === 'road') {
policeStartBlocks.push(cityBlocks[i]);
}
}
if (policeStartBlocks.length > 0) {
// Always spawn one at the top left of region
var policeStartX = cityBlocks[0].x;
var policeStartY = cityBlocks[0].y;
spawnPolice(policeStartX, policeStartY);
// Pick two more random road blocks (not the same as above)
var used = {};
used[policeStartX + ',' + policeStartY] = true;
for (var p = 0; p < 2; p++) {
var idx = Math.floor(Math.random() * policeStartBlocks.length);
var b = policeStartBlocks[idx];
var key = b.x + ',' + b.y;
if (!used[key]) {
spawnPolice(b.x, b.y);
used[key] = true;
}
}
}
// --- Mission Setup ---
var mission = new Mission();
// Place mission at far corner of grid
mission.x = cityBlocks[cityBlocks.length - 1].x;
mission.y = cityBlocks[cityBlocks.length - 1].y;
game.addChild(mission);
// --- Money on ground ---
var moneyPickups = [];
function spawnMoney(x, y) {
var m = new Container();
m.asset = m.attachAsset('mission', {
anchorX: 0.5,
anchorY: 0.5
});
m.x = x;
m.y = y;
m.value = 50;
moneyPickups.push(m);
game.addChild(m);
return m;
}
// Place 15 money pickups at random road blocks in grid
var roadBlocksForMoney = [];
for (var i = 0; i < cityBlocks.length; i++) {
if (cityBlocks[i].type === 'road') {
roadBlocksForMoney.push(cityBlocks[i]);
}
}
for (var i = 0; i < 15; i++) {
if (roadBlocksForMoney.length > 0) {
var idx = Math.floor(Math.random() * roadBlocksForMoney.length);
var b = roadBlocksForMoney[idx];
var mx = b.x + (Math.random() - 0.5) * (blockSize - 100);
var my = b.y + (Math.random() - 0.5) * (blockSize - 100);
spawnMoney(mx, my);
}
}
// --- Score and Wanted Level ---
var score = 0;
var wantedLevel = 1; // 1-5
var scoreTxt = new Text2('Score: 0', {
size: 90,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var wantedTxt = new Text2('★', {
size: 80,
fill: "#ff0"
});
wantedTxt.anchor.set(1, 0); // anchor to top right
wantedTxt.x = 0;
wantedTxt.y = 0;
LK.gui.topRight.addChild(wantedTxt);
// (Removed 'elinde akm var' text below the wanted stars)
// --- Timer ---
var timeLeft = 60; // seconds
var timerTxt = new Text2('60', {
size: 80,
fill: "#fff"
});
timerTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timerTxt);
// --- Touch Controls ---
var dragDir = null; // {dx,dy}
var dragStart = null;
var dragNode = null;
var lastTouchX = 0,
lastTouchY = 0;
// --- Shooting & Health ---
var bullets = [];
var policeBullets = [];
var playerHealth = 5;
// --- Ammo system ---
var playerAmmo = 100;
var ammoTxt = new Text2('Ammo: 100', {
size: 80,
fill: "#fff"
});
// Anchor to top right (same as wanted stars)
ammoTxt.anchor.set(1, 0);
// Place just below the wanted stars (wantedTxt is at y=0, height ~80)
ammoTxt.x = 0;
ammoTxt.y = wantedTxt.y + wantedTxt.height + 10; // 10px below stars
LK.gui.topRight.addChild(ammoTxt);
function updateAmmoDisplay() {
ammoTxt.setText('Ammo: ' + playerAmmo);
}
// Money system
var playerMoney = 0;
var moneyTxt = new Text2('₺ 0', {
size: 120,
fill: 0x44FF44
});
moneyTxt.anchor.set(0, 0);
moneyTxt.x = 100;
moneyTxt.y = 90;
LK.gui.addChild(moneyTxt);
var healthTxt = new Text2('♥♥♥♥♥', {
size: 80,
fill: 0xFF4444
});
healthTxt.anchor.set(0, 0); // anchor top left
// Place health bar at (100,0) to avoid platform menu
healthTxt.x = 100;
healthTxt.y = 0;
LK.gui.addChild(healthTxt);
// Track if police are aggressive (chasing) or passive
var policeAggro = false;
// Helper to update health display
function updateHealthDisplay() {
var hearts = '';
for (var i = 0; i < playerHealth; i++) hearts += '♥';
healthTxt.setText(hearts);
}
function updateMoneyDisplay() {
moneyTxt.setText('₺ ' + playerMoney);
}
var policeShooters = [];
// Spawn a few police shooters at random road blocks, not near player (fixed world)
for (var i = 0; i < 8; i++) {
var roadBlocks = [];
for (var j = 0; j < cityBlocks.length; j++) {
if (cityBlocks[j].type === 'road') {
var b = cityBlocks[j];
var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2));
if (distToPlayer > 600) roadBlocks.push(b);
}
}
if (roadBlocks.length > 0) {
var idx = Math.floor(Math.random() * roadBlocks.length);
var shooter = new PoliceShooter();
shooter.x = roadBlocks[idx].x + (Math.random() - 0.5) * 100;
shooter.y = roadBlocks[idx].y + (Math.random() - 0.5) * 100;
shooter.target = null; // Do not target player at start
shooter.wasShotAt = false; // Only shoot if player shoots at them
policeShooters.push(shooter);
game.addChild(shooter);
}
}
// --- Helper Functions ---
function isOnRoad(x, y) {
// Returns true if (x,y) is on a road block (fixed world)
var colRow = getColRowForXY(x, y);
var block = getOrCreateCityBlock(colRow.col, colRow.row);
if (block && block.type === 'road') {
var left = block.x - blockSize / 2;
var right = block.x + blockSize / 2;
var top = block.y - blockSize / 2;
var bottom = block.y + blockSize / 2;
if (x >= left && x <= right && y >= top && y <= bottom) {
return true;
}
}
return false;
}
function clampToRoad(x, y) {
// Snap to nearest road block center (fixed world)
var colRow = getColRowForXY(x, y);
var block = getOrCreateCityBlock(colRow.col, colRow.row);
if (block && block.type === 'road') {
return {
x: block.x,
y: block.y
};
}
// If not road, search nearby
for (var r = -1; r <= 1; r++) {
for (var c = -1; c <= 1; c++) {
var b = getOrCreateCityBlock(colRow.col + c, colRow.row + r);
if (b && b.type === 'road') return {
x: b.x,
y: b.y
};
}
}
// Fallback: return original
return {
x: x,
y: y
};
}
function updateWantedDisplay() {
var stars = '';
for (var i = 0; i < wantedLevel; i++) stars += '★';
wantedTxt.setText(stars);
// Default color
var color = "#ff0";
// If 5 stars, flash red/blue
if (wantedLevel === 5) {
// Show "wandet" text for 3 seconds only on the frame wantedLevel becomes 5
if (typeof wandetTextShown === "undefined") wandetTextShown = false;
if (!wandetTextShown) {
// Remove previous if exists
if (typeof wandetText !== "undefined" && wandetText.parent) {
wandetText.parent.removeChild(wandetText);
wandetText = undefined;
}
wandetText = new Text2("wandet", {
size: 160,
fill: 0xFF2222
});
wandetText.anchor.set(0.5, 0);
wandetText.x = 2048 / 2;
wandetText.y = 500;
LK.gui.addChild(wandetText);
wandetTextShown = true;
LK.setTimeout(function () {
if (wandetText && wandetText.parent) {
wandetText.parent.removeChild(wandetText);
wandetText = undefined;
}
wandetTextShown = false;
}, 3000);
}
// Alternate color every 20 ticks (about 1/3 second at 60fps)
if (LK.ticks % 40 < 20) {
color = "#ff2222"; // red
} else {
color = "#2288ff"; // blue
}
}
if (wantedTxt && wantedTxt.style && typeof wantedTxt.style.fill !== "undefined") {
wantedTxt.style.fill = color;
}
}
function addScore(amt) {
score += amt;
scoreTxt.setText('Score: ' + score);
LK.setScore(score);
}
function resetMission() {
// Place mission at random road block (fixed world)
var roadBlocks = [];
for (var i = 0; i < cityBlocks.length; i++) {
if (cityBlocks[i].type === 'road') {
var b = cityBlocks[i];
var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2));
if (distToPlayer > 400) roadBlocks.push(b);
}
}
if (roadBlocks.length > 0) {
var idx = Math.floor(Math.random() * roadBlocks.length);
mission.x = roadBlocks[idx].x;
mission.y = roadBlocks[idx].y;
mission.active = true;
mission.visible = true;
}
}
// --- Game Touch Controls ---
// Drag to move (swipe in direction to move)
game.down = function (x, y, obj) {
// Don't allow drag from top left 100x100
if (x < 100 && y < 100) return;
// --- SHOOTING LOGIC ---
// If not dragging, treat as tap to shoot
if (!dragNode && !dragDir && !player.inCar) {
if (typeof playerAmmo === "undefined") playerAmmo = 0;
if (playerAmmo > 0) {
// Player shoots toward (x, y)
var b = new Bullet();
b.x = player.x;
b.y = player.y;
b.dir = Math.atan2(y - b.y, x - b.x);
b.owner = 'player';
bullets.push(b);
game.addChild(b);
// Update player's gun direction to point at shot
player.lastShotDir = b.dir;
playerAmmo--;
if (typeof updateAmmoDisplay === "function") updateAmmoDisplay();
} else {
// Out of ammo: show "mermi bitti" for 1 second at center-top
if (typeof ammoEmptyText !== "undefined" && ammoEmptyText.parent) {
ammoEmptyText.parent.removeChild(ammoEmptyText);
ammoEmptyText = undefined;
}
ammoEmptyText = new Text2("mermi bitti", {
size: 120,
fill: 0xFF2222
});
ammoEmptyText.anchor.set(0.5, 0);
ammoEmptyText.x = 2048 / 2;
ammoEmptyText.y = 320;
LK.gui.addChild(ammoEmptyText);
LK.setTimeout(function () {
if (ammoEmptyText && ammoEmptyText.parent) {
ammoEmptyText.parent.removeChild(ammoEmptyText);
ammoEmptyText = undefined;
}
}, 1000);
}
// No police aggro or wanted level increase when shooting police
}
// --- DRAG LOGIC (movement) ---
dragStart = {
x: x,
y: y
};
lastTouchX = x;
lastTouchY = y;
dragDir = null;
dragNode = player;
};
game.move = function (x, y, obj) {
if (!dragNode) return;
var dx = x - dragStart.x;
var dy = y - dragStart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 30) {
dragDir = {
dx: dx,
dy: dy
};
}
lastTouchX = x;
lastTouchY = y;
};
game.up = function (x, y, obj) {
dragNode = null;
dragDir = null;
dragStart = null;
};
// --- Car Enter/Exit Logic ---
// Track tap times for triple-tap exit
var carTapTimes = [];
game.on('down', function (x, y, obj) {
// If player is not in car, check if tap is on car and player is close enough
if (!player.inCar) {
for (var i = 0; i < cars.length; i++) {
var car = cars[i];
var dx = car.x - x;
var dy = car.y - y;
// Tap must be on car
if (Math.abs(dx) < 80 && Math.abs(dy) < 50) {
// Player must be close to car to enter
if (Math.abs(player.x - car.x) < 120 && Math.abs(player.y - car.y) < 80) {
player.inCar = car;
car.driver = player;
// Do not set car.speed here; Car.update will handle acceleration
car.direction = 0;
// Increase wanted level
wantedLevel = Math.min(5, wantedLevel + 1);
updateWantedDisplay();
// Police target car
for (var j = 0; j < policeCars.length; j++) {
policeCars[j].target = car;
}
// Reset tap times for exit
carTapTimes = [];
break;
}
}
}
} else {
// Player is in car: require triple-tap within 1 second to exit
var now = Date.now();
carTapTimes.push(now);
// Only keep last 3 taps
if (carTapTimes.length > 3) carTapTimes.shift();
if (carTapTimes.length === 3) {
// Check if 3 taps are within 1 second
if (carTapTimes[2] - carTapTimes[0] < 1000) {
var car = player.inCar;
player.inCar = null;
car.driver = null;
// Do not set car.speed to 0; Car.update will handle deceleration
// Place player next to car
player.x = car.x + 80;
player.y = car.y;
// Police target player
for (var j = 0; j < policeCars.length; j++) {
policeCars[j].target = player;
}
carTapTimes = [];
}
}
}
});
// --- Game Update Loop ---
game.update = function () {
// Timer
if (LK.ticks % 60 === 0 && timeLeft > 0) {
timeLeft--;
timerTxt.setText(timeLeft + '');
// Do not end the game when timer reaches zero
}
// --- Market logic ---
// If not in market, check for market entrance
if (!playerInMarket) {
// Check if player is at market entrance (right edge)
if (Math.abs(player.x - marketEntrance.x) < 120 && Math.abs(player.y - marketEntrance.y) < 120) {
// Teleport to market interior
playerInMarket = true;
marketStartPos.x = player.x;
marketStartPos.y = player.y;
player.x = marketInterior.x;
player.y = marketInterior.y - 100;
marketInterior.visible = true;
marketBuyBtn.visible = true;
marketExit.visible = true;
// Hide city elements
for (var i = 0; i < cityBlocks.length; i++) cityBlocks[i].visible = false;
for (var i = 0; i < cars.length; i++) cars[i].visible = false;
for (var i = 0; i < civilians.length; i++) civilians[i].visible = false;
for (var i = 0; i < policeCars.length; i++) policeCars[i].visible = false;
for (var i = 0; i < policeShooters.length; i++) policeShooters[i].visible = false;
for (var i = 0; i < moneyPickups.length; i++) moneyPickups[i].visible = false;
if (typeof gunPickups !== "undefined") for (var i = 0; i < gunPickups.length; i++) gunPickups[i].visible = false;
mission.visible = false;
marketEntrance.visible = false;
}
} else {
// In market: only allow movement inside market area
if (dragDir) {
var angle = Math.atan2(dragDir.dy, dragDir.dx);
var nx = player.x + Math.cos(angle) * player.speed;
var ny = player.y + Math.sin(angle) * player.speed;
// Clamp to market interior bounds
var left = marketInterior.x - 350,
right = marketInterior.x + 350;
var top = marketInterior.y - 350,
bottom = marketInterior.y + 350;
if (nx > left && nx < right && ny > top && ny < bottom) {
player.x = nx;
player.y = ny;
}
}
// Check for ammo buy (touch on buy button)
if (Math.abs(player.x - marketBuyBtn.x) < 200 && Math.abs(player.y - marketBuyBtn.y) < 100) {
if (playerMoney >= 50 && playerAmmo < 100) {
playerMoney -= 50;
playerAmmo = 100;
updateMoneyDisplay();
updateAmmoDisplay();
// Show "100 mermi alındı" text
if (typeof marketBuyText !== "undefined" && marketBuyText.parent) {
marketBuyText.parent.removeChild(marketBuyText);
marketBuyText = undefined;
}
marketBuyText = new Text2("100 mermi alındı", {
size: 120,
fill: 0x44FF44
});
marketBuyText.anchor.set(0.5, 0);
marketBuyText.x = 2048 / 2;
marketBuyText.y = 320;
LK.gui.addChild(marketBuyText);
LK.setTimeout(function () {
if (marketBuyText && marketBuyText.parent) {
marketBuyText.parent.removeChild(marketBuyText);
marketBuyText = undefined;
}
}, 1000);
}
}
// Check for exit (touch on exit door)
if (Math.abs(player.x - marketExit.x) < 120 && Math.abs(player.y - marketExit.y) < 80) {
// Teleport back to start
player.x = marketStartPos.x;
player.y = marketStartPos.y;
playerInMarket = false;
marketInterior.visible = false;
marketBuyBtn.visible = false;
marketExit.visible = false;
// Show city elements again
for (var i = 0; i < cityBlocks.length; i++) cityBlocks[i].visible = true;
for (var i = 0; i < cars.length; i++) cars[i].visible = true;
for (var i = 0; i < civilians.length; i++) civilians[i].visible = true;
for (var i = 0; i < policeCars.length; i++) policeCars[i].visible = true;
for (var i = 0; i < policeShooters.length; i++) policeShooters[i].visible = true;
for (var i = 0; i < moneyPickups.length; i++) moneyPickups[i].visible = true;
if (typeof gunPickups !== "undefined") for (var i = 0; i < gunPickups.length; i++) gunPickups[i].visible = true;
mission.visible = true;
marketEntrance.visible = true;
}
}
// Player movement
if (!player.inCar && dragDir && !playerInMarket) {
// Move player in drag direction, but only on road
var angle = Math.atan2(dragDir.dy, dragDir.dx);
var nx = player.x + Math.cos(angle) * player.speed;
var ny = player.y + Math.sin(angle) * player.speed;
if (isOnRoad(nx, ny)) {
player.x = nx;
player.y = ny;
}
// No infinite world: no need to generate new blocks in update
}
// Car movement (if player in car)
for (var i = 0; i < cars.length; i++) {
var car = cars[i];
// If player is driving this car, set car's targetDir to drag direction
if (car.driver === player && dragDir) {
var angle = Math.atan2(dragDir.dy, dragDir.dx);
car.targetDir = angle;
}
car.update();
// Clamp car to road
if (car.driver) {
if (!isOnRoad(car.x, car.y)) {
// Stop car at edge
car.speed = 0;
}
}
}
// Police movement
for (var i = 0; i < policeCars.length; i++) {
var police = policeCars[i];
// Police patrol logic: move randomly on roads if not chasing
if (!policeAggro) {
if (typeof police.patrolTarget === "undefined" || !police.patrolTarget) {
// Pick a random road block as patrol target
var roadBlocks = [];
for (var k = 0; k < cityBlocks.length; k++) {
if (cityBlocks[k].type === 'road') roadBlocks.push(cityBlocks[k]);
}
if (roadBlocks.length > 0) {
var idx = Math.floor(Math.random() * roadBlocks.length);
police.patrolTarget = {
x: roadBlocks[idx].x + (Math.random() - 0.5) * 100,
y: roadBlocks[idx].y + (Math.random() - 0.5) * 100
};
}
}
if (police.patrolTarget) {
var dx = police.patrolTarget.x - police.x;
var dy = police.patrolTarget.y - police.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
police.direction = Math.atan2(dy, dx);
police.speed = police.maxSpeed * 0.5;
police.x += Math.cos(police.direction) * police.speed;
police.y += Math.sin(police.direction) * police.speed;
} else {
// Arrived at patrol target, pick a new one next frame
police.patrolTarget = null;
police.speed = 0;
}
}
} else {
police.update();
}
// Clamp police to road
if (!isOnRoad(police.x, police.y)) {
police.speed = 0;
}
// Police catch player
var target = police.target;
if (policeAggro && target && Math.abs(police.x - target.x) < 80 && Math.abs(police.y - target.y) < 80) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
return;
}
}
// Police shooters update
for (var i = policeShooters.length - 1; i >= 0; i--) {
var shooter = policeShooters[i];
shooter.update();
// Remove if off map
if (shooter.x < 0 || shooter.x > 2048 || shooter.y < 0 || shooter.y > 2732) {
shooter.destroy();
policeShooters.splice(i, 1);
continue;
}
// --- Police shooter fires at player only if aggro (wasShotAt or policeAggro) ---
// Only fire if player is within 700px, not in car, and shooter is aggro
if (shooter.wasShotAt || policeAggro) {
var px = player.inCar ? player.inCar.x : player.x;
var py = player.inCar ? player.inCar.y : player.y;
var dx = px - shooter.x;
var dy = py - shooter.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 700 && LK.ticks - shooter.lastShotTick > 60) {
// Fire bullet toward player
var b = new Bullet();
b.x = shooter.x;
b.y = shooter.y;
b.dir = Math.atan2(dy, dx);
b.owner = 'police';
policeBullets.push(b);
game.addChild(b);
shooter.lastShotTick = LK.ticks;
}
}
}
// Bullets update (player)
for (var i = bullets.length - 1; i >= 0; i--) {
var b = bullets[i];
if (b && typeof b.update === "function") {
b.update();
}
// Remove if off screen
if (b && typeof b.x === "number" && typeof b.y === "number" && (b.x < 0 || b.x > 2048 || b.y < 0 || b.y > 2732)) {
b.destroy();
bullets.splice(i, 1);
continue;
}
// --- Police car kill check ---
for (var j = policeCars.length - 1; j >= 0; j--) {
var police = policeCars[j];
// --- Car bullet hit logic ---
if (police && b && typeof police.x === "number" && typeof police.y === "number" && typeof b.x === "number" && typeof b.y === "number" && Math.abs(police.x - b.x) < 60 && Math.abs(police.y - b.y) < 60) {
// Track bullet hits on this car
if (typeof police.bulletHitCount === "undefined") police.bulletHitCount = 0;
police.bulletHitCount++;
// Remove bullet
b.destroy();
bullets.splice(i, 1);
// If hit 30 times, destroy car and show "arba patladı"
if (police.bulletHitCount >= 30) {
// Drop a gun at police car's position
var gunDrop = new Container();
gunDrop.asset = gunDrop.attachAsset('gun', {
anchorX: 0.5,
anchorY: 0.5
});
// Add 'gun' label above the dropped gun
gunDrop.gunLabel = new Text2('gun', {
size: 38,
fill: 0xFFFFFF
});
gunDrop.gunLabel.anchor.set(0.5, 1);
gunDrop.gunLabel.y = -30;
gunDrop.addChild(gunDrop.gunLabel);
gunDrop.x = police.x;
gunDrop.y = police.y;
gunDrop.isGunPickup = true;
game.addChild(gunDrop);
if (typeof gunPickups === "undefined") gunPickups = [];
gunPickups.push(gunDrop);
police.destroy();
policeCars.splice(j, 1);
// Show "arba patladı" text for 2 seconds at center of screen
if (typeof carExplodedText !== "undefined" && carExplodedText.parent) {
carExplodedText.parent.removeChild(carExplodedText);
carExplodedText = undefined;
}
carExplodedText = new Text2("arba patladı", {
size: 160,
fill: 0xFF2222
});
carExplodedText.anchor.set(0.5, 0.5);
carExplodedText.x = 2048 / 2;
carExplodedText.y = 2732 / 2;
LK.gui.addChild(carExplodedText);
LK.setTimeout(function () {
if (carExplodedText && carExplodedText.parent) {
carExplodedText.parent.removeChild(carExplodedText);
carExplodedText = undefined;
}
}, 2000);
addScore(20);
// Increase wanted level by 1 (max 5) when police car is killed
wantedLevel = Math.min(5, wantedLevel + 1);
updateWantedDisplay();
// Only aggro police if wantedLevel is 5
if (wantedLevel === 5 && typeof spawned5StarPolice === "undefined") {
for (var p = 0; p < policeShooters.length; p++) {
policeShooters[p].wasShotAt = true;
policeShooters[p].target = player;
}
for (var p = 0; p < policeCars.length; p++) {
policeCars[p].target = player;
}
policeAggro = true;
// Spawn 20 police cars at random road blocks (not near player)
var roadBlocks = [];
for (var i = 0; i < cityBlocks.length; i++) {
if (cityBlocks[i].type === 'road') {
var distToPlayer = Math.sqrt(Math.pow(cityBlocks[i].x - player.x, 2) + Math.pow(cityBlocks[i].y - player.y, 2));
if (distToPlayer > 400) {
roadBlocks.push(cityBlocks[i]);
}
}
}
for (var s = 0; s < 20; s++) {
if (roadBlocks.length > 0) {
var idx = Math.floor(Math.random() * roadBlocks.length);
var b = roadBlocks[idx];
var newPolice = spawnPolice(b.x, b.y);
newPolice.target = player;
// Remove this block from roadBlocks to avoid double-spawn at same spot
roadBlocks.splice(idx, 1);
}
}
// --- Spawn 40 police shooters at random road blocks, not near player ---
for (var spawnCount = 0; spawnCount < 40; spawnCount++) {
var shooterBlocks = [];
for (var k = 0; k < cityBlocks.length; k++) {
var b2 = cityBlocks[k];
if (b2.type === 'road') {
var distToPlayer = Math.sqrt(Math.pow(b2.x - player.x, 2) + Math.pow(b2.y - player.y, 2));
if (distToPlayer > 600) {
shooterBlocks.push(b2);
}
}
}
if (shooterBlocks.length > 0) {
var idx2 = Math.floor(Math.random() * shooterBlocks.length);
var newShooter = new PoliceShooter();
newShooter.x = shooterBlocks[idx2].x + (Math.random() - 0.5) * 100;
newShooter.y = shooterBlocks[idx2].y + (Math.random() - 0.5) * 100;
newShooter.target = player;
newShooter.wasShotAt = true;
policeShooters.push(newShooter);
game.addChild(newShooter);
shooterBlocks.splice(idx2, 1);
}
}
spawned5StarPolice = true;
}
break;
}
// If not destroyed, do not break, allow other cars to be checked
continue;
}
// --- END Car bullet hit logic ---
}
// --- Police shooter kill check ---
for (var j = policeShooters.length - 1; j >= 0; j--) {
var shooter = policeShooters[j];
if (shooter && b && typeof shooter.x === "number" && typeof shooter.y === "number" && typeof b.x === "number" && typeof b.y === "number" && Math.abs(shooter.x - b.x) < 60 && Math.abs(shooter.y - b.y) < 60) {
// Mark this shooter as shot at, so it will shoot back
shooter.wasShotAt = true;
// --- Police shooter health logic ---
// Instantly kill police shooter with one bullet
b.destroy();
bullets.splice(i, 1);
// Police shooter killed!
// Drop a gun at police shooter's position
var gunDrop = new Container();
gunDrop.asset = gunDrop.attachAsset('gun', {
anchorX: 0.5,
anchorY: 0.5
});
// Add 'gun' label above the dropped gun
gunDrop.gunLabel = new Text2('gun', {
size: 38,
fill: 0xFFFFFF
});
gunDrop.gunLabel.anchor.set(0.5, 1);
gunDrop.gunLabel.y = -30;
gunDrop.addChild(gunDrop.gunLabel);
gunDrop.x = shooter.x;
gunDrop.y = shooter.y;
gunDrop.isGunPickup = true;
game.addChild(gunDrop);
if (typeof gunPickups === "undefined") gunPickups = [];
gunPickups.push(gunDrop);
shooter.destroy();
policeShooters.splice(j, 1);
// --- Spawn TWO new police shooters at random road blocks, not near player ---
for (var spawnCount = 0; spawnCount < 2; spawnCount++) {
var roadBlocks = [];
for (var k = 0; k < cityBlocks.length; k++) {
var b2 = cityBlocks[k];
if (b2.type === 'road') {
var distToPlayer = Math.sqrt(Math.pow(b2.x - player.x, 2) + Math.pow(b2.y - player.y, 2));
if (distToPlayer > 600) {
roadBlocks.push(b2);
}
}
}
if (roadBlocks.length > 0) {
var idx2 = Math.floor(Math.random() * roadBlocks.length);
var newShooter = new PoliceShooter();
newShooter.x = roadBlocks[idx2].x + (Math.random() - 0.5) * 100;
newShooter.y = roadBlocks[idx2].y + (Math.random() - 0.5) * 100;
if (policeAggro) {
newShooter.target = player;
newShooter.wasShotAt = true;
} else {
newShooter.target = null;
newShooter.wasShotAt = false;
}
policeShooters.push(newShooter);
game.addChild(newShooter);
}
}
addScore(15);
// Increase wanted level by 1 (max 5) when police shooter is killed
wantedLevel = Math.min(5, wantedLevel + 1);
updateWantedDisplay();
break;
// If not dead, do not break, allow other shooters to be checked
}
}
}
// Bullets update (police)
for (var i = policeBullets.length - 1; i >= 0; i--) {
var b = policeBullets[i];
if (b && typeof b.update === "function") {
b.update();
}
// Remove if off screen
if (b && typeof b.x === "number" && typeof b.y === "number" && (b.x < 0 || b.x > 2048 || b.y < 0 || b.y > 2732)) {
b.destroy();
policeBullets.splice(i, 1);
continue;
}
// Check hit player
var px = player.inCar ? player.inCar.x : player.x;
var py = player.inCar ? player.inCar.y : player.y;
if (Math.abs(px - b.x) < 60 && Math.abs(py - b.y) < 60) {
// Police bullet hits player: decrease health if not in car
if (!player.inCar && playerHealth > 0) {
playerHealth--;
updateHealthDisplay();
// Optional: flash screen on hit
LK.effects.flashScreen(0xff0000, 200);
// Game over if health reaches 0
if (playerHealth <= 0) {
LK.showGameOver();
return;
}
}
// If player is in car, do not take damage
b.destroy();
policeBullets.splice(i, 1);
}
}
// Player update
player.update();
// Civilians update and check for being shot by player or run over by car
for (var i = civilians.length - 1; i >= 0; i--) {
var civ = civilians[i];
if (civ && typeof civ.update === "function") {
civ.update();
}
var killed = false;
// Check if hit by any player bullet
for (var j = bullets.length - 1; j >= 0; j--) {
var b = bullets[j];
if (Math.abs(civ.x - b.x) < 60 && Math.abs(civ.y - b.y) < 60) {
killed = true;
// Remove bullet
b.destroy();
bullets.splice(j, 1);
break;
}
}
// Check if run over by any car driven by player
if (!killed) {
for (var c = 0; c < cars.length; c++) {
var car = cars[c];
if (car.driver === player) {
// Use a slightly larger hitbox for car
if (Math.abs(civ.x - car.x) < 90 && Math.abs(civ.y - car.y) < 70) {
killed = true;
break;
}
}
}
}
if (killed) {
// Civilian killed
// Drop money at civilian's position
spawnMoney(civ.x, civ.y);
// Respawn civilian at random road block (fixed world)
var roadBlocks = [];
for (var k = 0; k < cityBlocks.length; k++) {
if (cityBlocks[k].type === 'road') {
var b = cityBlocks[k];
var distToPlayer = Math.sqrt(Math.pow(b.x - player.x, 2) + Math.pow(b.y - player.y, 2));
if (distToPlayer > 400) roadBlocks.push(b);
}
}
if (roadBlocks.length > 0) {
var idx = Math.floor(Math.random() * roadBlocks.length);
var newCiv = new Civilian();
newCiv.x = roadBlocks[idx].x + (Math.random() - 0.5) * 100;
newCiv.y = roadBlocks[idx].y + (Math.random() - 0.5) * 100;
civilians.push(newCiv);
game.addChild(newCiv);
}
civ.destroy();
civilians.splice(i, 1);
addScore(10);
// Increase wanted level by 1 every time player kills an NPC, max 5
wantedLevel = Math.min(5, wantedLevel + 1);
updateWantedDisplay();
// Only aggro police if wantedLevel is 5
if (wantedLevel === 5) {
for (var p = 0; p < policeShooters.length; p++) {
policeShooters[p].wasShotAt = true;
policeShooters[p].target = player;
}
for (var p = 0; p < policeCars.length; p++) {
policeCars[p].target = player;
}
policeAggro = true;
}
// No break here: allow this to happen every time an NPC is killed
continue;
}
// --- Police run over by car logic ---
// Player can run over police shooters and police cars when driving a car
for (var c = 0; c < cars.length; c++) {
var car = cars[c];
if (car.driver === player) {
// Run over police shooters
for (var s = policeShooters.length - 1; s >= 0; s--) {
var shooter = policeShooters[s];
if (Math.abs(shooter.x - car.x) < 90 && Math.abs(shooter.y - car.y) < 70) {
// Police shooter killed by car
// Drop a gun at police shooter's position
var gunDrop = new Container();
gunDrop.asset = gunDrop.attachAsset('gun', {
anchorX: 0.5,
anchorY: 0.5
});
gunDrop.gunLabel = new Text2('gun', {
size: 38,
fill: 0xFFFFFF
});
gunDrop.gunLabel.anchor.set(0.5, 1);
gunDrop.gunLabel.y = -30;
gunDrop.addChild(gunDrop.gunLabel);
gunDrop.x = shooter.x;
gunDrop.y = shooter.y;
gunDrop.isGunPickup = true;
game.addChild(gunDrop);
if (typeof gunPickups === "undefined") gunPickups = [];
gunPickups.push(gunDrop);
shooter.destroy();
policeShooters.splice(s, 1);
// Spawn TWO new police shooters at random road blocks, not near player
for (var spawnCount = 0; spawnCount < 2; spawnCount++) {
var roadBlocks = [];
for (var k = 0; k < cityBlocks.length; k++) {
var b2 = cityBlocks[k];
if (b2.type === 'road') {
var distToPlayer = Math.sqrt(Math.pow(b2.x - player.x, 2) + Math.pow(b2.y - player.y, 2));
if (distToPlayer > 600) {
roadBlocks.push(b2);
}
}
}
if (roadBlocks.length > 0) {
var idx2 = Math.floor(Math.random() * roadBlocks.length);
var newShooter = new PoliceShooter();
newShooter.x = roadBlocks[idx2].x + (Math.random() - 0.5) * 100;
newShooter.y = roadBlocks[idx2].y + (Math.random() - 0.5) * 100;
if (policeAggro) {
newShooter.target = player;
newShooter.wasShotAt = true;
} else {
newShooter.target = null;
newShooter.wasShotAt = false;
}
policeShooters.push(newShooter);
game.addChild(newShooter);
}
}
addScore(15);
// Increase wanted level by 1 (max 5) when police shooter is killed
wantedLevel = Math.min(5, wantedLevel + 1);
updateWantedDisplay();
// Only aggro police if wantedLevel is 5
if (wantedLevel === 5) {
for (var p = 0; p < policeShooters.length; p++) {
policeShooters[p].wasShotAt = true;
policeShooters[p].target = player;
}
for (var p = 0; p < policeCars.length; p++) {
policeCars[p].target = player;
}
policeAggro = true;
}
// Only allow one run over per car per frame
break;
}
}
// Run over police cars
for (var pc = policeCars.length - 1; pc >= 0; pc--) {
var police = policeCars[pc];
if (Math.abs(police.x - car.x) < 90 && Math.abs(police.y - car.y) < 70) {
// Police car killed by car
// Drop a gun at police car's position
var gunDrop = new Container();
gunDrop.asset = gunDrop.attachAsset('gun', {
anchorX: 0.5,
anchorY: 0.5
});
gunDrop.gunLabel = new Text2('gun', {
size: 38,
fill: 0xFFFFFF
});
gunDrop.gunLabel.anchor.set(0.5, 1);
gunDrop.gunLabel.y = -30;
gunDrop.addChild(gunDrop.gunLabel);
gunDrop.x = police.x;
gunDrop.y = police.y;
gunDrop.isGunPickup = true;
game.addChild(gunDrop);
if (typeof gunPickups === "undefined") gunPickups = [];
gunPickups.push(gunDrop);
police.destroy();
policeCars.splice(pc, 1);
addScore(20);
// Increase wanted level by 1 (max 5) when police car is killed
wantedLevel = Math.min(5, wantedLevel + 1);
updateWantedDisplay();
// Only aggro police if wantedLevel is 5
if (wantedLevel === 5 && typeof spawned5StarPolice === "undefined") {
for (var p = 0; p < policeShooters.length; p++) {
policeShooters[p].wasShotAt = true;
policeShooters[p].target = player;
}
for (var p = 0; p < policeCars.length; p++) {
policeCars[p].target = player;
}
policeAggro = true;
// Spawn 5 police cars at random road blocks (not near player)
var roadBlocks = [];
for (var i = 0; i < cityBlocks.length; i++) {
if (cityBlocks[i].type === 'road') {
var distToPlayer = Math.sqrt(Math.pow(cityBlocks[i].x - player.x, 2) + Math.pow(cityBlocks[i].y - player.y, 2));
if (distToPlayer > 400) {
roadBlocks.push(cityBlocks[i]);
}
}
}
for (var s = 0; s < 5; s++) {
if (roadBlocks.length > 0) {
var idx = Math.floor(Math.random() * roadBlocks.length);
var b = roadBlocks[idx];
var newPolice = spawnPolice(b.x, b.y);
newPolice.target = player;
// Remove this block from roadBlocks to avoid double-spawn at same spot
roadBlocks.splice(idx, 1);
}
}
spawned5StarPolice = true;
}
// Only allow one run over per car per frame
break;
}
}
}
}
}
// Money pickup check
var px = player.inCar ? player.inCar.x : player.x;
var py = player.inCar ? player.inCar.y : player.y;
for (var i = moneyPickups.length - 1; i >= 0; i--) {
var m = moneyPickups[i];
if (Math.abs(m.x - px) < 60 && Math.abs(m.y - py) < 60) {
playerMoney += m.value;
updateMoneyDisplay();
m.destroy();
moneyPickups.splice(i, 1);
// Show "+50 money" text for 1 second at center-top of screen
if (typeof moneyPickupText !== "undefined" && moneyPickupText.parent) {
moneyPickupText.parent.removeChild(moneyPickupText);
moneyPickupText = undefined;
}
moneyPickupText = new Text2("+50 money", {
size: 120,
fill: 0x44FF44
});
moneyPickupText.anchor.set(0.5, 0);
moneyPickupText.x = 2048 / 2;
moneyPickupText.y = 320;
LK.gui.addChild(moneyPickupText);
LK.setTimeout(function () {
if (moneyPickupText && moneyPickupText.parent) {
moneyPickupText.parent.removeChild(moneyPickupText);
moneyPickupText = undefined;
}
}, 1000);
}
}
// Gun pickup check
if (typeof gunPickups === "undefined") gunPickups = [];
for (var i = gunPickups.length - 1; i >= 0; i--) {
var g = gunPickups[i];
if (Math.abs(g.x - px) < 60 && Math.abs(g.y - py) < 60) {
// Player picks up gun: restore ammo to 100
playerAmmo = 100;
if (typeof updateAmmoDisplay === "function") updateAmmoDisplay();
g.destroy();
gunPickups.splice(i, 1);
// Show "+1 gun" text for 1 second at center-top of screen
if (typeof gunPickupText !== "undefined" && gunPickupText.parent) {
gunPickupText.parent.removeChild(gunPickupText);
gunPickupText = undefined;
}
gunPickupText = new Text2("+1 gun", {
size: 120,
fill: 0xFFF700
});
gunPickupText.anchor.set(0.5, 0);
// Place at top center, but not in top left 100x100
gunPickupText.x = 2048 / 2;
gunPickupText.y = 180;
LK.gui.addChild(gunPickupText);
// Remove after 1 second
LK.setTimeout(function () {
if (gunPickupText && gunPickupText.parent) {
gunPickupText.parent.removeChild(gunPickupText);
gunPickupText = undefined;
}
}, 1000);
// Optionally: add logic for ammo, weapon upgrade, etc.
}
}
// Mission check
if (mission.active) {
var px = player.inCar ? player.inCar.x : player.x;
var py = player.inCar ? player.inCar.y : player.y;
if (Math.abs(mission.x - px) < 80 && Math.abs(mission.y - py) < 80) {
// Complete mission
addScore(10 * wantedLevel);
mission.active = false;
mission.visible = false;
// Increase wanted level, spawn more police
wantedLevel = Math.min(5, wantedLevel + 1);
updateWantedDisplay();
if (wantedLevel > policeCars.length) {
// Spawn new police at random road
var roadBlocks = [];
for (var i = 0; i < cityBlocks.length; i++) {
if (cityBlocks[i].type === 'road') roadBlocks.push(cityBlocks[i]);
}
var idx = Math.floor(Math.random() * roadBlocks.length);
var b = roadBlocks[idx];
spawnPolice(b.x, b.y);
}
// Next mission after short delay
LK.setTimeout(function () {
resetMission();
}, 1200);
}
}
// Win condition removed: game never ends when score >= 100
};
// --- Start State ---
addScore(0);
updateWantedDisplay();
timerTxt.setText(timeLeft + '');
resetMission();
playerHealth = 5;
updateHealthDisplay();
playerMoney = 0;
updateMoneyDisplay();
policeAggro = false;
var policeHitCount = 0;
;
;