User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'for (var i = 0; i < buildings.length; i++) {' Line Number: 288
User prompt
npc'ler için daha fazla diyalog ve daha fazla görev.NPC'ler bina veya herhangi şeyin arkasında kalmasın.
User prompt
NPC ler'in konuşması ekranı ortalar
User prompt
evler yan yana değil, ve nizami
User prompt
daha fazla bina
User prompt
Please fix the bug: 'TypeError: specialNpcs[i].update is not a function' in or related to this line: 'specialNpcs[i].update();' Line Number: 599
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'tint')' in or related to this line: 'self.children[0].tint = 0x8e44ad;' Line Number: 170
Code edit (1 edits merged)
Please save this source code
User prompt
Mini City Mayhem
Initial prompt
GTA style, streets, many buildings and communication as well as NPCs that we can enter. NPCs can provide services such as certain simple events such as "my ball is lost in that place, they will find it and come to me". Each NPC is different and special. When we commit a crime, for example, any part is broken or malfunctioning, let the police be and chase us, let the police have cars and helicopters, and let them work with the star system like in GTA.
/**** * 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