User prompt
pause menüsünden ses özelliklerini açıp kapatabilelim.
User prompt
ses dosyalarını ayarlayalım güzel bir background sound oluşturalım, paket teslim alındığında "thanks, drive safe" gibi kelimeler kullanılsın. Teslimat yapıldığında ise "thank you, here a tip for you" ve paradüşme sesi ekleyelim. Eğer peşimize polis takılırsa süreğen bir siren sesi olsun. Ayrıca vandallar paketimizi çalarsa kaza sesi veya ürkütücü kahkaha sesi ekleyelim.
User prompt
binalara çarpınca araçlar yola kaysın. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yön gösterici ok oyuncuya daha yakın ve büyük olsun. Ve heart beaten şeklinde dükkana veya müşteriye yakınlaştıkça daha hızlı uzakken daha yavaş bir animasyon olsun. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'x')' in or related to this line: 'policeNPC.patrol.x = 200 + Math.random() * 1600;' Line Number: 1312
User prompt
npc ler haritada sürekli gezmek zorundalar. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Tüm npcleri sil ve baştan yarat. Bina görüntülerinin etrafında gezebilecekler fakat binalara temas edemezler. Polisler sürekli vandal ve insurance ı olmayan kuryeleri arayacaklar. Vandallar polisi gördüklerinde kaçacaklar. Tüm npc ler maksimum 20px etrafını algılayabilirler.
User prompt
npc kuryeler durmadan paket alıp taşırlar.
User prompt
npc kuryelere paket alma ve teslim etme konusunda biraz daha esnek ol paket teslim almalarını engelleyen birşey var.
User prompt
npc kuryeler görünmüyorlarherhangi bir teslimat gerçekleşmiyor kontrol et
User prompt
polislerin kuryelerin ve vandalların doğdukları noktaları tekrar gözden geçir ve polisleri merkezden, vandalları haritanın 4 köşesinden, npc kuryeleri de shop ların önünde başlamasını sağla. NPC kuryeler belirli rotalardan paket alıp taşısınlar.
User prompt
im here yazısını 10 px e küçült ve rengini beyaz yap 3px siyah stroke ekle
User prompt
Im here yazılarını küçült
User prompt
biraz daha yaklaşabiliriz mahalle ekrana sığacak kadar
User prompt
tanrı modu oluştur ve tüm haritayı görebileceği kadar zoom out çalışsın.
User prompt
bütün npc ler yollarda doğmak zorunda bina ile çakışamazlar. Hepsi hareket halinde olmak zorunda. Kuryeler sipariş alıp teslim etmek zorunda.
User prompt
bütün npc ler sadece çevresindeki binaları düşünsün. Genel olarak haritayı düşündükleri için hem işlemci yoruluyor hemde hatalar yapıyorlar hareket etmiyorlar.
User prompt
yazı renkleri beyaz olsun
User prompt
komple gri arkaplanı sil ve vandal butonu aynı şekilde kalsın
User prompt
genel olarak menünün transparan görüntüsü sankı yazıların üstündeymiş gibi duruyor
User prompt
vandal seçeneğinin arka planını beyaz yap
User prompt
Please fix the bug: 'FFFFFF is not defined' in or related to this line: 'var policeButton = new Text2('POLICE', {' Line Number: 968
Code edit (1 edits merged)
Please save this source code
User prompt
evet şimdi Choose your role menüsündeki her bir yazıya 5px lik beyaz stroke ekle
User prompt
şimdi okunabilir boyutlara küçült
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var ArrowIndicator = Container.expand(function (targetX, targetY, color, arrowType) {
var self = Container.call(this);
// Create arrow shape using dedicated arrow assets
var assetName = arrowType === 'pickup' ? 'pickupArrow' : 'deliveryArrow';
var arrowGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
arrowGraphics.scaleX = 0.8;
arrowGraphics.scaleY = 1.2;
self.targetX = targetX;
self.targetY = targetY;
self.update = function () {
// Calculate direction to target
var dx = self.targetX - courier.x;
var dy = self.targetY - courier.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 150) {
// Position arrow at edge of screen pointing toward target
var angle = Math.atan2(dy, dx);
var edgeDistance = 300;
// Position relative to screen center (where courier appears)
self.x = 1024 + Math.cos(angle) * edgeDistance;
self.y = 1366 + Math.sin(angle) * edgeDistance;
// Rotate arrow to point toward target
arrowGraphics.rotation = angle;
self.visible = true;
} else {
self.visible = false;
}
};
return self;
});
var Courier = Container.expand(function () {
var self = Container.call(this);
var courierGraphics = self.attachAsset('courierAsset', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.hasOrder = false;
self.targetRestaurant = null;
self.targetCustomer = null;
self.moveToward = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Smooth movement with momentum for agar.io feel
var moveSpeed = Math.min(self.speed, distance * 0.15);
var newX = self.x + dx / distance * moveSpeed;
var newY = self.y + dy / distance * moveSpeed;
// Check collision with buildings
var canMove = true;
for (var i = 0; i < buildings.length; i++) {
var tempCourier = {
x: newX,
y: newY,
width: 9,
height: 9
};
var building = buildings[i];
if (tempCourier.x < building.x + building.width / 2 && tempCourier.x + tempCourier.width / 2 > building.x - building.width / 2 && tempCourier.y < building.y + building.height / 2 && tempCourier.y + tempCourier.height / 2 > building.y - building.height / 2) {
canMove = false;
break;
}
}
// Check collision with restaurants/shops
for (var i = 0; i < restaurants.length; i++) {
var tempCourier = {
x: newX,
y: newY,
width: 9,
height: 9
};
var restaurant = restaurants[i];
if (tempCourier.x < restaurant.x + restaurant.width / 2 && tempCourier.x + tempCourier.width / 2 > restaurant.x - restaurant.width / 2 && tempCourier.y < restaurant.y + restaurant.height / 2 && tempCourier.y + tempCourier.height / 2 > restaurant.y - restaurant.height / 2) {
canMove = false;
break;
}
}
// Check collision with customers
for (var i = 0; i < customers.length; i++) {
var tempCourier = {
x: newX,
y: newY,
width: 9,
height: 9
};
var customer = customers[i];
if (tempCourier.x < customer.x + 1 / 2 && tempCourier.x + tempCourier.width / 2 > customer.x - 1 / 2 && tempCourier.y < customer.y + 1 / 2 && tempCourier.y + tempCourier.height / 2 > customer.y - 1 / 2) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
}
}
};
return self;
});
var Customer = Container.expand(function () {
var self = Container.call(this);
var customerGraphics = self.attachAsset('customer', {
anchorX: 0.5,
anchorY: 0.5
});
self.waitingForOrder = false;
self.orderTime = 0;
self.maxWaitTime = 10000; // 10 seconds
self.deliveryTarget = null;
self.orderColor = 0x2196f3; // Default blue
self.setOrderColor = function (color) {
self.orderColor = color;
customerGraphics.tint = color;
};
self.update = function () {
if (self.waitingForOrder) {
self.orderTime += 16.67; // Approximate ms per frame at 60fps
}
};
return self;
});
var NPCCourier = Container.expand(function () {
var self = Container.call(this);
var courierGraphics = self.attachAsset('courierAsset', {
anchorX: 0.5,
anchorY: 0.5
});
courierGraphics.tint = 0xff5722;
self.speed = 2;
self.detectionRadius = 20;
self.isMoving = false;
self.hasInsurance = false;
self.lastX = self.x;
self.lastY = self.y;
self.lastSeenPolice = false;
self.lastSeenVandal = false;
self.startRoaming = function () {
if (self.isMoving) return;
self.isMoving = true;
var newX = 200 + Math.random() * 1600;
var newY = 200 + Math.random() * 2300;
var dx = newX - self.x;
var dy = newY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var duration = distance * 50; // Adjust speed by changing multiplier
tween(self, {
x: newX,
y: newY
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.isMoving = false;
// Start next movement after a short pause
LK.setTimeout(function () {
self.startRoaming();
}, 500 + Math.random() * 1000);
}
});
};
self.update = function () {
// Start roaming if not already moving
if (!self.isMoving) {
self.startRoaming();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var NPCPolice = Container.expand(function () {
var self = Container.call(this);
var policeGraphics = self.attachAsset('policeAsset', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.detectionRadius = 20;
self.target = null;
self.isMoving = false;
self.lastX = self.x;
self.lastY = self.y;
self.thinkTimer = 0;
self.startRoaming = function () {
if (self.isMoving) return;
self.isMoving = true;
var newX = 200 + Math.random() * 1600;
var newY = 200 + Math.random() * 2300;
var dx = newX - self.x;
var dy = newY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var duration = distance * 40; // Faster than couriers
tween(self, {
x: newX,
y: newY
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.isMoving = false;
// Start next movement after a short pause
LK.setTimeout(function () {
self.startRoaming();
}, 300 + Math.random() * 800);
}
});
};
self.update = function () {
self.thinkTimer += 16.67;
if (self.thinkTimer >= 100) {
self.thinkTimer = 0;
self.target = null;
// Look for vandals
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (npc.npcType === 'vandal') {
var dx = npc.x - self.x;
var dy = npc.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.target = npc;
// Stop current movement and chase target
tween.stop(self, {
x: true,
y: true
});
self.isMoving = false;
break;
}
}
}
// Look for uninsured couriers
if (!self.target) {
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (npc.npcType === 'courier' && !npc.hasInsurance) {
var dx = npc.x - self.x;
var dy = npc.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.target = npc;
// Stop current movement and chase target
tween.stop(self, {
x: true,
y: true
});
self.isMoving = false;
break;
}
}
}
}
// Check player courier if no insurance
if (!self.target && playerRole === 'courier' && courier && !hasInsurance) {
var dx = courier.x - self.x;
var dy = courier.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.target = courier;
// Stop current movement and chase target
tween.stop(self, {
x: true,
y: true
});
self.isMoving = false;
}
}
}
// Move behavior
if (self.target) {
// Chase target with tween
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
tween.stop(self, {
x: true,
y: true
});
tween(self, {
x: self.target.x,
y: self.target.y
}, {
duration: distance * 30,
easing: tween.linear
});
}
} else {
// Start roaming if not already moving
if (!self.isMoving) {
self.startRoaming();
}
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var NPCVandal = Container.expand(function () {
var self = Container.call(this);
var vandalGraphics = self.attachAsset('vandalAsset', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.detectionRadius = 20;
self.fleeing = false;
self.isMoving = false;
self.lastX = self.x;
self.lastY = self.y;
self.thinkTimer = 0;
self.startRoaming = function () {
if (self.isMoving || self.fleeing) return;
self.isMoving = true;
var newX = 200 + Math.random() * 1600;
var newY = 200 + Math.random() * 2300;
var dx = newX - self.x;
var dy = newY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var duration = distance * 60; // Slower than police
tween(self, {
x: newX,
y: newY
}, {
duration: duration,
easing: tween.linear,
onFinish: function onFinish() {
self.isMoving = false;
// Start next movement after a short pause
LK.setTimeout(function () {
self.startRoaming();
}, 800 + Math.random() * 1200);
}
});
};
self.flee = function (fromX, fromY) {
self.fleeing = true;
tween.stop(self, {
x: true,
y: true
});
self.isMoving = false;
var dx = self.x - fromX;
var dy = self.y - fromY;
var fleeX = self.x + dx * 5;
var fleeY = self.y + dy * 5;
// Keep within bounds
fleeX = Math.max(100, Math.min(1900, fleeX));
fleeY = Math.max(200, Math.min(2500, fleeY));
tween(self, {
x: fleeX,
y: fleeY
}, {
duration: 2000,
easing: tween.easeOut,
onFinish: function onFinish() {
self.fleeing = false;
LK.setTimeout(function () {
self.startRoaming();
}, 1000);
}
});
};
self.update = function () {
self.thinkTimer += 16.67;
if (self.thinkTimer >= 100) {
self.thinkTimer = 0;
var shouldFlee = false;
// Look for police
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (npc.npcType === 'police') {
var dx = npc.x - self.x;
var dy = npc.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.flee(npc.x, npc.y);
shouldFlee = true;
break;
}
}
}
// Check player police
if (!shouldFlee && playerRole === 'police' && courier) {
var dx = courier.x - self.x;
var dy = courier.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.flee(courier.x, courier.y);
shouldFlee = true;
}
}
}
// Start roaming if not fleeing and not already moving
if (!self.fleeing && !self.isMoving) {
self.startRoaming();
}
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
var Order = Container.expand(function () {
var self = Container.call(this);
var orderGraphics = self.attachAsset('order', {
anchorX: 0.5,
anchorY: 0.5
});
self.restaurant = null;
self.customer = null;
self.baseTimeLimit = 25000; // 25 seconds minimum
self.timeLimit = self.baseTimeLimit;
self.timeRemaining = self.timeLimit;
self.isPickedUp = false;
self.courierSpeed = 8; // Default courier speed
self.setColor = function (color) {
orderGraphics.tint = color;
};
self.startDeliveryTimer = function (courierSpeed) {
self.isPickedUp = true;
self.courierSpeed = courierSpeed;
self.timeLimit = self.baseTimeLimit;
self.timeRemaining = self.timeLimit;
};
self.updateTimeLimit = function (courierX, courierY) {
if (!self.isPickedUp || !self.customer) {
return;
}
// Calculate distance to customer
var dx = self.customer.x - courierX;
var dy = self.customer.y - courierY;
var distance = Math.sqrt(dx * dx + dy * dy);
// Calculate time needed to reach customer at max speed + 2 seconds buffer
var timeToReach = distance / self.courierSpeed * 16.67 + 2000; // Convert to ms and add 2s buffer
// Use the greater of base time limit or calculated time
var newTimeLimit = Math.max(self.baseTimeLimit, timeToReach);
// Only extend time, never reduce it
if (newTimeLimit > self.timeLimit) {
var extension = newTimeLimit - self.timeLimit;
self.timeLimit = newTimeLimit;
self.timeRemaining += extension;
}
};
self.update = function () {
if (self.isPickedUp && self.timeRemaining > 0) {
self.timeRemaining -= 16.67;
}
};
return self;
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
var powerupGraphics = self.attachAsset(type === 'health' ? 'healthPowerup' : 'timePowerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.type = type;
self.lifeTime = 0;
self.maxLifeTime = 8000; // 8 seconds
self.update = function () {
self.lifeTime += 16.67;
// Pulse animation
var pulse = 1 + 0.2 * Math.sin(self.lifeTime * 0.01);
powerupGraphics.scaleX = pulse;
powerupGraphics.scaleY = pulse;
};
return self;
});
var Restaurant = Container.expand(function () {
var self = Container.call(this);
var restaurantGraphics = self.attachAsset('restaurant', {
anchorX: 0.5,
anchorY: 0.5
});
self.hasOrder = false;
self.orderReady = false;
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x8BC34A
});
/****
* Game Code
****/
// Game variables
var courier;
var npcs = [];
var restaurants = [];
var customers = [];
var orders = [];
var aiOrders = [];
var powerUps = [];
var buildings = [];
var health = 100;
var money = 20;
var currentOrder = null;
var gameState = 'pickup'; // 'pickup', 'delivery'
var timeFreezeActive = false;
var timeFreezeRemaining = 0;
// Role system
var playerRole = storage.playerRole || null; // 'courier', 'police', 'vandal'
var hasInsurance = false;
var insuranceEndTime = 0;
var gameStarted = false;
// Camera system
var cameraX = 0;
var cameraY = 0;
var worldContainer;
var pickupArrow = null;
var deliveryArrow = null;
// God mode system
var godMode = false;
var godModeScale = 0.8; // Zoom out scale for full map view
var normalScale = 3; // Normal game scale
// Order colors for variety
var orderColors = [0xffff00, 0xff4444, 0x44ff44, 0x4444ff, 0xff44ff, 0x44ffff, 0xffa500, 0x800080];
// UI Elements
var healthBar = new Text2('Health: 100', {
size: 48
});
healthBar.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthBar);
healthBar.x = 120;
healthBar.y = 20;
var moneyText = new Text2('$0', {
size: 48
});
moneyText.anchor.set(1, 0);
LK.gui.topRight.addChild(moneyText);
moneyText.x = -20;
moneyText.y = 20;
var orderTimer = new Text2('', {
size: 42
});
orderTimer.anchor.set(0.5, 0);
LK.gui.top.addChild(orderTimer);
orderTimer.y = 100;
// Role selection UI
var roleSelectionContainer = new Container();
LK.gui.center.addChild(roleSelectionContainer);
var roleTitle = new Text2('Choose Your Role', {
size: 72,
fill: 0xFFFFFF,
stroke: 0xFFFFFF,
strokeThickness: 8
});
roleTitle.anchor.set(0.5, 0.5);
roleTitle.y = -200;
roleSelectionContainer.addChild(roleTitle);
// Create green background for courier button
var courierButtonBg = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1
});
courierButtonBg.tint = 0x4caf50;
courierButtonBg.y = -50;
roleSelectionContainer.addChild(courierButtonBg);
var courierButton = new Text2('COURIER', {
size: 48,
fill: 0xFFFFFF,
stroke: 0xFFFFFF,
strokeThickness: 8
});
courierButton.anchor.set(0.5, 0.5);
courierButton.y = -50;
roleSelectionContainer.addChild(courierButton);
// Create blue background for police button
var policeButtonBg = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1
});
policeButtonBg.tint = 0x2196f3;
policeButtonBg.y = 50;
roleSelectionContainer.addChild(policeButtonBg);
var policeButton = new Text2('POLICE', {
size: 48,
fill: 0xFFFFFF,
stroke: 0xFFFFFF,
strokeThickness: 8
});
policeButton.anchor.set(0.5, 0.5);
policeButton.y = 50;
roleSelectionContainer.addChild(policeButton);
// Create white background for vandal button
var vandalButtonBg = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 1
});
vandalButtonBg.tint = 0xFFFFFF;
vandalButtonBg.y = 150;
roleSelectionContainer.addChild(vandalButtonBg);
var vandalButton = new Text2('VANDAL', {
size: 48,
fill: 0xFFFFFF,
stroke: 0xFFFFFF,
strokeThickness: 8
});
vandalButton.anchor.set(0.5, 0.5);
vandalButton.y = 150;
roleSelectionContainer.addChild(vandalButton);
// Insurance UI (for couriers)
var insuranceButton = new Text2('Buy Insurance ($5)', {
size: 36
});
insuranceButton.anchor.set(0.5, 0);
LK.gui.top.addChild(insuranceButton);
insuranceButton.y = 160;
insuranceButton.visible = false;
var insuranceStatus = new Text2('', {
size: 32
});
insuranceStatus.anchor.set(0.5, 0);
LK.gui.top.addChild(insuranceStatus);
insuranceStatus.y = 210;
// God mode toggle button
var godModeButton = new Text2('God Mode: OFF', {
size: 36,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 4
});
godModeButton.anchor.set(1, 0);
LK.gui.topRight.addChild(godModeButton);
godModeButton.x = -20;
godModeButton.y = 80;
// Create neighborhood layout
function createNeighborhood() {
// Fill entire screen with buildings and restaurants
var buildingRows = 20; // More rows to fill the map
var buildingCols = 15; // More columns to fill the map
var gridSpacingX = 135; // Tighter spacing
var gridSpacingY = 135; // Tighter spacing
var startX = 70; // Start position
var startY = 150; // Start from top, leaving space for UI
// Helper function to check if two objects overlap
function checkOverlap(obj1, obj2) {
var obj1Left = obj1.x - obj1.width / 2;
var obj1Right = obj1.x + obj1.width / 2;
var obj1Top = obj1.y - obj1.height / 2;
var obj1Bottom = obj1.y + obj1.height / 2;
var obj2Left = obj2.x - obj2.width / 2;
var obj2Right = obj2.x + obj2.width / 2;
var obj2Top = obj2.y - obj2.height / 2;
var obj2Bottom = obj2.y + obj2.height / 2;
return !(obj1Right < obj2Left || obj1Left > obj2Right || obj1Bottom < obj2Top || obj1Top > obj2Bottom);
}
// Create array of all positions
var allPositions = [];
for (var row = 0; row < buildingRows; row++) {
for (var col = 0; col < buildingCols; col++) {
var buildingX = startX + col * gridSpacingX;
var buildingY = startY + row * gridSpacingY;
// Ensure buildings stay within reasonable bounds
if (buildingX < 2000 && buildingY < 2700) {
allPositions.push({
x: buildingX,
y: buildingY
});
}
}
}
// Calculate shop count (1 shop per 15 buildings)
var totalPositions = allPositions.length;
var shopCount = Math.floor(totalPositions / 16); // 1 shop for every 15 buildings (16 total positions)
// Shuffle positions to randomly distribute shops
for (var i = allPositions.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = allPositions[i];
allPositions[i] = allPositions[j];
allPositions[j] = temp;
}
// Track all placed objects to prevent overlaps
var placedObjects = [];
// Place shops in first N positions after shuffle
for (var i = 0; i < shopCount; i++) {
var pos = allPositions[i];
var restaurant = worldContainer.addChild(new Restaurant());
restaurant.x = pos.x;
restaurant.y = pos.y;
restaurant.width = 60; // Restaurant asset width
restaurant.height = 60; // Restaurant asset height
// Check for overlaps with existing objects
var overlaps = false;
for (var j = 0; j < placedObjects.length; j++) {
if (checkOverlap(restaurant, placedObjects[j])) {
overlaps = true;
break;
}
}
if (!overlaps) {
restaurants.push(restaurant);
placedObjects.push(restaurant);
} else {
// Remove restaurant if it overlaps
restaurant.destroy();
}
}
// Place buildings in remaining positions
for (var i = shopCount; i < totalPositions; i++) {
var pos = allPositions[i];
var building = worldContainer.addChild(LK.getAsset('building', {
anchorX: 0.5,
anchorY: 0.5,
x: pos.x,
y: pos.y,
scaleX: 2.5,
scaleY: 2.5
}));
building.width = 40; // Building collision width (unchanged)
building.height = 40; // Building collision height (unchanged)
// Check for overlaps with existing objects
var overlaps = false;
for (var j = 0; j < placedObjects.length; j++) {
if (checkOverlap(building, placedObjects[j])) {
overlaps = true;
break;
}
}
if (!overlaps) {
buildings.push(building);
placedObjects.push(building);
} else {
// Remove building if it overlaps
building.destroy();
}
}
// Create customers - place them outside buildings (not restaurants)
var customerCount = Math.min(25, buildings.length);
for (var i = 0; i < customerCount; i++) {
var customer = worldContainer.addChild(new Customer());
// Place customer further outside building to account for 2.5x visual scale
// Buildings are visually 2.5x larger, so move customers proportionally further out
customer.x = buildings[i].x + buildings[i].width / 2 * 2.5 + 15;
customer.y = buildings[i].y + buildings[i].height / 2 * 2.5 + 15;
// Hide all customers initially - only show when they have an order
customer.visible = false;
customers.push(customer);
}
// Create boundary fences around the neighborhood
// Top fence
var topFence = worldContainer.addChild(LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 0.5,
x: 1000,
y: 10,
scaleX: 20,
scaleY: 1
}));
topFence.width = 2000;
topFence.height = 20;
buildings.push(topFence);
// Bottom fence
var bottomFence = worldContainer.addChild(LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 0.5,
x: 1000,
y: 2722,
scaleX: 20,
scaleY: 1
}));
bottomFence.width = 2000;
bottomFence.height = 20;
buildings.push(bottomFence);
// Left fence
var leftFence = worldContainer.addChild(LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 0.5,
x: 10,
y: 1400,
scaleX: 1,
scaleY: 135
}));
leftFence.width = 20;
leftFence.height = 2700;
buildings.push(leftFence);
// Right fence
var rightFence = worldContainer.addChild(LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 0.5,
x: 2038,
y: 1400,
scaleX: 1,
scaleY: 135
}));
rightFence.width = 20;
rightFence.height = 2700;
buildings.push(rightFence);
}
function createNewOrder() {
if (currentOrder) {
return;
}
var availableRestaurants = restaurants.filter(function (r) {
return !r.hasOrder;
});
var availableCustomers = customers.filter(function (c) {
return !c.waitingForOrder;
});
if (availableRestaurants.length === 0 || availableCustomers.length === 0) {
return;
}
var restaurant = availableRestaurants[Math.floor(Math.random() * availableRestaurants.length)];
var customer = availableCustomers[Math.floor(Math.random() * availableCustomers.length)];
var order = worldContainer.addChild(new Order());
order.x = restaurant.x;
order.y = restaurant.y - 60;
order.restaurant = restaurant;
order.customer = customer;
order.claimed = false; // Allow AI couriers to claim this order
// Set random order color and match customer color
var orderColor = orderColors[Math.floor(Math.random() * orderColors.length)];
order.setColor(orderColor);
customer.setOrderColor(orderColor);
restaurant.hasOrder = true;
restaurant.orderReady = true;
customer.waitingForOrder = true;
customer.orderTime = 0;
// Show only this customer for player orders
customer.visible = true;
// Hide all other customers that might be visible from AI orders
for (var c = 0; c < customers.length; c++) {
if (customers[c] !== customer) {
customers[c].visible = false;
}
}
// Add "I'm here" text to the building that has the customer
var building = null;
// Find the building this customer belongs to (customers are positioned relative to buildings)
for (var b = 0; b < buildings.length; b++) {
var buildingCenterX = buildings[b].x;
var buildingCenterY = buildings[b].y;
var expectedCustomerX = buildingCenterX + buildings[b].width / 2 * 2.5 + 15;
var expectedCustomerY = buildingCenterY + buildings[b].height / 2 * 2.5 + 15;
if (Math.abs(customer.x - expectedCustomerX) < 5 && Math.abs(customer.y - expectedCustomerY) < 5) {
building = buildings[b];
break;
}
}
if (building && !building.customerText) {
var customerText = new Text2("I'm here", {
size: 10,
fill: 0xffffff,
stroke: 0x000000,
strokeThickness: 3
});
customerText.anchor.set(0.5, 0.5);
customerText.x = 0;
customerText.y = -30;
building.addChild(customerText);
building.customerText = customerText;
}
customer.deliveryTarget = null;
currentOrder = order;
orders.push(order);
gameState = 'pickup';
// Create pickup arrow indicator
if (pickupArrow) {
pickupArrow.destroy();
}
pickupArrow = game.addChild(new ArrowIndicator(restaurant.x, restaurant.y, 0x00ff00, 'pickup'));
}
function createAIOrder() {
var availableRestaurants = restaurants.filter(function (r) {
return !r.hasOrder;
});
var availableCustomers = customers.filter(function (c) {
return !c.waitingForOrder;
});
if (availableRestaurants.length === 0 || availableCustomers.length === 0) {
return;
}
var restaurant = availableRestaurants[Math.floor(Math.random() * availableRestaurants.length)];
var customer = availableCustomers[Math.floor(Math.random() * availableCustomers.length)];
var order = worldContainer.addChild(new Order());
order.x = restaurant.x;
order.y = restaurant.y - 60;
order.restaurant = restaurant;
order.customer = customer;
order.claimed = false; // For AI to claim
// Set random order color and match customer color
var orderColor = orderColors[Math.floor(Math.random() * orderColors.length)];
order.setColor(orderColor);
customer.setOrderColor(orderColor);
restaurant.hasOrder = true;
restaurant.orderReady = true;
customer.waitingForOrder = true;
customer.orderTime = 0;
// Don't show AI order customers - only player's customer should be visible
customer.visible = false;
// Add "I'm here" text to the building that has the customer
var building = null;
// Find the building this customer belongs to (customers are positioned relative to buildings)
for (var b = 0; b < buildings.length; b++) {
var buildingCenterX = buildings[b].x;
var buildingCenterY = buildings[b].y;
var expectedCustomerX = buildingCenterX + buildings[b].width / 2 * 2.5 + 15;
var expectedCustomerY = buildingCenterY + buildings[b].height / 2 * 2.5 + 15;
if (Math.abs(customer.x - expectedCustomerX) < 5 && Math.abs(customer.y - expectedCustomerY) < 5) {
building = buildings[b];
break;
}
}
if (building && !building.customerText) {
var customerText = new Text2("I'm here", {
size: 10,
fill: 0xffffff,
stroke: 0x000000,
strokeThickness: 3
});
customerText.anchor.set(0.5, 0.5);
customerText.x = 0;
customerText.y = -30;
building.addChild(customerText);
building.customerText = customerText;
}
customer.deliveryTarget = null;
aiOrders.push(order);
}
function spawnPowerUp() {
if (powerUps.length >= 3) {
return;
}
var type = Math.random() < 0.6 ? 'health' : 'time';
var powerUp = worldContainer.addChild(new PowerUp(type));
// Spawn power-ups on road positions between buildings
var roadPositions = [{
x: 200,
y: 220
}, {
x: 335,
y: 220
}, {
x: 470,
y: 220
}, {
x: 605,
y: 220
}, {
x: 740,
y: 220
}, {
x: 200,
y: 355
}, {
x: 335,
y: 355
}, {
x: 470,
y: 355
}, {
x: 605,
y: 355
}, {
x: 740,
y: 355
}, {
x: 200,
y: 490
}, {
x: 335,
y: 490
}, {
x: 470,
y: 490
}, {
x: 605,
y: 490
}, {
x: 740,
y: 490
}, {
x: 200,
y: 625
}, {
x: 335,
y: 625
}, {
x: 470,
y: 625
}, {
x: 605,
y: 625
}, {
x: 740,
y: 625
}];
var roadPos = roadPositions[Math.floor(Math.random() * roadPositions.length)];
powerUp.x = roadPos.x;
powerUp.y = roadPos.y;
powerUps.push(powerUp);
}
function updateUI() {
if (playerRole === 'courier') {
healthBar.setText('Health: ' + Math.floor(health));
moneyText.setText('$' + money);
if (hasInsurance) {
var timeLeft = Math.max(0, (insuranceEndTime - Date.now()) / 1000);
insuranceStatus.setText('Insurance: ' + timeLeft.toFixed(0) + 's');
insuranceStatus.visible = true;
} else {
insuranceStatus.visible = false;
}
if (currentOrder && gameState === 'delivery') {
var timeLeft = Math.max(0, currentOrder.timeRemaining / 1000);
orderTimer.setText('Deliver in: ' + timeLeft.toFixed(1) + 's');
} else {
orderTimer.setText('');
}
} else if (playerRole === 'police') {
healthBar.setText('Police Money: $' + courier.money);
moneyText.setText('Player: $' + money);
orderTimer.setText('');
insuranceStatus.visible = false;
} else if (playerRole === 'vandal') {
healthBar.setText('Vandal Money: $' + courier.money);
moneyText.setText('Player: $' + money);
orderTimer.setText('');
insuranceStatus.visible = false;
}
}
function checkCollisions() {
// Check courier-restaurant collision for pickup
if (gameState === 'pickup' && currentOrder) {
if (courier.intersects(currentOrder.restaurant)) {
// Pick up order
LK.getSound('pickup').play();
courier.hasOrder = true;
gameState = 'delivery';
currentOrder.claimed = true; // Mark as claimed by player
currentOrder.x = courier.x;
currentOrder.y = courier.y - 40;
// Start delivery timer now that order is picked up
currentOrder.startDeliveryTimer(courier.speed);
// Remove pickup arrow and create delivery arrow
if (pickupArrow) {
pickupArrow.destroy();
pickupArrow = null;
}
if (deliveryArrow) {
deliveryArrow.destroy();
}
// Create delivery arrow pointing to customer location
deliveryArrow = game.addChild(new ArrowIndicator(currentOrder.customer.x, currentOrder.customer.y, 0xff0000, 'delivery'));
}
}
// Check courier-customer collision for delivery
if (gameState === 'delivery' && currentOrder && courier.hasOrder) {
// Check if courier intersects directly with the customer
if (courier.intersects(currentOrder.customer)) {
// Deliver order
LK.getSound('delivery').play();
completeDelivery();
}
}
// Check courier-powerup collisions
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
if (courier.intersects(powerUp)) {
LK.getSound('powerup').play();
if (powerUp.type === 'health') {
health = Math.min(100, health + 25);
} else if (powerUp.type === 'time') {
timeFreezeActive = true;
timeFreezeRemaining = 3000; // 3 seconds
}
powerUp.destroy();
powerUps.splice(i, 1);
}
}
}
function completeDelivery() {
if (!currentOrder) {
return;
}
var deliveryTime = currentOrder.timeLimit - currentOrder.timeRemaining;
var bonus = 0;
if (currentOrder.timeRemaining > 0) {
// Successful delivery - calculate price based on distance
var dx = currentOrder.restaurant.x - currentOrder.customer.x;
var dy = currentOrder.restaurant.y - currentOrder.customer.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var basePrice = Math.max(2, Math.min(10, Math.floor(distance / 100) + 2));
var timeBonus = Math.floor(currentOrder.timeRemaining / 1000);
bonus = basePrice + Math.min(timeBonus, 3); // Cap time bonus at $3
money += bonus;
// Flash green for success
LK.effects.flashObject(courier, 0x00ff00, 500);
} else {
// Late delivery - health penalty
health -= 20;
LK.effects.flashObject(courier, 0xff0000, 1000);
if (health <= 0) {
LK.showGameOver();
return;
}
}
// Clean up order
currentOrder.restaurant.hasOrder = false;
currentOrder.customer.waitingForOrder = false;
// Reset customer color to default
currentOrder.customer.setOrderColor(0x2196f3);
// Hide customer after delivery
currentOrder.customer.visible = false;
// Remove "I'm here" text from building
var customer = currentOrder.customer;
var building = null;
// Find the building this customer belongs to using the same logic as creation
for (var b = 0; b < buildings.length; b++) {
var buildingCenterX = buildings[b].x;
var buildingCenterY = buildings[b].y;
var expectedCustomerX = buildingCenterX + buildings[b].width / 2 * 2.5 + 15;
var expectedCustomerY = buildingCenterY + buildings[b].height / 2 * 2.5 + 15;
if (Math.abs(customer.x - expectedCustomerX) < 5 && Math.abs(customer.y - expectedCustomerY) < 5) {
building = buildings[b];
break;
}
}
if (building && building.customerText) {
building.customerText.destroy();
building.customerText = null;
}
currentOrder.destroy();
for (var i = orders.length - 1; i >= 0; i--) {
if (orders[i] === currentOrder) {
orders.splice(i, 1);
break;
}
}
currentOrder = null;
courier.hasOrder = false;
gameState = 'pickup';
// Clean up arrow indicators
if (deliveryArrow) {
deliveryArrow.destroy();
deliveryArrow = null;
}
// Create new order after short delay
LK.setTimeout(function () {
createNewOrder();
}, 1000);
}
// Create world container for scrolling
worldContainer = game.addChild(new Container());
// Scale up the world to zoom in more - this makes roads wider and couriers appear smaller
worldContainer.scaleX = 3;
worldContainer.scaleY = 3;
// Initialize game elements
createNeighborhood();
// Courier will be created after role selection
// Role selection handlers
courierButtonBg.down = function () {
selectRole('courier');
};
courierButton.down = function () {
selectRole('courier');
};
policeButtonBg.down = function () {
selectRole('police');
};
policeButton.down = function () {
selectRole('police');
};
vandalButtonBg.down = function () {
selectRole('vandal');
};
vandalButton.down = function () {
selectRole('vandal');
};
insuranceButton.down = function () {
buyInsurance();
};
godModeButton.down = function () {
toggleGodMode();
};
function selectRole(role) {
playerRole = role;
storage.playerRole = role;
roleSelectionContainer.visible = false;
if (role === 'courier') {
insuranceButton.visible = true;
courier = worldContainer.addChild(new Courier());
courier.x = 1000;
courier.y = 1400;
} else if (role === 'police') {
courier = worldContainer.addChild(new Police());
courier.x = 1000;
courier.y = 1400;
} else if (role === 'vandal') {
courier = worldContainer.addChild(new Vandal());
courier.x = 1000;
courier.y = 1400;
}
gameStarted = true;
if (role === 'courier') {
createNewOrder();
}
}
function buyInsurance() {
if (money >= 5 && !hasInsurance) {
money -= 5;
hasInsurance = true;
insuranceEndTime = Date.now() + 300000; // 5 minutes
insuranceButton.visible = false;
}
}
function toggleGodMode() {
godMode = !godMode;
godModeButton.setText('God Mode: ' + (godMode ? 'ON' : 'OFF'));
if (godMode) {
// Switch to god mode - zoom out to see full map
worldContainer.scaleX = godModeScale;
worldContainer.scaleY = godModeScale;
// Center camera on map center
cameraX = 1024 - 1024 / godModeScale;
cameraY = 1366 - 1366 / godModeScale;
} else {
// Switch back to normal mode
worldContainer.scaleX = normalScale;
worldContainer.scaleY = normalScale;
// Center camera back on courier if available
if (courier) {
cameraX = courier.x - 1024 / worldContainer.scaleX;
cameraY = courier.y - 1366 / worldContainer.scaleY;
}
}
}
// Touch controls
var targetX = 1000;
var targetY = 1400;
game.down = function (x, y, obj) {
if (godMode) {
// In god mode, don't set movement target, just allow camera control
return;
}
// Convert screen coordinates to world coordinates accounting for scale
targetX = x / worldContainer.scaleX + cameraX;
targetY = y / worldContainer.scaleY + cameraY;
};
game.move = function (x, y, obj) {
if (godMode) {
// In god mode, don't set movement target, just allow camera control
return;
}
// Convert screen coordinates to world coordinates accounting for scale
targetX = x / worldContainer.scaleX + cameraX;
targetY = y / worldContainer.scaleY + cameraY;
};
// Create NPCs with unified system
// Create 8 police - spawn from center of map
for (var i = 0; i < 8; i++) {
var policeNPC = worldContainer.addChild(new NPCPolice());
// Spawn police from center area of the map
var centerX = 1024;
var centerY = 1366;
var radius = 150;
var angle = i / 8 * Math.PI * 2;
policeNPC.x = centerX + Math.cos(angle) * radius;
policeNPC.y = centerY + Math.sin(angle) * radius;
policeNPC.npcType = 'police';
policeNPC.patrol.x = 200 + Math.random() * 1600;
policeNPC.patrol.y = 200 + Math.random() * 2300;
npcs.push(policeNPC);
}
// Create 6 vandals - spawn from 4 corners of map
var cornerPositions = [{
x: 100,
y: 200
},
// Top-left
{
x: 1900,
y: 200
},
// Top-right
{
x: 100,
y: 2500
},
// Bottom-left
{
x: 1900,
y: 2500
},
// Bottom-right
{
x: 100,
y: 1350
},
// Left middle
{
x: 1900,
y: 1350
} // Right middle
];
for (var i = 0; i < 6; i++) {
var vandalNPC = worldContainer.addChild(new NPCVandal());
var pos = cornerPositions[i];
vandalNPC.x = pos.x;
vandalNPC.y = pos.y;
vandalNPC.npcType = 'vandal';
vandalNPC.patrol.x = 200 + Math.random() * 1600;
vandalNPC.patrol.y = 200 + Math.random() * 2300;
npcs.push(vandalNPC);
}
// Create NPC couriers - spawn around buildings
for (var i = 0; i < 10; i++) {
var courierNPC = worldContainer.addChild(new NPCCourier());
// Spawn around buildings randomly
if (buildings.length > 0) {
var building = buildings[Math.floor(Math.random() * buildings.length)];
courierNPC.x = building.x + (Math.random() - 0.5) * 100;
courierNPC.y = building.y + (Math.random() - 0.5) * 100;
} else {
courierNPC.x = 200 + Math.random() * 1600;
courierNPC.y = 200 + Math.random() * 2300;
}
courierNPC.npcType = 'courier';
courierNPC.patrol.x = 200 + Math.random() * 1600;
courierNPC.patrol.y = 200 + Math.random() * 2300;
npcs.push(courierNPC);
}
// Create initial AI orders immediately and more frequently
LK.setTimeout(function () {
createAIOrder();
}, 100);
LK.setTimeout(function () {
createAIOrder();
}, 300);
LK.setTimeout(function () {
createAIOrder();
}, 600);
LK.setTimeout(function () {
createAIOrder();
}, 900);
// Spawn initial power-up
LK.setTimeout(function () {
spawnPowerUp();
}, 3000);
game.update = function () {
// Only run game logic if role is selected
if (!gameStarted || !courier) {
return;
}
// Update insurance status
if (hasInsurance && Date.now() > insuranceEndTime) {
hasInsurance = false;
if (playerRole === 'courier') {
insuranceButton.visible = true;
}
}
// Move courier toward target
courier.moveToward(targetX, targetY);
// Update camera system
if (godMode) {
// In god mode, allow free camera movement or keep centered on map
// Keep camera centered on the map center for full view
cameraX = 1024 - 1024 / worldContainer.scaleX;
cameraY = 1366 - 1366 / worldContainer.scaleY;
} else {
// Normal mode - follow courier
cameraX = courier.x - 1024 / worldContainer.scaleX; // Center courier horizontally accounting for scale
cameraY = courier.y - 1366 / worldContainer.scaleY; // Center courier vertically accounting for scale
}
// Apply camera offset to world container
worldContainer.x = -cameraX * worldContainer.scaleX;
worldContainer.y = -cameraY * worldContainer.scaleY;
// Update time freeze
if (timeFreezeActive) {
timeFreezeRemaining -= 16.67;
if (timeFreezeRemaining <= 0) {
timeFreezeActive = false;
}
}
// Update current order timer (if not frozen)
if (currentOrder && !timeFreezeActive) {
// Update time limit based on courier distance if picked up
if (courier.hasOrder && gameState === 'delivery') {
currentOrder.updateTimeLimit(courier.x, courier.y);
}
currentOrder.update();
// Move order with courier if picked up
if (courier.hasOrder && gameState === 'delivery') {
currentOrder.x = courier.x;
currentOrder.y = courier.y - 40;
}
// No delivery target flashing needed anymore - buildings show "I'm here" text instead
// Check for failed delivery
if (currentOrder.timeRemaining <= 0 && gameState === 'delivery') {
completeDelivery();
}
}
// Update customers
for (var i = 0; i < customers.length; i++) {
customers[i].update();
}
// Update all NPCs
for (var i = 0; i < npcs.length; i++) {
npcs[i].update();
}
// Create new AI orders periodically - more frequently and higher limit
if (LK.ticks % 90 === 0 && aiOrders.length < 12) {
// Every 1.5 seconds, maintain up to 12 AI orders
createAIOrder();
}
// Create additional AI orders periodically
if (LK.ticks % 90 === 0 && aiOrders.length < 8) {
createAIOrder();
}
// Update power-ups
for (var i = powerUps.length - 1; i >= 0; i--) {
var powerUp = powerUps[i];
powerUp.update();
if (powerUp.lifeTime >= powerUp.maxLifeTime) {
powerUp.destroy();
powerUps.splice(i, 1);
}
}
// Spawn new power-ups occasionally
if (LK.ticks % 600 === 0) {
// Every 10 seconds
spawnPowerUp();
}
// Check collisions
checkCollisions();
// Update arrow indicators
if (pickupArrow) {
pickupArrow.update();
}
if (deliveryArrow) {
deliveryArrow.update();
}
// Update UI
updateUI();
// Game is now endless - no win condition
}; ===================================================================
--- original.js
+++ change.js
@@ -140,57 +140,42 @@
});
courierGraphics.tint = 0xff5722;
self.speed = 2;
self.detectionRadius = 20;
+ self.isMoving = false;
self.hasInsurance = false;
- self.patrol = {
- x: 1024,
- y: 1366
- };
self.lastX = self.x;
self.lastY = self.y;
self.lastSeenPolice = false;
self.lastSeenVandal = false;
- self.thinkTimer = 0;
- self.moveToward = function (targetX, targetY) {
- var dx = targetX - self.x;
- var dy = targetY - self.y;
+ self.startRoaming = function () {
+ if (self.isMoving) return;
+ self.isMoving = true;
+ var newX = 200 + Math.random() * 1600;
+ var newY = 200 + Math.random() * 2300;
+ var dx = newX - self.x;
+ var dy = newY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance > 5) {
- var newX = self.x + dx / distance * self.speed;
- var newY = self.y + dy / distance * self.speed;
- var canMove = true;
- // Check collision with buildings
- for (var i = 0; i < buildings.length; i++) {
- var building = buildings[i];
- var tempCourier = {
- x: newX,
- y: newY,
- width: 15,
- height: 15
- };
- if (tempCourier.x - tempCourier.width / 2 < building.x + building.width / 2 && tempCourier.x + tempCourier.width / 2 > building.x - building.width / 2 && tempCourier.y - tempCourier.height / 2 < building.y + building.height / 2 && tempCourier.y + tempCourier.height / 2 > building.y - building.height / 2) {
- canMove = false;
- break;
- }
+ var duration = distance * 50; // Adjust speed by changing multiplier
+ tween(self, {
+ x: newX,
+ y: newY
+ }, {
+ duration: duration,
+ easing: tween.linear,
+ onFinish: function onFinish() {
+ self.isMoving = false;
+ // Start next movement after a short pause
+ LK.setTimeout(function () {
+ self.startRoaming();
+ }, 500 + Math.random() * 1000);
}
- if (canMove) {
- self.x = newX;
- self.y = newY;
- }
- }
+ });
};
self.update = function () {
- self.thinkTimer += 16.67;
- if (self.thinkTimer >= 100) {
- self.thinkTimer = 0;
- // Get new patrol point if reached current one
- if (Math.abs(self.x - self.patrol.x) < 30 && Math.abs(self.y - self.patrol.y) < 30) {
- self.patrol.x = 200 + Math.random() * 1600;
- self.patrol.y = 200 + Math.random() * 2300;
- }
- // Move around buildings
- self.moveToward(self.patrol.x, self.patrol.y);
+ // Start roaming if not already moving
+ if (!self.isMoving) {
+ self.startRoaming();
}
self.lastX = self.x;
self.lastY = self.y;
};
@@ -203,43 +188,36 @@
anchorY: 0.5
});
self.speed = 3;
self.detectionRadius = 20;
- self.patrol = {
- x: 1024,
- y: 1366
- };
self.target = null;
+ self.isMoving = false;
self.lastX = self.x;
self.lastY = self.y;
self.thinkTimer = 0;
- self.moveToward = function (targetX, targetY) {
- var dx = targetX - self.x;
- var dy = targetY - self.y;
+ self.startRoaming = function () {
+ if (self.isMoving) return;
+ self.isMoving = true;
+ var newX = 200 + Math.random() * 1600;
+ var newY = 200 + Math.random() * 2300;
+ var dx = newX - self.x;
+ var dy = newY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance > 5) {
- var newX = self.x + dx / distance * self.speed;
- var newY = self.y + dy / distance * self.speed;
- var canMove = true;
- // Check collision with buildings
- for (var i = 0; i < buildings.length; i++) {
- var building = buildings[i];
- var tempPolice = {
- x: newX,
- y: newY,
- width: 15,
- height: 15
- };
- if (tempPolice.x - tempPolice.width / 2 < building.x + building.width / 2 && tempPolice.x + tempPolice.width / 2 > building.x - building.width / 2 && tempPolice.y - tempPolice.height / 2 < building.y + building.height / 2 && tempPolice.y + tempPolice.height / 2 > building.y - building.height / 2) {
- canMove = false;
- break;
- }
+ var duration = distance * 40; // Faster than couriers
+ tween(self, {
+ x: newX,
+ y: newY
+ }, {
+ duration: duration,
+ easing: tween.linear,
+ onFinish: function onFinish() {
+ self.isMoving = false;
+ // Start next movement after a short pause
+ LK.setTimeout(function () {
+ self.startRoaming();
+ }, 300 + Math.random() * 800);
}
- if (canMove) {
- self.x = newX;
- self.y = newY;
- }
- }
+ });
};
self.update = function () {
self.thinkTimer += 16.67;
if (self.thinkTimer >= 100) {
@@ -253,8 +231,14 @@
var dy = npc.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.target = npc;
+ // Stop current movement and chase target
+ tween.stop(self, {
+ x: true,
+ y: true
+ });
+ self.isMoving = false;
break;
}
}
}
@@ -267,8 +251,14 @@
var dy = npc.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.target = npc;
+ // Stop current movement and chase target
+ tween.stop(self, {
+ x: true,
+ y: true
+ });
+ self.isMoving = false;
break;
}
}
}
@@ -279,21 +269,41 @@
var dy = courier.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
self.target = courier;
+ // Stop current movement and chase target
+ tween.stop(self, {
+ x: true,
+ y: true
+ });
+ self.isMoving = false;
}
}
}
// Move behavior
if (self.target) {
- self.moveToward(self.target.x, self.target.y);
+ // Chase target with tween
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance > 5) {
+ tween.stop(self, {
+ x: true,
+ y: true
+ });
+ tween(self, {
+ x: self.target.x,
+ y: self.target.y
+ }, {
+ duration: distance * 30,
+ easing: tween.linear
+ });
+ }
} else {
- // Get new patrol point if reached current one
- if (Math.abs(self.x - self.patrol.x) < 30 && Math.abs(self.y - self.patrol.y) < 30) {
- self.patrol.x = 200 + Math.random() * 1600;
- self.patrol.y = 200 + Math.random() * 2300;
+ // Start roaming if not already moving
+ if (!self.isMoving) {
+ self.startRoaming();
}
- self.moveToward(self.patrol.x, self.patrol.y);
}
self.lastX = self.x;
self.lastY = self.y;
};
@@ -306,91 +316,99 @@
anchorY: 0.5
});
self.speed = 2;
self.detectionRadius = 20;
- self.patrol = {
- x: 1024,
- y: 1366
- };
self.fleeing = false;
+ self.isMoving = false;
self.lastX = self.x;
self.lastY = self.y;
self.thinkTimer = 0;
- self.moveToward = function (targetX, targetY) {
- var dx = targetX - self.x;
- var dy = targetY - self.y;
+ self.startRoaming = function () {
+ if (self.isMoving || self.fleeing) return;
+ self.isMoving = true;
+ var newX = 200 + Math.random() * 1600;
+ var newY = 200 + Math.random() * 2300;
+ var dx = newX - self.x;
+ var dy = newY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
- if (distance > 5) {
- var newX = self.x + dx / distance * self.speed;
- var newY = self.y + dy / distance * self.speed;
- var canMove = true;
- // Check collision with buildings
- for (var i = 0; i < buildings.length; i++) {
- var building = buildings[i];
- var tempVandal = {
- x: newX,
- y: newY,
- width: 15,
- height: 15
- };
- if (tempVandal.x - tempVandal.width / 2 < building.x + building.width / 2 && tempVandal.x + tempVandal.width / 2 > building.x - building.width / 2 && tempVandal.y - tempVandal.height / 2 < building.y + building.height / 2 && tempVandal.y + tempVandal.height / 2 > building.y - building.height / 2) {
- canMove = false;
- break;
- }
+ var duration = distance * 60; // Slower than police
+ tween(self, {
+ x: newX,
+ y: newY
+ }, {
+ duration: duration,
+ easing: tween.linear,
+ onFinish: function onFinish() {
+ self.isMoving = false;
+ // Start next movement after a short pause
+ LK.setTimeout(function () {
+ self.startRoaming();
+ }, 800 + Math.random() * 1200);
}
- if (canMove) {
- self.x = newX;
- self.y = newY;
+ });
+ };
+ self.flee = function (fromX, fromY) {
+ self.fleeing = true;
+ tween.stop(self, {
+ x: true,
+ y: true
+ });
+ self.isMoving = false;
+ var dx = self.x - fromX;
+ var dy = self.y - fromY;
+ var fleeX = self.x + dx * 5;
+ var fleeY = self.y + dy * 5;
+ // Keep within bounds
+ fleeX = Math.max(100, Math.min(1900, fleeX));
+ fleeY = Math.max(200, Math.min(2500, fleeY));
+ tween(self, {
+ x: fleeX,
+ y: fleeY
+ }, {
+ duration: 2000,
+ easing: tween.easeOut,
+ onFinish: function onFinish() {
+ self.fleeing = false;
+ LK.setTimeout(function () {
+ self.startRoaming();
+ }, 1000);
}
- }
+ });
};
self.update = function () {
self.thinkTimer += 16.67;
if (self.thinkTimer >= 100) {
self.thinkTimer = 0;
- self.fleeing = false;
+ var shouldFlee = false;
// Look for police
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (npc.npcType === 'police') {
var dx = npc.x - self.x;
var dy = npc.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
- self.fleeing = true;
- // Run in opposite direction
- self.patrol.x = self.x - dx * 10;
- self.patrol.y = self.y - dy * 10;
- // Keep within bounds
- self.patrol.x = Math.max(100, Math.min(1900, self.patrol.x));
- self.patrol.y = Math.max(200, Math.min(2500, self.patrol.y));
+ self.flee(npc.x, npc.y);
+ shouldFlee = true;
break;
}
}
}
// Check player police
- if (!self.fleeing && playerRole === 'police' && courier) {
+ if (!shouldFlee && playerRole === 'police' && courier) {
var dx = courier.x - self.x;
var dy = courier.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance <= self.detectionRadius) {
- self.fleeing = true;
- // Run in opposite direction
- self.patrol.x = self.x - dx * 10;
- self.patrol.y = self.y - dy * 10;
- // Keep within bounds
- self.patrol.x = Math.max(100, Math.min(1900, self.patrol.x));
- self.patrol.y = Math.max(200, Math.min(2500, self.patrol.y));
+ self.flee(courier.x, courier.y);
+ shouldFlee = true;
}
}
- // Get new patrol point if not fleeing and reached current one
- if (!self.fleeing && Math.abs(self.x - self.patrol.x) < 30 && Math.abs(self.y - self.patrol.y) < 30) {
- self.patrol.x = 200 + Math.random() * 1600;
- self.patrol.y = 200 + Math.random() * 2300;
- }
}
- // Move behavior
- self.moveToward(self.patrol.x, self.patrol.y);
+ // Start roaming if not fleeing and not already moving
+ if (!self.fleeing && !self.isMoving) {
+ self.startRoaming();
+ }
self.lastX = self.x;
self.lastY = self.y;
};
return self;
ıts a courier scooter its looking behind . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
police car with flashing lights. In-Game asset. 2d. High contrast. No shadows
vandal riding dirt bike. In-Game asset. High contrast. No shadows
health powerup. In-Game asset. 2d. High contrast. No shadows
pizza boxes. In-Game asset. 2d. High contrast. No shadows
extra time powerup. In-Game asset. 2d. High contrast. No shadows
restaurant shop. In-Game asset. High contrast. No shadows
town house. In-Game asset. 2d. High contrast. No shadows
a man waiting outside. In-Game asset. 2d. High contrast. No shadows
arrow. In-Game asset. 2d. High contrast. No shadows
fence. In-Game asset. 2d. High contrast. No shadows