/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Building class var Building = Container.expand(function () { var self = Container.call(this); var buildingSprite = self.attachAsset('building', { anchorX: 0.5, anchorY: 0.5 }); self.radius = buildingSprite.width / 2; self.entry = null; self.hasEntry = false; self.entryX = 0; self.entryY = 0; self.id = ''; self.addEntry = function () { self.hasEntry = true; self.entry = self.attachAsset('building_entry', { anchorX: 0.5, anchorY: 0.5, x: buildingSprite.width / 2 - 30, y: buildingSprite.height / 2 - 30 }); self.entryX = self.x + buildingSprite.width / 2 - 30; self.entryY = self.y + buildingSprite.height / 2 - 30; }; return self; }); // Corridor class for building interiors var Corridor = Container.expand(function () { var self = Container.call(this); // Use the 'en' asset for the corridor's visual appearance var corridorSprite = self.attachAsset('en', { anchorX: 0.5, anchorY: 0.5 }); corridorSprite.width = 1100; corridorSprite.height = 340; self.radius = corridorSprite.width / 2; return self; }); // Helicopter class var Helicopter = Container.expand(function () { var self = Container.call(this); var heliSprite = self.attachAsset('helicopter', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 28; self.radius = heliSprite.width / 2; self.target = null; self.update = function () { if (!self.target) { return; } // Hover above target var dx = self.target.x - self.x; var dy = self.target.y - 300 - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.x += self.speed * dx / dist; self.y += self.speed * dy / dist; } }; return self; }); // Item class var Item = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'item'; var assetId = 'item'; if (self.type === 'flower') { assetId = 'item_flower'; } else if (self.type === 'coin') { assetId = 'item_coin'; } else if (self.type === 'book') { assetId = 'item_book'; } else if (self.type === 'bag') { assetId = 'item_bag'; } else if (self.type === 'key') { assetId = 'item_key'; } else if (self.type === 'map') { assetId = 'item_map'; } else if (self.type === 'wallet') { assetId = 'item_wallet'; } var itemSprite = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); self.radius = itemSprite.width / 2; self.id = ''; self.collected = false; // Add pulsing animation to make quest items stand out // Only pulse if not collected self._pulseTween = null; function startPulse() { if (self._pulseTween) { return; } function pulseUp() { self._pulseTween = tween(itemSprite, { scaleX: 1.25, scaleY: 1.25, alpha: 1 }, { duration: 500, easing: tween.easeInOutQuad, onComplete: pulseDown }); } function pulseDown() { self._pulseTween = tween(itemSprite, { scaleX: 1.0, scaleY: 1.0, alpha: 0.7 }, { duration: 500, easing: tween.easeInOutQuad, onComplete: pulseUp }); } pulseUp(); } function stopPulse() { if (self._pulseTween && self._pulseTween.stop) { self._pulseTween.stop(); } self._pulseTween = null; itemSprite.scaleX = 1.0; itemSprite.scaleY = 1.0; itemSprite.alpha = 1.0; } self.update = function () { // If not collected, keep pulsing if (!self.collected && self.visible) { startPulse(); } else { stopPulse(); } }; return self; }); // NPC class var NPC = Container.expand(function () { var self = Container.call(this); var npcSprite = self.attachAsset('npc', { anchorX: 0.5, anchorY: 0.5 }); self.radius = npcSprite.width / 2; self.isSpecial = false; self.hasQuest = false; self.questGiven = false; self.questCompleted = false; self.questType = null; self.questTarget = null; self.questText = ''; self.id = ''; self.update = function () { // Prevent NPC from overlapping with buildings for (var i = 0; i < buildings.length; i++) { var b = buildings[i]; var dx = self.x - b.x; var dy = self.y - b.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.radius + b.radius - 10) { // Push NPC out of building var overlap = self.radius + b.radius - 10 - dist; if (dist === 0) { // Avoid division by zero, move randomly self.x += Math.random() > 0.5 ? overlap : -overlap; self.y += Math.random() > 0.5 ? overlap : -overlap; } else { self.x += dx / dist * overlap; self.y += dy / dist * overlap; } } } }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); var playerSprite = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 18; self.radius = playerSprite.width / 2; self.isInBuilding = false; self.targetX = self.x; self.targetY = self.y; self.moveTo = function (x, y) { self.targetX = x; self.targetY = y; }; self.update = function () { // Move towards targetX, targetY var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > self.speed) { // Calculate intended new position var nextX = self.x + self.speed * dx / dist; var nextY = self.y + self.speed * dy / dist; var blocked = false; if (!self.isInBuilding) { // Check collision with buildings only when outside for (var i = 0; i < buildings.length; i++) { var b = buildings[i]; // Use circle collision for blocking var ddx = nextX - b.x; var ddy = nextY - b.y; var dDist = Math.sqrt(ddx * ddx + ddy * ddy); if (dDist < self.radius + b.radius - 10) { // If building has an entry, check if next position is within entry area var allowEntry = false; if (b.hasEntry && b.entry) { // Calculate entry's world position var entryWorldX = b.x + b.entry.x; var entryWorldY = b.y + b.entry.y; // Allow a generous area for the door var entryRadius = b.entry.width / 2 + 30; var entryDist = Math.sqrt((nextX - entryWorldX) * (nextX - entryWorldX) + (nextY - entryWorldY) * (nextY - entryWorldY)); if (entryDist < entryRadius) { allowEntry = true; } } if (!allowEntry) { blocked = true; break; } } } } // Do NOT block player for NPCs! if (!blocked) { self.x = nextX; self.y = nextY; } // If blocked, do not move this frame } else { self.x = self.targetX; self.y = self.targetY; } }; return self; }); // Police Car class var PoliceCar = Container.expand(function () { var self = Container.call(this); var policeSprite = self.attachAsset('police', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 22; self.radius = policeSprite.width / 2; self.target = null; self.update = function () { if (!self.target) { return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.x += self.speed * dx / dist; self.y += self.speed * dy / dist; } }; return self; }); // Room class for building interiors var Room = Container.expand(function () { var self = Container.call(this); // Use the corridor asset for the room's visual appearance var roomSprite = self.attachAsset('Koridor', { anchorX: 0.5, anchorY: 0.5 }); roomSprite.width = 220; roomSprite.height = 320; self.radius = roomSprite.width / 2; // Optionally add a door self.addDoor = function (doorX, doorY) { var door = self.attachAsset('building_entry', { anchorX: 0.5, anchorY: 0.5, x: doorX, y: doorY }); door.width = 60; door.height = 60; return door; }; // Optionally add a decor item self.addDecor = function (assetId, decorX, decorY) { var decor = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5, x: decorX, y: decorY }); decor.width = 70; decor.height = 70; return decor; }; return self; }); // Special NPC class var SpecialNPC = Container.expand(function () { var self = Container.call(this); // Attach the special NPC asset and set tint directly var npcSpecialSprite = self.attachAsset('npc_special', { anchorX: 0.5, anchorY: 0.5 }); self.isSpecial = true; self.hasQuest = true; self.questGiven = false; self.questCompleted = false; self.questType = 'find_item'; self.questText = 'Find my lost wallet!'; // No need to set tint, as the asset is already colored // Add empty update method to avoid 'update is not a function' error self.update = function () {}; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222a36 }); /**** * Game Code ****/ // Star (wanted level) // Item // Buildings // Police // NPCs // Player // World boundaries var WORLD_W = 2048; var WORLD_H = 2732; // Player var player = new Player(); player.x = WORLD_W / 2; player.y = WORLD_H / 2; game.addChild(player); // Buildings var buildings = []; // NPCs var npcs = []; var specialNpcs = []; // Expanded NPC positions, spaced away from buildings var npcPositions = [{ x: 300, y: 300, dialog: ["Merhaba! Şehirde yeni misin?", "Buralar eskiden çok daha sakindi.", "Bana bir çiçek bulabilir misin? Çok mutlu olurum!"], quest: { type: "find_item", item: "flower", text: "Bir çiçek bul ve bana getir!" } }, { x: 1750, y: 350, dialog: ["Hey! Polislerden uzak dur.", "Biraz para kaybettim, bulursan getir lütfen.", "Şu binanın arkasında bir şey gördüm."], quest: { type: "find_item", item: "coin", text: "Kaybolan parayı bul ve getir!" } }, { x: 400, y: 2300, dialog: ["Bugün hava çok güzel.", "Beni bulduğun için teşekkürler!", "Bir kitap kaybettim, bulursan çok sevinirim."], quest: { type: "find_item", item: "book", text: "Kaybolan kitabı bul ve getir!" } }, { x: 1800, y: 2300, dialog: ["Şehirde çok fazla bina var.", "Birazdan markete gideceğim.", "Bir market poşeti kaybettim, bulursan getirir misin?"], quest: { type: "find_item", item: "bag", text: "Market poşetimi bul ve getir!" } }, // Ekstra NPC'ler { x: 1000, y: 800, dialog: ["Selam! Helikopteri gördün mü?", "Bazen yukarıdan izliyorlar.", "Bir anahtar kaybettim, bulursan bana getir."], quest: { type: "find_item", item: "key", text: "Kaybolan anahtarı bul ve getir!" } }, { x: 1500, y: 1200, dialog: ["Binaların arasında kaybolmak kolay.", "Bir harita bulursan bana getirir misin?", "Teşekkürler!"], quest: { type: "find_item", item: "map", text: "Bir harita bul ve getir!" } }]; // Add NPCs, keep them above roads visually (do not add to game yet) // Randomize NPC positions at every game start function getRandomNPCPos() { // Keep NPCs away from the very edges and from the top menu var margin = 180; var minX = margin, maxX = WORLD_W - margin; var minY = 200, maxY = WORLD_H - margin; return { x: Math.floor(Math.random() * (maxX - minX)) + minX, y: Math.floor(Math.random() * (maxY - minY)) + minY }; } for (var i = 0; i < npcPositions.length; i++) { var npc = new NPC(); var maxTries = 30; var pos; var insideBuilding = true; // Try to find a position not inside any building var tryCount = 0; while (insideBuilding && maxTries-- > 0) { pos = getRandomNPCPos(); insideBuilding = false; for (var b = 0; b < buildings.length; b++) { var build = buildings[b]; var dx = pos.x - build.x; var dy = pos.y - build.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < npc.radius + build.radius + 30) { insideBuilding = true; break; } } tryCount++; } // If after all tries, still inside a building, move to a default safe spot if (insideBuilding) { pos.x = 200 + 100 * i; pos.y = 200 + 100 * i; } npc.x = pos.x; npc.y = pos.y; npc.id = 'npc' + i; npc.dialog = npcPositions[i].dialog; npc.dialogIndex = 0; npc.hasQuest = true; npc.questGiven = false; npc.questCompleted = false; npc.questType = npcPositions[i].quest.type; npc.questTarget = npcPositions[i].quest.item; npc.questText = npcPositions[i].quest.text; npcs.push(npc); } // Add a special NPC with a quest (do not add to game yet) var specialNpc = new SpecialNPC(); specialNpc.x = 600; specialNpc.y = 1500; specialNpc.id = 'special1'; specialNpcs.push(specialNpc); // Buildings var buildings = []; // Draw roads between buildings, avoiding all entities (buildings, player, NPCs, items) and ensuring roads are visually under all entities var roads = []; function isRoadClear(x, y, width, height) { // Check if a rectangle (road) at (x, y, width, height) collides with any building, player, NPC, or item // All positions are center-based var margin = 18; // extra margin to avoid touching var rectLeft = x - width / 2 - margin; var rectRight = x + width / 2 + margin; var rectTop = y - height / 2 - margin; var rectBottom = y + height / 2 + margin; // Check buildings for (var i = 0; i < buildings.length; i++) { var b = buildings[i]; var bx = b.x, by = b.y, br = b.radius + margin; if (bx + br > rectLeft && bx - br < rectRight && by + br > rectTop && by - br < rectBottom) { return false; } } // Check player if (typeof player !== "undefined") { var bx = player.x, by = player.y, br = player.radius + margin; if (bx + br > rectLeft && bx - br < rectRight && by + br > rectTop && by - br < rectBottom) { return false; } } // Check NPCs for (var i = 0; i < npcs.length; i++) { var n = npcs[i]; var nx = n.x, ny = n.y, nr = n.radius + margin; if (nx + nr > rectLeft && nx - nr < rectRight && ny + nr > rectTop && ny - nr < rectBottom) { return false; } } // Check special NPCs for (var i = 0; i < specialNpcs.length; i++) { var n = specialNpcs[i]; var nx = n.x, ny = n.y, nr = n.radius + margin; if (nx + nr > rectLeft && nx - nr < rectRight && ny + nr > rectTop && ny - nr < rectBottom) { return false; } } // Check items if (typeof items !== "undefined") { for (var i = 0; i < items.length; i++) { var it = items[i]; var ix = it.x, iy = it.y, ir = it.radius + margin; if (ix + ir > rectLeft && ix - ir < rectRight && iy + ir > rectTop && iy - ir < rectBottom) { return false; } } } return true; } // Draw horizontal and vertical roads between buildings in the grid var roadWidth = 90; var roadColor = 0x888888; for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { var idx = row * gridCols + col; var b = buildingPositions[idx]; // Horizontal road to right neighbor if (col < gridCols - 1) { var b2 = buildingPositions[row * gridCols + (col + 1)]; var midX = (b.x + b2.x) / 2; var midY = (b.y + b2.y) / 2; var length = Math.abs(b2.x - b.x); if (isRoadClear(midX, midY, length, roadWidth)) { var road = LK.getAsset('en', { anchorX: 0.5, anchorY: 0.5, x: midX, y: midY }); road.width = length; road.height = roadWidth; road.tint = roadColor; roads.push(road); game.addChild(road); } } // Vertical road to bottom neighbor if (row < gridRows - 1) { var b2 = buildingPositions[(row + 1) * gridCols + col]; var midX = (b.x + b2.x) / 2; var midY = (b.y + b2.y) / 2; var length = Math.abs(b2.y - b.y); if (isRoadClear(midX, midY, roadWidth, length)) { var road = LK.getAsset('en', { anchorX: 0.5, anchorY: 0.5, x: midX, y: midY }); road.width = roadWidth; road.height = length; road.tint = roadColor; roads.push(road); game.addChild(road); } } } } // Add all buildings to game after roads, so they are above roads for (var i = 0; i < buildings.length; i++) { game.addChild(buildings[i]); } // Ensure items array is defined before use if (typeof items === "undefined") { var items = []; } // Add all items above buildings for (var i = 0; i < items.length; i++) { game.addChild(items[i]); } // Add all NPCs above roads (after roads are drawn, before player) for (var i = 0; i < npcs.length; i++) { game.addChild(npcs[i]); } for (var i = 0; i < specialNpcs.length; i++) { game.addChild(specialNpcs[i]); } // Add player above all game.addChild(player); // Grid layout for buildings: 4 columns x 4 rows, spaced out, not side by side, orderly // Make sure there is enough open space between buildings for the player to move between them var buildingPositions = []; var gridCols = 4; var gridRows = 4; var startX = 350; var startY = 400; // Increase spacing to create open areas between buildings var spacingX = 520; var spacingY = 520; for (var row = 0; row < gridRows; row++) { for (var col = 0; col < gridCols; col++) { // Stagger rows for more realism var offsetX = row % 2 === 0 ? 0 : spacingX / 2; buildingPositions.push({ x: startX + col * spacingX + offsetX, y: startY + row * spacingY }); } } for (var i = 0; i < buildingPositions.length; i++) { var b = new Building(); b.x = buildingPositions[i].x; b.y = buildingPositions[i].y; b.id = 'building' + i; b.addEntry(); buildings.push(b); game.addChild(b); } // Items (for quests) var items = []; // Helper: Ensure item is not touching any building function placeItemAwayFromBuildings(item, desiredX, desiredY) { var safe = false; var tryCount = 0; var maxTries = 30; var offsetStep = 40; var angle = 0; var radius = 0; item.x = desiredX; item.y = desiredY; while (!safe && tryCount < maxTries) { safe = true; for (var i = 0; i < buildings.length; i++) { var b = buildings[i]; var dx = item.x - b.x; var dy = item.y - b.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < item.radius + b.radius + 10) { safe = false; break; } } if (!safe) { // Spiral outwards to find a safe spot angle += Math.PI / 4; radius += offsetStep; item.x = desiredX + Math.cos(angle) * radius; item.y = desiredY + Math.sin(angle) * radius; } tryCount++; } } // Wallet for special NPC (placed on a main vertical road, between buildings, not touching) var questItem = new Item('wallet'); placeItemAwayFromBuildings(questItem, buildingPositions[2].x, buildingPositions[1].y + (buildingPositions[2 * 4 + 1].y - buildingPositions[1].y) / 2); questItem.id = 'wallet'; items.push(questItem); game.addChild(questItem); // Flower for NPC 0 (placed on a main horizontal road, between buildings) var flower = new Item('flower'); placeItemAwayFromBuildings(flower, buildingPositions[1].x + (buildingPositions[2].x - buildingPositions[1].x) / 2, buildingPositions[0].y); flower.id = 'flower'; items.push(flower); game.addChild(flower); // Coin for NPC 1 (placed on a main vertical road, between buildings) var coin = new Item('coin'); placeItemAwayFromBuildings(coin, buildingPositions[3].x, buildingPositions[1 * 4 + 3].y + (buildingPositions[2 * 4 + 3].y - buildingPositions[1 * 4 + 3].y) / 2); coin.id = 'coin'; items.push(coin); game.addChild(coin); // Book for NPC 2 (placed on a main horizontal road, between buildings) var book = new Item('book'); placeItemAwayFromBuildings(book, buildingPositions[0].x + (buildingPositions[1].x - buildingPositions[0].x) / 2, buildingPositions[3 * 4].y); book.id = 'book'; items.push(book); game.addChild(book); // Bag for NPC 3 (placed on a main vertical road, between buildings) var bag = new Item('bag'); placeItemAwayFromBuildings(bag, buildingPositions[2].x, buildingPositions[2 * 4 + 2].y + (buildingPositions[3 * 4 + 2].y - buildingPositions[2 * 4 + 2].y) / 2); bag.id = 'bag'; items.push(bag); game.addChild(bag); // Key for NPC 4 (placed at intersection, but offset from building center) var key = new Item('key'); placeItemAwayFromBuildings(key, buildingPositions[1].x + 60, buildingPositions[1 * 4 + 1].y - 60); key.id = 'key'; items.push(key); game.addChild(key); // Map for NPC 5 (placed at intersection, but offset from building center) var map = new Item('map'); placeItemAwayFromBuildings(map, buildingPositions[3].x - 60, buildingPositions[2 * 4 + 3].y + 60); map.id = 'map'; items.push(map); game.addChild(map); // Police var policeCars = []; var helicopters = []; // Wanted system var wantedLevel = 0; // 0-5 var wantedDecayTimer = 0; var wantedMax = 5; // GUI: Wanted stars var starIcons = []; for (var i = 0; i < wantedMax; i++) { var star = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5 }); star.x = 120 + i * 70; star.y = 80; star.visible = false; LK.gui.top.addChild(star); starIcons.push(star); } // GUI: Quest text var questText = new Text2('', { size: 70, fill: "#fff" }); // Anchor'ı üst ortada olacak şekilde ayarla questText.anchor.set(0.5, 0); // Ekranın üst orta kısmına yerleştir (100px aşağıda, üstteki menüyle çakışmasın) questText.x = LK.gui.width / 2; questText.y = 120; LK.gui.top.addChild(questText); // GUI: Mission complete text var missionText = new Text2('', { size: 90, fill: 0x27AE60 }); missionText.anchor.set(0.5, 0); missionText.x = LK.gui.width / 2; missionText.y = 300; LK.gui.top.addChild(missionText); // GUI: Police alert text var policeText = new Text2('', { size: 80, fill: 0xE74C3C }); policeText.anchor.set(0.5, 0); policeText.x = LK.gui.width / 2; policeText.y = 400; LK.gui.top.addChild(policeText); // Dragging/movement var dragging = false; // Helper: Check circle collision function circlesCollide(a, b) { var dx = a.x - b.x; var dy = a.y - b.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < a.radius + b.radius - 10; } // Helper: Show wanted stars function updateWantedStars() { for (var i = 0; i < wantedMax; i++) { starIcons[i].visible = i < wantedLevel; } } // Helper: Set quest text function setQuestText(txt) { questText.setText(txt); } // Helper: Set mission text function setMissionText(txt) { missionText.setText(txt); if (txt) { tween(missionText, { alpha: 1 }, { duration: 0 }); tween(missionText, { alpha: 0 }, { duration: 1800, easing: tween.linear }); } } // Helper: Set police alert function setPoliceText(txt) { policeText.setText(txt); if (txt) { tween(policeText, { alpha: 1 }, { duration: 0 }); tween(policeText, { alpha: 0 }, { duration: 1800, easing: tween.linear }); } } // Helper: Enter building var buildingDoor = null; var currentBuilding = null; function tryEnterBuilding() { for (var i = 0; i < buildings.length; i++) { var b = buildings[i]; // Enter if near entry, but do NOT block movement inside building if (circlesCollide(player, b)) { if (b.hasEntry && Math.abs(player.x - (b.x + b.entry.x)) < 80 && Math.abs(player.y - (b.y + b.entry.y)) < 80) { player.isInBuilding = true; currentBuilding = b; setQuestText('Binanın içindesin. Kapıya tıklayarak çıkabilirsin.'); // Hide all city elements (buildings, npcs, items, police, etc) for (var i = 0; i < buildings.length; i++) { buildings[i].visible = false; } for (var i = 0; i < npcs.length; i++) { npcs[i].visible = false; } for (var i = 0; i < specialNpcs.length; i++) { specialNpcs[i].visible = false; } for (var i = 0; i < items.length; i++) { items[i].visible = false; } for (var i = 0; i < policeCars.length; i++) { policeCars[i].visible = false; } for (var i = 0; i < helicopters.length; i++) { helicopters[i].visible = false; } // Show only the building interior background (add corridor entity) if (typeof buildingInteriorBg !== "undefined" && buildingInteriorBg.parent) { buildingInteriorBg.parent.removeChild(buildingInteriorBg); } buildingInteriorBg = new Container(); buildingInteriorBg.visible = true; game.addChild(buildingInteriorBg); // Add the 'corridor' entity to the building interior if (typeof buildingCorridor !== "undefined" && buildingCorridor.parent) { buildingCorridor.parent.removeChild(buildingCorridor); } buildingCorridor = new Corridor(); buildingCorridor.x = WORLD_W / 2; buildingCorridor.y = WORLD_H / 2; buildingInteriorBg.addChild(buildingCorridor); // Add rooms (oda) to the building interior, placed at the ends of the corridor (not inside it) if (typeof buildingRooms !== "undefined") { for (var i = 0; i < buildingRooms.length; i++) { if (buildingRooms[i].parent) { buildingRooms[i].parent.removeChild(buildingRooms[i]); } } } buildingRooms = []; // Place rooms at the ends of the corridor var corridorCenterX = WORLD_W / 2; var corridorCenterY = WORLD_H / 2; var corridorWidth = 1100; var corridorHeight = 340; var roomOffsetX = corridorWidth / 2 + 140; // 140 is half room width + margin var roomOffsetY = 0; // Left end room var leftRoom = new Room(); leftRoom.x = corridorCenterX - roomOffsetX; leftRoom.y = corridorCenterY; buildingInteriorBg.addChild(leftRoom); buildingRooms.push(leftRoom); // Add a coin at the end of the left room var leftCoin = new Item('coin'); leftCoin.x = leftRoom.x; leftCoin.y = leftRoom.y + leftRoom.height / 2 - 60; leftCoin.id = 'room_coin_left_' + (currentBuilding ? currentBuilding.id : ''); leftCoin.collected = false; leftCoin.visible = true; buildingInteriorBg.addChild(leftCoin); // Right end room var rightRoom = new Room(); rightRoom.x = corridorCenterX + roomOffsetX; rightRoom.y = corridorCenterY; buildingInteriorBg.addChild(rightRoom); buildingRooms.push(rightRoom); // Add a coin at the end of the right room var rightCoin = new Item('coin'); rightCoin.x = rightRoom.x; rightCoin.y = rightRoom.y + rightRoom.height / 2 - 60; rightCoin.id = 'room_coin_right_' + (currentBuilding ? currentBuilding.id : ''); rightCoin.collected = false; rightCoin.visible = true; buildingInteriorBg.addChild(rightCoin); // Move player to the center of the building interior player.x = WORLD_W / 2; player.y = WORLD_H / 2 + 60; // Show player inside the building interior if (player.parent) { player.parent.removeChild(player); } buildingInteriorBg.addChild(player); // Show a door inside the building to exit (centered at top) if (buildingDoor && buildingDoor.parent) { buildingDoor.parent.removeChild(buildingDoor); } buildingDoor = LK.getAsset('building_entry', { anchorX: 0.5, anchorY: 0.5, x: WORLD_W / 2, y: WORLD_H / 2 - 320 }); buildingDoor.interactive = true; buildingDoor.visible = true; game.addChild(buildingDoor); return false; } } } return false; } // Helper: Exit building function tryExitBuilding(x, y) { if (player.isInBuilding && buildingDoor) { // Check if tap is on the door var dx = x - buildingDoor.x; var dy = y - buildingDoor.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < buildingDoor.width / 2 + 30) { // Hide building interior background if (typeof buildingInteriorBg !== "undefined" && buildingInteriorBg.parent) { buildingInteriorBg.parent.removeChild(buildingInteriorBg); buildingInteriorBg.visible = false; } // Restore city elements for (var i = 0; i < buildings.length; i++) { buildings[i].visible = true; } for (var i = 0; i < npcs.length; i++) { npcs[i].visible = true; } for (var i = 0; i < specialNpcs.length; i++) { specialNpcs[i].visible = true; } for (var i = 0; i < items.length; i++) { items[i].visible = true; } for (var i = 0; i < policeCars.length; i++) { policeCars[i].visible = true; } for (var i = 0; i < helicopters.length; i++) { helicopters[i].visible = true; } // Move player outside the building, near the entry if (currentBuilding && currentBuilding.hasEntry && currentBuilding.entry) { player.x = currentBuilding.x + currentBuilding.entry.x; player.y = currentBuilding.y + currentBuilding.entry.y + 100; } else { player.x = player.x + 200; player.y = player.y + 200; } // Restore player to city scene if (player.parent) { player.parent.removeChild(player); } game.addChild(player); player.isInBuilding = false; setQuestText(''); // Remove door if (buildingDoor.parent) { buildingDoor.parent.removeChild(buildingDoor); } buildingDoor = null; currentBuilding = null; } } } // Helper: Interact with NPCs function tryInteractNPC() { var interacted = false; for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; if (circlesCollide(player, npc)) { // If NPC has a quest if (npc.hasQuest) { if (!npc.questGiven && !npc.questCompleted) { // Show quest text and mark quest as given setQuestText(npc.questText); npc.questGiven = true; npc.dialogIndex = 0; interacted = true; } else if (npc.questGiven && !npc.questCompleted) { // Check if player has the quest item (only the correct item is accepted) var found = false; for (var j = 0; j < items.length; j++) { if (items[j].id === npc.questTarget && items[j].collected) { npc.questCompleted = true; setQuestText('Teşekkürler! Görevi tamamladın.'); setMissionText('Görev Tamamlandı!'); LK.setScore(LK.getScore() + 1); interacted = true; found = true; break; } } // If not found, remind quest if (!npc.questCompleted) { setQuestText('Görev: ' + npc.questText + " (Sadece doğru eşyayı kabul eder)"); interacted = true; } } else if (npc.questCompleted) { // After quest is completed, show dialog or thanks if (!npc._thankedTwice) { setQuestText('Tekrar teşekkürler!'); if (typeof npc._thanksCount === "undefined") { npc._thanksCount = 0; } npc._thanksCount++; if (npc._thanksCount >= 2) { npc._thankedTwice = true; // Clear quest text after a short delay so player can move freely LK.setTimeout(function () { setQuestText(''); }, 1200); } interacted = true; } else { // Already thanked twice, allow player to move freely setQuestText(''); // Do not block movement } } } // If no quest, cycle through dialog if (!npc.hasQuest && npc.dialog && npc.dialog.length > 0) { setQuestText(npc.dialog[npc.dialogIndex]); npc.dialogIndex = (npc.dialogIndex + 1) % npc.dialog.length; interacted = true; } else if (!npc.hasQuest) { setQuestText('Merhaba!'); interacted = true; } } } for (var i = 0; i < specialNpcs.length; i++) { var npc = specialNpcs[i]; if (circlesCollide(player, npc)) { if (!npc.questGiven && !npc.questCompleted) { setQuestText(npc.questText); npc.questGiven = true; interacted = true; } else if (npc.questGiven && !npc.questCompleted) { // Check if player has wallet (only wallet is accepted) var found = false; for (var j = 0; j < items.length; j++) { if (items[j].id === 'wallet' && items[j].collected) { npc.questCompleted = true; setQuestText('Thank you for finding my wallet!'); setMissionText('Mission Complete!'); LK.setScore(LK.getScore() + 1); interacted = true; found = true; break; } } if (!npc.questCompleted) { setQuestText('Did you find my wallet? (Only the wallet is accepted)'); interacted = true; } } else if (npc.questCompleted) { setQuestText('Thanks again!'); interacted = true; } } } // Always return false so player movement is never blocked by NPC interaction return false; } // Helper: Interact with items function tryCollectItem() { for (var i = 0; i < items.length; i++) { var item = items[i]; if (!item.collected && circlesCollide(player, item)) { item.collected = true; item.visible = false; // Show a unique message for each item var foundMsg = ''; if (item.id === 'wallet') { foundMsg = 'You found a wallet!'; } else if (item.id === 'flower') { foundMsg = 'Bir çiçek buldun!'; } else if (item.id === 'coin') { foundMsg = 'Bir para buldun!'; } else if (item.id === 'book') { foundMsg = 'Bir kitap buldun!'; } else if (item.id === 'bag') { foundMsg = 'Bir poşet buldun!'; } else if (item.id === 'key') { foundMsg = 'Bir anahtar buldun!'; } else if (item.id === 'map') { foundMsg = 'Bir harita buldun!'; } else { foundMsg = 'Bir eşya buldun!'; } setMissionText(foundMsg); return true; } } return false; } // Helper: Commit crime (damage property) function tryDamageProperty(x, y) { // If player is near a building, "damage" it for (var i = 0; i < buildings.length; i++) { var b = buildings[i]; var dx = x - (b.x + 150); var dy = y - (b.y + 150); if (Math.abs(dx) < 150 && Math.abs(dy) < 150) { // Crime committed! wantedLevel = Math.min(wantedLevel + 1, wantedMax); updateWantedStars(); setPoliceText('Police alerted!'); wantedDecayTimer = 0; LK.effects.flashObject(b, 0xff0000, 500); return true; } } return false; } // Police AI: spawn police based on wanted level function updatePolice() { // Remove police if wantedLevel is 0 if (wantedLevel === 0) { for (var i = 0; i < policeCars.length; i++) { policeCars[i].destroy(); } policeCars = []; for (var i = 0; i < helicopters.length; i++) { helicopters[i].destroy(); } helicopters = []; return; } // Spawn police cars while (policeCars.length < wantedLevel && policeCars.length < 3) { var pc = new PoliceCar(); // Spawn at random edge var edge = Math.floor(Math.random() * 4); if (edge === 0) { pc.x = 0; pc.y = Math.random() * WORLD_H; } if (edge === 1) { pc.x = WORLD_W; pc.y = Math.random() * WORLD_H; } if (edge === 2) { pc.x = Math.random() * WORLD_W; pc.y = 0; } if (edge === 3) { pc.x = Math.random() * WORLD_W; pc.y = WORLD_H; } pc.target = player; policeCars.push(pc); game.addChild(pc); } // Spawn helicopter at wanted level 4+ if (wantedLevel >= 4 && helicopters.length < 1) { var heli = new Helicopter(); heli.x = Math.random() * WORLD_W; heli.y = 0; heli.target = player; helicopters.push(heli); game.addChild(heli); } } // Police AI: check for player caught function checkPoliceCatch() { for (var i = 0; i < policeCars.length; i++) { if (circlesCollide(player, policeCars[i])) { LK.effects.flashScreen(0xff0000, 1000); setPoliceText('You were caught!'); LK.showGameOver(); return true; } } for (var i = 0; i < helicopters.length; i++) { if (Math.abs(player.x - helicopters[i].x) < 80 && Math.abs(player.y - helicopters[i].y) < 80) { LK.effects.flashScreen(0xff0000, 1000); setPoliceText('Helicopter caught you!'); LK.showGameOver(); return true; } } return false; } // Decay wanted level over time function decayWantedLevel() { if (wantedLevel > 0) { wantedDecayTimer++; if (wantedDecayTimer > 300) { // ~5 seconds wantedLevel--; updateWantedStars(); wantedDecayTimer = 0; } } } // Game move handler (tap to move, tap on NPC/building to interact) function handleMove(x, y, obj) { // Convert to game coordinates if (dragging) { player.moveTo(x, y); } } // Tap down: move or interact game.down = function (x, y, obj) { // If in building, check if tap is on the door to exit if (player.isInBuilding) { tryExitBuilding(x, y); return; } // Try interact with NPC if (tryInteractNPC()) { return; } // Try collect item if (tryCollectItem()) { return; } // Try enter building if (tryEnterBuilding()) { return; } // Otherwise, move player.moveTo(x, y); dragging = true; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragging = false; }; game.move = handleMove; // Score display (missions completed) var scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); scoreTxt.x = LK.gui.width / 2; scoreTxt.y = 40; // Main game update game.update = function () { if (!player.isInBuilding) { player.update(); // Update NPCs for (var i = 0; i < npcs.length; i++) { npcs[i].update(); } for (var i = 0; i < specialNpcs.length; i++) { specialNpcs[i].update(); } // Update police for (var i = 0; i < policeCars.length; i++) { policeCars[i].update(); } for (var i = 0; i < helicopters.length; i++) { helicopters[i].update(); } // Police AI updatePolice(); checkPoliceCatch(); decayWantedLevel(); } else { // Allow player to move inside building (no city collision logic) player.update(); } // Update score scoreTxt.setText(LK.getScore()); // Clamp player to world player.x = Math.max(player.radius, Math.min(WORLD_W - player.radius, player.x)); player.y = Math.max(player.radius, Math.min(WORLD_H - player.radius, player.y)); // Ortadaki yazıların her zaman ekranın ortasında kalmasını sağla if (typeof LK !== "undefined" && LK.gui && typeof LK.gui.width === "number" && LK.gui.width > 0) { questText.x = LK.gui.width / 2; missionText.x = LK.gui.width / 2; policeText.x = LK.gui.width / 2; scoreTxt.x = LK.gui.width / 2; } }; // Initial wanted stars updateWantedStars(); setQuestText('Explore the city! Tap on people or buildings.'); setMissionText(''); setPoliceText(''); ; // Always play 'a' sesi at 30% volume, looping LK.playMusic('a', { volume: 0.3 });
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Building class
var Building = Container.expand(function () {
var self = Container.call(this);
var buildingSprite = self.attachAsset('building', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = buildingSprite.width / 2;
self.entry = null;
self.hasEntry = false;
self.entryX = 0;
self.entryY = 0;
self.id = '';
self.addEntry = function () {
self.hasEntry = true;
self.entry = self.attachAsset('building_entry', {
anchorX: 0.5,
anchorY: 0.5,
x: buildingSprite.width / 2 - 30,
y: buildingSprite.height / 2 - 30
});
self.entryX = self.x + buildingSprite.width / 2 - 30;
self.entryY = self.y + buildingSprite.height / 2 - 30;
};
return self;
});
// Corridor class for building interiors
var Corridor = Container.expand(function () {
var self = Container.call(this);
// Use the 'en' asset for the corridor's visual appearance
var corridorSprite = self.attachAsset('en', {
anchorX: 0.5,
anchorY: 0.5
});
corridorSprite.width = 1100;
corridorSprite.height = 340;
self.radius = corridorSprite.width / 2;
return self;
});
// Helicopter class
var Helicopter = Container.expand(function () {
var self = Container.call(this);
var heliSprite = self.attachAsset('helicopter', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 28;
self.radius = heliSprite.width / 2;
self.target = null;
self.update = function () {
if (!self.target) {
return;
}
// Hover above target
var dx = self.target.x - self.x;
var dy = self.target.y - 300 - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += self.speed * dx / dist;
self.y += self.speed * dy / dist;
}
};
return self;
});
// Item class
var Item = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'item';
var assetId = 'item';
if (self.type === 'flower') {
assetId = 'item_flower';
} else if (self.type === 'coin') {
assetId = 'item_coin';
} else if (self.type === 'book') {
assetId = 'item_book';
} else if (self.type === 'bag') {
assetId = 'item_bag';
} else if (self.type === 'key') {
assetId = 'item_key';
} else if (self.type === 'map') {
assetId = 'item_map';
} else if (self.type === 'wallet') {
assetId = 'item_wallet';
}
var itemSprite = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = itemSprite.width / 2;
self.id = '';
self.collected = false;
// Add pulsing animation to make quest items stand out
// Only pulse if not collected
self._pulseTween = null;
function startPulse() {
if (self._pulseTween) {
return;
}
function pulseUp() {
self._pulseTween = tween(itemSprite, {
scaleX: 1.25,
scaleY: 1.25,
alpha: 1
}, {
duration: 500,
easing: tween.easeInOutQuad,
onComplete: pulseDown
});
}
function pulseDown() {
self._pulseTween = tween(itemSprite, {
scaleX: 1.0,
scaleY: 1.0,
alpha: 0.7
}, {
duration: 500,
easing: tween.easeInOutQuad,
onComplete: pulseUp
});
}
pulseUp();
}
function stopPulse() {
if (self._pulseTween && self._pulseTween.stop) {
self._pulseTween.stop();
}
self._pulseTween = null;
itemSprite.scaleX = 1.0;
itemSprite.scaleY = 1.0;
itemSprite.alpha = 1.0;
}
self.update = function () {
// If not collected, keep pulsing
if (!self.collected && self.visible) {
startPulse();
} else {
stopPulse();
}
};
return self;
});
// NPC class
var NPC = Container.expand(function () {
var self = Container.call(this);
var npcSprite = self.attachAsset('npc', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = npcSprite.width / 2;
self.isSpecial = false;
self.hasQuest = false;
self.questGiven = false;
self.questCompleted = false;
self.questType = null;
self.questTarget = null;
self.questText = '';
self.id = '';
self.update = function () {
// Prevent NPC from overlapping with buildings
for (var i = 0; i < buildings.length; i++) {
var b = buildings[i];
var dx = self.x - b.x;
var dy = self.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.radius + b.radius - 10) {
// Push NPC out of building
var overlap = self.radius + b.radius - 10 - dist;
if (dist === 0) {
// Avoid division by zero, move randomly
self.x += Math.random() > 0.5 ? overlap : -overlap;
self.y += Math.random() > 0.5 ? overlap : -overlap;
} else {
self.x += dx / dist * overlap;
self.y += dy / dist * overlap;
}
}
}
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var playerSprite = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 18;
self.radius = playerSprite.width / 2;
self.isInBuilding = false;
self.targetX = self.x;
self.targetY = self.y;
self.moveTo = function (x, y) {
self.targetX = x;
self.targetY = y;
};
self.update = function () {
// Move towards targetX, targetY
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > self.speed) {
// Calculate intended new position
var nextX = self.x + self.speed * dx / dist;
var nextY = self.y + self.speed * dy / dist;
var blocked = false;
if (!self.isInBuilding) {
// Check collision with buildings only when outside
for (var i = 0; i < buildings.length; i++) {
var b = buildings[i];
// Use circle collision for blocking
var ddx = nextX - b.x;
var ddy = nextY - b.y;
var dDist = Math.sqrt(ddx * ddx + ddy * ddy);
if (dDist < self.radius + b.radius - 10) {
// If building has an entry, check if next position is within entry area
var allowEntry = false;
if (b.hasEntry && b.entry) {
// Calculate entry's world position
var entryWorldX = b.x + b.entry.x;
var entryWorldY = b.y + b.entry.y;
// Allow a generous area for the door
var entryRadius = b.entry.width / 2 + 30;
var entryDist = Math.sqrt((nextX - entryWorldX) * (nextX - entryWorldX) + (nextY - entryWorldY) * (nextY - entryWorldY));
if (entryDist < entryRadius) {
allowEntry = true;
}
}
if (!allowEntry) {
blocked = true;
break;
}
}
}
}
// Do NOT block player for NPCs!
if (!blocked) {
self.x = nextX;
self.y = nextY;
}
// If blocked, do not move this frame
} else {
self.x = self.targetX;
self.y = self.targetY;
}
};
return self;
});
// Police Car class
var PoliceCar = Container.expand(function () {
var self = Container.call(this);
var policeSprite = self.attachAsset('police', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 22;
self.radius = policeSprite.width / 2;
self.target = null;
self.update = function () {
if (!self.target) {
return;
}
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.x += self.speed * dx / dist;
self.y += self.speed * dy / dist;
}
};
return self;
});
// Room class for building interiors
var Room = Container.expand(function () {
var self = Container.call(this);
// Use the corridor asset for the room's visual appearance
var roomSprite = self.attachAsset('Koridor', {
anchorX: 0.5,
anchorY: 0.5
});
roomSprite.width = 220;
roomSprite.height = 320;
self.radius = roomSprite.width / 2;
// Optionally add a door
self.addDoor = function (doorX, doorY) {
var door = self.attachAsset('building_entry', {
anchorX: 0.5,
anchorY: 0.5,
x: doorX,
y: doorY
});
door.width = 60;
door.height = 60;
return door;
};
// Optionally add a decor item
self.addDecor = function (assetId, decorX, decorY) {
var decor = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5,
x: decorX,
y: decorY
});
decor.width = 70;
decor.height = 70;
return decor;
};
return self;
});
// Special NPC class
var SpecialNPC = Container.expand(function () {
var self = Container.call(this);
// Attach the special NPC asset and set tint directly
var npcSpecialSprite = self.attachAsset('npc_special', {
anchorX: 0.5,
anchorY: 0.5
});
self.isSpecial = true;
self.hasQuest = true;
self.questGiven = false;
self.questCompleted = false;
self.questType = 'find_item';
self.questText = 'Find my lost wallet!';
// No need to set tint, as the asset is already colored
// Add empty update method to avoid 'update is not a function' error
self.update = function () {};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222a36
});
/****
* Game Code
****/
// Star (wanted level)
// Item
// Buildings
// Police
// NPCs
// Player
// World boundaries
var WORLD_W = 2048;
var WORLD_H = 2732;
// Player
var player = new Player();
player.x = WORLD_W / 2;
player.y = WORLD_H / 2;
game.addChild(player);
// Buildings
var buildings = [];
// NPCs
var npcs = [];
var specialNpcs = [];
// Expanded NPC positions, spaced away from buildings
var npcPositions = [{
x: 300,
y: 300,
dialog: ["Merhaba! Şehirde yeni misin?", "Buralar eskiden çok daha sakindi.", "Bana bir çiçek bulabilir misin? Çok mutlu olurum!"],
quest: {
type: "find_item",
item: "flower",
text: "Bir çiçek bul ve bana getir!"
}
}, {
x: 1750,
y: 350,
dialog: ["Hey! Polislerden uzak dur.", "Biraz para kaybettim, bulursan getir lütfen.", "Şu binanın arkasında bir şey gördüm."],
quest: {
type: "find_item",
item: "coin",
text: "Kaybolan parayı bul ve getir!"
}
}, {
x: 400,
y: 2300,
dialog: ["Bugün hava çok güzel.", "Beni bulduğun için teşekkürler!", "Bir kitap kaybettim, bulursan çok sevinirim."],
quest: {
type: "find_item",
item: "book",
text: "Kaybolan kitabı bul ve getir!"
}
}, {
x: 1800,
y: 2300,
dialog: ["Şehirde çok fazla bina var.", "Birazdan markete gideceğim.", "Bir market poşeti kaybettim, bulursan getirir misin?"],
quest: {
type: "find_item",
item: "bag",
text: "Market poşetimi bul ve getir!"
}
},
// Ekstra NPC'ler
{
x: 1000,
y: 800,
dialog: ["Selam! Helikopteri gördün mü?", "Bazen yukarıdan izliyorlar.", "Bir anahtar kaybettim, bulursan bana getir."],
quest: {
type: "find_item",
item: "key",
text: "Kaybolan anahtarı bul ve getir!"
}
}, {
x: 1500,
y: 1200,
dialog: ["Binaların arasında kaybolmak kolay.", "Bir harita bulursan bana getirir misin?", "Teşekkürler!"],
quest: {
type: "find_item",
item: "map",
text: "Bir harita bul ve getir!"
}
}];
// Add NPCs, keep them above roads visually (do not add to game yet)
// Randomize NPC positions at every game start
function getRandomNPCPos() {
// Keep NPCs away from the very edges and from the top menu
var margin = 180;
var minX = margin,
maxX = WORLD_W - margin;
var minY = 200,
maxY = WORLD_H - margin;
return {
x: Math.floor(Math.random() * (maxX - minX)) + minX,
y: Math.floor(Math.random() * (maxY - minY)) + minY
};
}
for (var i = 0; i < npcPositions.length; i++) {
var npc = new NPC();
var maxTries = 30;
var pos;
var insideBuilding = true;
// Try to find a position not inside any building
var tryCount = 0;
while (insideBuilding && maxTries-- > 0) {
pos = getRandomNPCPos();
insideBuilding = false;
for (var b = 0; b < buildings.length; b++) {
var build = buildings[b];
var dx = pos.x - build.x;
var dy = pos.y - build.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < npc.radius + build.radius + 30) {
insideBuilding = true;
break;
}
}
tryCount++;
}
// If after all tries, still inside a building, move to a default safe spot
if (insideBuilding) {
pos.x = 200 + 100 * i;
pos.y = 200 + 100 * i;
}
npc.x = pos.x;
npc.y = pos.y;
npc.id = 'npc' + i;
npc.dialog = npcPositions[i].dialog;
npc.dialogIndex = 0;
npc.hasQuest = true;
npc.questGiven = false;
npc.questCompleted = false;
npc.questType = npcPositions[i].quest.type;
npc.questTarget = npcPositions[i].quest.item;
npc.questText = npcPositions[i].quest.text;
npcs.push(npc);
}
// Add a special NPC with a quest (do not add to game yet)
var specialNpc = new SpecialNPC();
specialNpc.x = 600;
specialNpc.y = 1500;
specialNpc.id = 'special1';
specialNpcs.push(specialNpc);
// Buildings
var buildings = [];
// Draw roads between buildings, avoiding all entities (buildings, player, NPCs, items) and ensuring roads are visually under all entities
var roads = [];
function isRoadClear(x, y, width, height) {
// Check if a rectangle (road) at (x, y, width, height) collides with any building, player, NPC, or item
// All positions are center-based
var margin = 18; // extra margin to avoid touching
var rectLeft = x - width / 2 - margin;
var rectRight = x + width / 2 + margin;
var rectTop = y - height / 2 - margin;
var rectBottom = y + height / 2 + margin;
// Check buildings
for (var i = 0; i < buildings.length; i++) {
var b = buildings[i];
var bx = b.x,
by = b.y,
br = b.radius + margin;
if (bx + br > rectLeft && bx - br < rectRight && by + br > rectTop && by - br < rectBottom) {
return false;
}
}
// Check player
if (typeof player !== "undefined") {
var bx = player.x,
by = player.y,
br = player.radius + margin;
if (bx + br > rectLeft && bx - br < rectRight && by + br > rectTop && by - br < rectBottom) {
return false;
}
}
// Check NPCs
for (var i = 0; i < npcs.length; i++) {
var n = npcs[i];
var nx = n.x,
ny = n.y,
nr = n.radius + margin;
if (nx + nr > rectLeft && nx - nr < rectRight && ny + nr > rectTop && ny - nr < rectBottom) {
return false;
}
}
// Check special NPCs
for (var i = 0; i < specialNpcs.length; i++) {
var n = specialNpcs[i];
var nx = n.x,
ny = n.y,
nr = n.radius + margin;
if (nx + nr > rectLeft && nx - nr < rectRight && ny + nr > rectTop && ny - nr < rectBottom) {
return false;
}
}
// Check items
if (typeof items !== "undefined") {
for (var i = 0; i < items.length; i++) {
var it = items[i];
var ix = it.x,
iy = it.y,
ir = it.radius + margin;
if (ix + ir > rectLeft && ix - ir < rectRight && iy + ir > rectTop && iy - ir < rectBottom) {
return false;
}
}
}
return true;
}
// Draw horizontal and vertical roads between buildings in the grid
var roadWidth = 90;
var roadColor = 0x888888;
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var idx = row * gridCols + col;
var b = buildingPositions[idx];
// Horizontal road to right neighbor
if (col < gridCols - 1) {
var b2 = buildingPositions[row * gridCols + (col + 1)];
var midX = (b.x + b2.x) / 2;
var midY = (b.y + b2.y) / 2;
var length = Math.abs(b2.x - b.x);
if (isRoadClear(midX, midY, length, roadWidth)) {
var road = LK.getAsset('en', {
anchorX: 0.5,
anchorY: 0.5,
x: midX,
y: midY
});
road.width = length;
road.height = roadWidth;
road.tint = roadColor;
roads.push(road);
game.addChild(road);
}
}
// Vertical road to bottom neighbor
if (row < gridRows - 1) {
var b2 = buildingPositions[(row + 1) * gridCols + col];
var midX = (b.x + b2.x) / 2;
var midY = (b.y + b2.y) / 2;
var length = Math.abs(b2.y - b.y);
if (isRoadClear(midX, midY, roadWidth, length)) {
var road = LK.getAsset('en', {
anchorX: 0.5,
anchorY: 0.5,
x: midX,
y: midY
});
road.width = roadWidth;
road.height = length;
road.tint = roadColor;
roads.push(road);
game.addChild(road);
}
}
}
}
// Add all buildings to game after roads, so they are above roads
for (var i = 0; i < buildings.length; i++) {
game.addChild(buildings[i]);
}
// Ensure items array is defined before use
if (typeof items === "undefined") {
var items = [];
}
// Add all items above buildings
for (var i = 0; i < items.length; i++) {
game.addChild(items[i]);
}
// Add all NPCs above roads (after roads are drawn, before player)
for (var i = 0; i < npcs.length; i++) {
game.addChild(npcs[i]);
}
for (var i = 0; i < specialNpcs.length; i++) {
game.addChild(specialNpcs[i]);
}
// Add player above all
game.addChild(player);
// Grid layout for buildings: 4 columns x 4 rows, spaced out, not side by side, orderly
// Make sure there is enough open space between buildings for the player to move between them
var buildingPositions = [];
var gridCols = 4;
var gridRows = 4;
var startX = 350;
var startY = 400;
// Increase spacing to create open areas between buildings
var spacingX = 520;
var spacingY = 520;
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
// Stagger rows for more realism
var offsetX = row % 2 === 0 ? 0 : spacingX / 2;
buildingPositions.push({
x: startX + col * spacingX + offsetX,
y: startY + row * spacingY
});
}
}
for (var i = 0; i < buildingPositions.length; i++) {
var b = new Building();
b.x = buildingPositions[i].x;
b.y = buildingPositions[i].y;
b.id = 'building' + i;
b.addEntry();
buildings.push(b);
game.addChild(b);
}
// Items (for quests)
var items = [];
// Helper: Ensure item is not touching any building
function placeItemAwayFromBuildings(item, desiredX, desiredY) {
var safe = false;
var tryCount = 0;
var maxTries = 30;
var offsetStep = 40;
var angle = 0;
var radius = 0;
item.x = desiredX;
item.y = desiredY;
while (!safe && tryCount < maxTries) {
safe = true;
for (var i = 0; i < buildings.length; i++) {
var b = buildings[i];
var dx = item.x - b.x;
var dy = item.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < item.radius + b.radius + 10) {
safe = false;
break;
}
}
if (!safe) {
// Spiral outwards to find a safe spot
angle += Math.PI / 4;
radius += offsetStep;
item.x = desiredX + Math.cos(angle) * radius;
item.y = desiredY + Math.sin(angle) * radius;
}
tryCount++;
}
}
// Wallet for special NPC (placed on a main vertical road, between buildings, not touching)
var questItem = new Item('wallet');
placeItemAwayFromBuildings(questItem, buildingPositions[2].x, buildingPositions[1].y + (buildingPositions[2 * 4 + 1].y - buildingPositions[1].y) / 2);
questItem.id = 'wallet';
items.push(questItem);
game.addChild(questItem);
// Flower for NPC 0 (placed on a main horizontal road, between buildings)
var flower = new Item('flower');
placeItemAwayFromBuildings(flower, buildingPositions[1].x + (buildingPositions[2].x - buildingPositions[1].x) / 2, buildingPositions[0].y);
flower.id = 'flower';
items.push(flower);
game.addChild(flower);
// Coin for NPC 1 (placed on a main vertical road, between buildings)
var coin = new Item('coin');
placeItemAwayFromBuildings(coin, buildingPositions[3].x, buildingPositions[1 * 4 + 3].y + (buildingPositions[2 * 4 + 3].y - buildingPositions[1 * 4 + 3].y) / 2);
coin.id = 'coin';
items.push(coin);
game.addChild(coin);
// Book for NPC 2 (placed on a main horizontal road, between buildings)
var book = new Item('book');
placeItemAwayFromBuildings(book, buildingPositions[0].x + (buildingPositions[1].x - buildingPositions[0].x) / 2, buildingPositions[3 * 4].y);
book.id = 'book';
items.push(book);
game.addChild(book);
// Bag for NPC 3 (placed on a main vertical road, between buildings)
var bag = new Item('bag');
placeItemAwayFromBuildings(bag, buildingPositions[2].x, buildingPositions[2 * 4 + 2].y + (buildingPositions[3 * 4 + 2].y - buildingPositions[2 * 4 + 2].y) / 2);
bag.id = 'bag';
items.push(bag);
game.addChild(bag);
// Key for NPC 4 (placed at intersection, but offset from building center)
var key = new Item('key');
placeItemAwayFromBuildings(key, buildingPositions[1].x + 60, buildingPositions[1 * 4 + 1].y - 60);
key.id = 'key';
items.push(key);
game.addChild(key);
// Map for NPC 5 (placed at intersection, but offset from building center)
var map = new Item('map');
placeItemAwayFromBuildings(map, buildingPositions[3].x - 60, buildingPositions[2 * 4 + 3].y + 60);
map.id = 'map';
items.push(map);
game.addChild(map);
// Police
var policeCars = [];
var helicopters = [];
// Wanted system
var wantedLevel = 0; // 0-5
var wantedDecayTimer = 0;
var wantedMax = 5;
// GUI: Wanted stars
var starIcons = [];
for (var i = 0; i < wantedMax; i++) {
var star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
star.x = 120 + i * 70;
star.y = 80;
star.visible = false;
LK.gui.top.addChild(star);
starIcons.push(star);
}
// GUI: Quest text
var questText = new Text2('', {
size: 70,
fill: "#fff"
});
// Anchor'ı üst ortada olacak şekilde ayarla
questText.anchor.set(0.5, 0);
// Ekranın üst orta kısmına yerleştir (100px aşağıda, üstteki menüyle çakışmasın)
questText.x = LK.gui.width / 2;
questText.y = 120;
LK.gui.top.addChild(questText);
// GUI: Mission complete text
var missionText = new Text2('', {
size: 90,
fill: 0x27AE60
});
missionText.anchor.set(0.5, 0);
missionText.x = LK.gui.width / 2;
missionText.y = 300;
LK.gui.top.addChild(missionText);
// GUI: Police alert text
var policeText = new Text2('', {
size: 80,
fill: 0xE74C3C
});
policeText.anchor.set(0.5, 0);
policeText.x = LK.gui.width / 2;
policeText.y = 400;
LK.gui.top.addChild(policeText);
// Dragging/movement
var dragging = false;
// Helper: Check circle collision
function circlesCollide(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < a.radius + b.radius - 10;
}
// Helper: Show wanted stars
function updateWantedStars() {
for (var i = 0; i < wantedMax; i++) {
starIcons[i].visible = i < wantedLevel;
}
}
// Helper: Set quest text
function setQuestText(txt) {
questText.setText(txt);
}
// Helper: Set mission text
function setMissionText(txt) {
missionText.setText(txt);
if (txt) {
tween(missionText, {
alpha: 1
}, {
duration: 0
});
tween(missionText, {
alpha: 0
}, {
duration: 1800,
easing: tween.linear
});
}
}
// Helper: Set police alert
function setPoliceText(txt) {
policeText.setText(txt);
if (txt) {
tween(policeText, {
alpha: 1
}, {
duration: 0
});
tween(policeText, {
alpha: 0
}, {
duration: 1800,
easing: tween.linear
});
}
}
// Helper: Enter building
var buildingDoor = null;
var currentBuilding = null;
function tryEnterBuilding() {
for (var i = 0; i < buildings.length; i++) {
var b = buildings[i];
// Enter if near entry, but do NOT block movement inside building
if (circlesCollide(player, b)) {
if (b.hasEntry && Math.abs(player.x - (b.x + b.entry.x)) < 80 && Math.abs(player.y - (b.y + b.entry.y)) < 80) {
player.isInBuilding = true;
currentBuilding = b;
setQuestText('Binanın içindesin. Kapıya tıklayarak çıkabilirsin.');
// Hide all city elements (buildings, npcs, items, police, etc)
for (var i = 0; i < buildings.length; i++) {
buildings[i].visible = false;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].visible = false;
}
for (var i = 0; i < specialNpcs.length; i++) {
specialNpcs[i].visible = false;
}
for (var i = 0; i < items.length; i++) {
items[i].visible = false;
}
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].visible = false;
}
for (var i = 0; i < helicopters.length; i++) {
helicopters[i].visible = false;
}
// Show only the building interior background (add corridor entity)
if (typeof buildingInteriorBg !== "undefined" && buildingInteriorBg.parent) {
buildingInteriorBg.parent.removeChild(buildingInteriorBg);
}
buildingInteriorBg = new Container();
buildingInteriorBg.visible = true;
game.addChild(buildingInteriorBg);
// Add the 'corridor' entity to the building interior
if (typeof buildingCorridor !== "undefined" && buildingCorridor.parent) {
buildingCorridor.parent.removeChild(buildingCorridor);
}
buildingCorridor = new Corridor();
buildingCorridor.x = WORLD_W / 2;
buildingCorridor.y = WORLD_H / 2;
buildingInteriorBg.addChild(buildingCorridor);
// Add rooms (oda) to the building interior, placed at the ends of the corridor (not inside it)
if (typeof buildingRooms !== "undefined") {
for (var i = 0; i < buildingRooms.length; i++) {
if (buildingRooms[i].parent) {
buildingRooms[i].parent.removeChild(buildingRooms[i]);
}
}
}
buildingRooms = [];
// Place rooms at the ends of the corridor
var corridorCenterX = WORLD_W / 2;
var corridorCenterY = WORLD_H / 2;
var corridorWidth = 1100;
var corridorHeight = 340;
var roomOffsetX = corridorWidth / 2 + 140; // 140 is half room width + margin
var roomOffsetY = 0;
// Left end room
var leftRoom = new Room();
leftRoom.x = corridorCenterX - roomOffsetX;
leftRoom.y = corridorCenterY;
buildingInteriorBg.addChild(leftRoom);
buildingRooms.push(leftRoom);
// Add a coin at the end of the left room
var leftCoin = new Item('coin');
leftCoin.x = leftRoom.x;
leftCoin.y = leftRoom.y + leftRoom.height / 2 - 60;
leftCoin.id = 'room_coin_left_' + (currentBuilding ? currentBuilding.id : '');
leftCoin.collected = false;
leftCoin.visible = true;
buildingInteriorBg.addChild(leftCoin);
// Right end room
var rightRoom = new Room();
rightRoom.x = corridorCenterX + roomOffsetX;
rightRoom.y = corridorCenterY;
buildingInteriorBg.addChild(rightRoom);
buildingRooms.push(rightRoom);
// Add a coin at the end of the right room
var rightCoin = new Item('coin');
rightCoin.x = rightRoom.x;
rightCoin.y = rightRoom.y + rightRoom.height / 2 - 60;
rightCoin.id = 'room_coin_right_' + (currentBuilding ? currentBuilding.id : '');
rightCoin.collected = false;
rightCoin.visible = true;
buildingInteriorBg.addChild(rightCoin);
// Move player to the center of the building interior
player.x = WORLD_W / 2;
player.y = WORLD_H / 2 + 60;
// Show player inside the building interior
if (player.parent) {
player.parent.removeChild(player);
}
buildingInteriorBg.addChild(player);
// Show a door inside the building to exit (centered at top)
if (buildingDoor && buildingDoor.parent) {
buildingDoor.parent.removeChild(buildingDoor);
}
buildingDoor = LK.getAsset('building_entry', {
anchorX: 0.5,
anchorY: 0.5,
x: WORLD_W / 2,
y: WORLD_H / 2 - 320
});
buildingDoor.interactive = true;
buildingDoor.visible = true;
game.addChild(buildingDoor);
return false;
}
}
}
return false;
}
// Helper: Exit building
function tryExitBuilding(x, y) {
if (player.isInBuilding && buildingDoor) {
// Check if tap is on the door
var dx = x - buildingDoor.x;
var dy = y - buildingDoor.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < buildingDoor.width / 2 + 30) {
// Hide building interior background
if (typeof buildingInteriorBg !== "undefined" && buildingInteriorBg.parent) {
buildingInteriorBg.parent.removeChild(buildingInteriorBg);
buildingInteriorBg.visible = false;
}
// Restore city elements
for (var i = 0; i < buildings.length; i++) {
buildings[i].visible = true;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].visible = true;
}
for (var i = 0; i < specialNpcs.length; i++) {
specialNpcs[i].visible = true;
}
for (var i = 0; i < items.length; i++) {
items[i].visible = true;
}
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].visible = true;
}
for (var i = 0; i < helicopters.length; i++) {
helicopters[i].visible = true;
}
// Move player outside the building, near the entry
if (currentBuilding && currentBuilding.hasEntry && currentBuilding.entry) {
player.x = currentBuilding.x + currentBuilding.entry.x;
player.y = currentBuilding.y + currentBuilding.entry.y + 100;
} else {
player.x = player.x + 200;
player.y = player.y + 200;
}
// Restore player to city scene
if (player.parent) {
player.parent.removeChild(player);
}
game.addChild(player);
player.isInBuilding = false;
setQuestText('');
// Remove door
if (buildingDoor.parent) {
buildingDoor.parent.removeChild(buildingDoor);
}
buildingDoor = null;
currentBuilding = null;
}
}
}
// Helper: Interact with NPCs
function tryInteractNPC() {
var interacted = false;
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (circlesCollide(player, npc)) {
// If NPC has a quest
if (npc.hasQuest) {
if (!npc.questGiven && !npc.questCompleted) {
// Show quest text and mark quest as given
setQuestText(npc.questText);
npc.questGiven = true;
npc.dialogIndex = 0;
interacted = true;
} else if (npc.questGiven && !npc.questCompleted) {
// Check if player has the quest item (only the correct item is accepted)
var found = false;
for (var j = 0; j < items.length; j++) {
if (items[j].id === npc.questTarget && items[j].collected) {
npc.questCompleted = true;
setQuestText('Teşekkürler! Görevi tamamladın.');
setMissionText('Görev Tamamlandı!');
LK.setScore(LK.getScore() + 1);
interacted = true;
found = true;
break;
}
}
// If not found, remind quest
if (!npc.questCompleted) {
setQuestText('Görev: ' + npc.questText + " (Sadece doğru eşyayı kabul eder)");
interacted = true;
}
} else if (npc.questCompleted) {
// After quest is completed, show dialog or thanks
if (!npc._thankedTwice) {
setQuestText('Tekrar teşekkürler!');
if (typeof npc._thanksCount === "undefined") {
npc._thanksCount = 0;
}
npc._thanksCount++;
if (npc._thanksCount >= 2) {
npc._thankedTwice = true;
// Clear quest text after a short delay so player can move freely
LK.setTimeout(function () {
setQuestText('');
}, 1200);
}
interacted = true;
} else {
// Already thanked twice, allow player to move freely
setQuestText('');
// Do not block movement
}
}
}
// If no quest, cycle through dialog
if (!npc.hasQuest && npc.dialog && npc.dialog.length > 0) {
setQuestText(npc.dialog[npc.dialogIndex]);
npc.dialogIndex = (npc.dialogIndex + 1) % npc.dialog.length;
interacted = true;
} else if (!npc.hasQuest) {
setQuestText('Merhaba!');
interacted = true;
}
}
}
for (var i = 0; i < specialNpcs.length; i++) {
var npc = specialNpcs[i];
if (circlesCollide(player, npc)) {
if (!npc.questGiven && !npc.questCompleted) {
setQuestText(npc.questText);
npc.questGiven = true;
interacted = true;
} else if (npc.questGiven && !npc.questCompleted) {
// Check if player has wallet (only wallet is accepted)
var found = false;
for (var j = 0; j < items.length; j++) {
if (items[j].id === 'wallet' && items[j].collected) {
npc.questCompleted = true;
setQuestText('Thank you for finding my wallet!');
setMissionText('Mission Complete!');
LK.setScore(LK.getScore() + 1);
interacted = true;
found = true;
break;
}
}
if (!npc.questCompleted) {
setQuestText('Did you find my wallet? (Only the wallet is accepted)');
interacted = true;
}
} else if (npc.questCompleted) {
setQuestText('Thanks again!');
interacted = true;
}
}
}
// Always return false so player movement is never blocked by NPC interaction
return false;
}
// Helper: Interact with items
function tryCollectItem() {
for (var i = 0; i < items.length; i++) {
var item = items[i];
if (!item.collected && circlesCollide(player, item)) {
item.collected = true;
item.visible = false;
// Show a unique message for each item
var foundMsg = '';
if (item.id === 'wallet') {
foundMsg = 'You found a wallet!';
} else if (item.id === 'flower') {
foundMsg = 'Bir çiçek buldun!';
} else if (item.id === 'coin') {
foundMsg = 'Bir para buldun!';
} else if (item.id === 'book') {
foundMsg = 'Bir kitap buldun!';
} else if (item.id === 'bag') {
foundMsg = 'Bir poşet buldun!';
} else if (item.id === 'key') {
foundMsg = 'Bir anahtar buldun!';
} else if (item.id === 'map') {
foundMsg = 'Bir harita buldun!';
} else {
foundMsg = 'Bir eşya buldun!';
}
setMissionText(foundMsg);
return true;
}
}
return false;
}
// Helper: Commit crime (damage property)
function tryDamageProperty(x, y) {
// If player is near a building, "damage" it
for (var i = 0; i < buildings.length; i++) {
var b = buildings[i];
var dx = x - (b.x + 150);
var dy = y - (b.y + 150);
if (Math.abs(dx) < 150 && Math.abs(dy) < 150) {
// Crime committed!
wantedLevel = Math.min(wantedLevel + 1, wantedMax);
updateWantedStars();
setPoliceText('Police alerted!');
wantedDecayTimer = 0;
LK.effects.flashObject(b, 0xff0000, 500);
return true;
}
}
return false;
}
// Police AI: spawn police based on wanted level
function updatePolice() {
// Remove police if wantedLevel is 0
if (wantedLevel === 0) {
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].destroy();
}
policeCars = [];
for (var i = 0; i < helicopters.length; i++) {
helicopters[i].destroy();
}
helicopters = [];
return;
}
// Spawn police cars
while (policeCars.length < wantedLevel && policeCars.length < 3) {
var pc = new PoliceCar();
// Spawn at random edge
var edge = Math.floor(Math.random() * 4);
if (edge === 0) {
pc.x = 0;
pc.y = Math.random() * WORLD_H;
}
if (edge === 1) {
pc.x = WORLD_W;
pc.y = Math.random() * WORLD_H;
}
if (edge === 2) {
pc.x = Math.random() * WORLD_W;
pc.y = 0;
}
if (edge === 3) {
pc.x = Math.random() * WORLD_W;
pc.y = WORLD_H;
}
pc.target = player;
policeCars.push(pc);
game.addChild(pc);
}
// Spawn helicopter at wanted level 4+
if (wantedLevel >= 4 && helicopters.length < 1) {
var heli = new Helicopter();
heli.x = Math.random() * WORLD_W;
heli.y = 0;
heli.target = player;
helicopters.push(heli);
game.addChild(heli);
}
}
// Police AI: check for player caught
function checkPoliceCatch() {
for (var i = 0; i < policeCars.length; i++) {
if (circlesCollide(player, policeCars[i])) {
LK.effects.flashScreen(0xff0000, 1000);
setPoliceText('You were caught!');
LK.showGameOver();
return true;
}
}
for (var i = 0; i < helicopters.length; i++) {
if (Math.abs(player.x - helicopters[i].x) < 80 && Math.abs(player.y - helicopters[i].y) < 80) {
LK.effects.flashScreen(0xff0000, 1000);
setPoliceText('Helicopter caught you!');
LK.showGameOver();
return true;
}
}
return false;
}
// Decay wanted level over time
function decayWantedLevel() {
if (wantedLevel > 0) {
wantedDecayTimer++;
if (wantedDecayTimer > 300) {
// ~5 seconds
wantedLevel--;
updateWantedStars();
wantedDecayTimer = 0;
}
}
}
// Game move handler (tap to move, tap on NPC/building to interact)
function handleMove(x, y, obj) {
// Convert to game coordinates
if (dragging) {
player.moveTo(x, y);
}
}
// Tap down: move or interact
game.down = function (x, y, obj) {
// If in building, check if tap is on the door to exit
if (player.isInBuilding) {
tryExitBuilding(x, y);
return;
}
// Try interact with NPC
if (tryInteractNPC()) {
return;
}
// Try collect item
if (tryCollectItem()) {
return;
}
// Try enter building
if (tryEnterBuilding()) {
return;
}
// Otherwise, move
player.moveTo(x, y);
dragging = true;
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
dragging = false;
};
game.move = handleMove;
// Score display (missions completed)
var scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
scoreTxt.x = LK.gui.width / 2;
scoreTxt.y = 40;
// Main game update
game.update = function () {
if (!player.isInBuilding) {
player.update();
// Update NPCs
for (var i = 0; i < npcs.length; i++) {
npcs[i].update();
}
for (var i = 0; i < specialNpcs.length; i++) {
specialNpcs[i].update();
}
// Update police
for (var i = 0; i < policeCars.length; i++) {
policeCars[i].update();
}
for (var i = 0; i < helicopters.length; i++) {
helicopters[i].update();
}
// Police AI
updatePolice();
checkPoliceCatch();
decayWantedLevel();
} else {
// Allow player to move inside building (no city collision logic)
player.update();
}
// Update score
scoreTxt.setText(LK.getScore());
// Clamp player to world
player.x = Math.max(player.radius, Math.min(WORLD_W - player.radius, player.x));
player.y = Math.max(player.radius, Math.min(WORLD_H - player.radius, player.y));
// Ortadaki yazıların her zaman ekranın ortasında kalmasını sağla
if (typeof LK !== "undefined" && LK.gui && typeof LK.gui.width === "number" && LK.gui.width > 0) {
questText.x = LK.gui.width / 2;
missionText.x = LK.gui.width / 2;
policeText.x = LK.gui.width / 2;
scoreTxt.x = LK.gui.width / 2;
}
};
// Initial wanted stars
updateWantedStars();
setQuestText('Explore the city! Tap on people or buildings.');
setMissionText('');
setPoliceText('');
;
// Always play 'a' sesi at 30% volume, looping
LK.playMusic('a', {
volume: 0.3
});
kapı. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
birkaç camı olan, eşit bir gökdelen. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
mavi camlı, bir helikopter. In-Game asset. 2d. High contrast. No shadows
futbol topu. In-Game asset. 2d. High contrast. No shadows
gergin görünüşlü takım elbiseli adam. In-Game asset. 2d. High contrast. No shadows
yaşlı, üzerinde mor bir cübbe olan, ve mal mal sırıtan bir adam. vücudu tamamen görünür. In-Game asset. 2d. High contrast. No shadows
Cristiano Ronaldo. In-Game asset. 2d. High contrast. No shadows
mavi polis giysili, cebinde silah olan tüm vücudu görünen bir polis. In-Game asset. 2d. High contrast. No shadows
yıldız. In-Game asset. 2d. High contrast. No shadows
deri çanta. In-Game asset. 2d. High contrast. No shadows
kitap. In-Game asset. 2d. High contrast. No shadows
5 tane üst üste durmuş, kule gibi ama yamuk olan ve bir tanede yere düşmüş madeni para. In-Game asset. 2d. High contrast. No shadows
papatya. In-Game asset. 2d. High contrast. No shadows
içinde birkaç tane daha anahtar olan, ama bir tane anahtarı parlıyor. anahtarlık. In-Game asset. 2d. High contrast. No shadows
küre dünya. In-Game asset. 2d. High contrast. No shadows
İçinde birkaç dolar olan cüzdan. In-Game asset. 2d. High contrast. No shadows
üsttenbakış, parkeleri olan bir koridor. In-Game asset. 2d. High contrast. No shadows
kuş bakışı birkaç masası olan bir oda. In-Game asset. 2d. High contrast. No shadows
sarı çizgileri olan, siyah asfalt bir yol . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat