User prompt
Let's make the game a bit more agar.io-style, with the courier in the center and the map scrolling. And arrows indicate the pickup and delivery points. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Let's make the game a bit more agar.io-style, with the courier in the center and the map scrolling. And arrows indicate the pickup and delivery points. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
fill all screen with shops and buildings. And change colours orders and customers. Customers colors match the their order color ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
delete gray bars on screen
User prompt
please add another ai couriers and at the start may be more than 1 customer order different foods. And fix the building and shops locations. emptied first row because player cant reach there ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Connect building entrances with buildings. Have couriers pick up and deliver packages. If couriers intersect with each other or the player, they can change their path or turn back and take an alternative route.
User prompt
Please direct the couriers' paths so they don't intersect. They also can't pick up or deliver packages. Please also adjust the height of building entrances to 1px.
User prompt
Other AI couriers are frozen. Could you please take a look at them? They are not doing their job.
User prompt
The neighborhood should be infinite, like going to the far left of the screen and reaching the far right. Like in the game Snake. Packages should keep appearing. Each time a package is completed, a new one is created.
User prompt
At the beginning of the game, there can be multiple packages at the same time, for example, 5 packages. Match the colors of these packages and the customer. Other couriers should not be chasing the same package.
User prompt
Cars should be able to enter the neighborhood. Buildings and shops should have entrances. Couriers should be required to come to the asphalt in front of the entrance to pick up and make deliveries. The red dot representing customers should be located exactly in the middle of the building.
User prompt
Other couriers can only move vertically or horizontally between buildings. They cannot travel diagonally. Their speeds also vary. They can create traffic congestion. Add cars based on the money earned outside of the couriers. Start with 10 cars and increase to 20 cars until you reach $200. Cars, couriers, and the player cannot drive over each other.
User prompt
The size of the buildings should be smaller and the roads should be wider.
User prompt
The order is completed when it arrives at the customer's premises.
User prompt
Buildings should be arranged in an orderly manner. All buildings should have roads around them and these roads should be connected to each other.
User prompt
The neighborhood layout should consist of a 4-block park in the middle and buildings arranged in a non-circular pattern around it.
User prompt
To avoid couriers getting stuck in buildings, designate wider routes and have couriers follow them to reach the customer. They should choose which route to use. However, they must be able to reach the customer. Furthermore, if a package arrives anywhere in the store, it should be picked up and delivered to the customer's location within the building.
User prompt
Couriers do not wander around randomly, they just follow the routes and pick up and deliver the package.
User prompt
The purpose of other couriers is to pick up the order and deliver it to the customer.
User prompt
Customers should only be visible when the order arrives. If the courier doesn't pick up the order, it should disappear after a while. And then there will be new orders and new customers at different locations. Other couriers will be randomly passing through the streets, and they will be competing with us.
User prompt
The package's time starts when the courier receives the package.
User prompt
Please have buildings and shops line up in a circle. Layer by layer, covering the entire screen, with these circles gradually taking on more of the screen shape with each layer. Roads should be asphalt-colored, with no buildings overlapping. Shops and houses should change with each new game.
User prompt
the shops are need to be in a row as circular. And every new game will be changed shops and houses randomly.
User prompt
okay, we need to change the map. I want to make a neighbourhood. Middle of the screen have a park. And houses like a circular plan. and 1 outer layer is circular and have another houses and shops randomly. And i dont want to see gray squares on the screen.
User prompt
please show buildings are with dark blue, shops are orange, order is yellow. And customers must be in buildings. And please allow minimum 25 seconds per order completing.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AICourier = Container.expand(function () {
var self = Container.call(this);
var courierGraphics = self.attachAsset('courier', {
anchorX: 0.5,
anchorY: 0.5
});
courierGraphics.tint = 0xff4444; // Red tint to distinguish from player
self.speed = 6;
self.targetX = 1024;
self.targetY = 1366;
self.changeTargetTimer = 0;
self.hasOrder = false;
self.targetRestaurant = null;
self.targetCustomer = null;
self.currentWaypoint = 0;
self.routeWaypoints = [];
self.currentPath = [];
self.pathIndex = 0;
self.update = function () {
// Check for orders to pick up if not carrying one
if (!self.hasOrder && currentOrders.length > 0) {
// Find an unclaimed order that no other courier is chasing
var availableOrder = null;
for (var orderIdx = 0; orderIdx < currentOrders.length; orderIdx++) {
var order = currentOrders[orderIdx];
if (!order.isBeingChased && order.timerStarted === false) {
availableOrder = order;
break;
}
}
if (availableOrder) {
// Claim this order
availableOrder.isBeingChased = true;
self.targetOrder = availableOrder;
// Check if can pick up from restaurant entrance area
var restaurant = availableOrder.restaurant;
var restaurantDistance = Math.sqrt(Math.pow(restaurant.entranceX - self.x, 2) + Math.pow(restaurant.entranceY - self.y, 2));
if (restaurantDistance < 60) {
// Pick up the order
self.hasOrder = true;
self.targetCustomer = availableOrder.customer;
self.targetRestaurant = availableOrder.restaurant;
// Start the package timer
availableOrder.timerStarted = true;
// Move order with AI courier
availableOrder.x = self.x;
availableOrder.y = self.y - 40;
// Clear current path and find new path to customer
self.currentPath = findPath(self.x, self.y, self.targetCustomer.x, self.targetCustomer.y);
self.pathIndex = 0;
} else {
// Find path to restaurant entrance if don't have one or reached end
if (self.currentPath.length === 0 || self.pathIndex >= self.currentPath.length) {
self.currentPath = findPath(self.x, self.y, restaurant.entranceX, restaurant.entranceY);
self.pathIndex = 0;
}
}
}
} else if (self.hasOrder && self.targetCustomer && self.targetOrder) {
// AI courier is delivering - check if reached customer building entrance
// Find the building that contains this customer
var customerBuilding = null;
for (var i = 0; i < buildings.length; i++) {
if (buildings[i].x === self.targetCustomer.x && buildings[i].y === self.targetCustomer.y) {
customerBuilding = buildings[i];
break;
}
}
var customerDistance = Infinity;
if (customerBuilding) {
customerDistance = Math.sqrt(Math.pow(customerBuilding.entranceX - self.x, 2) + Math.pow(customerBuilding.entranceY - self.y, 2));
}
if (customerDistance < 60) {
// Deliver the order
LK.getSound('delivery').play();
self.hasOrder = false;
self.targetCustomer = null;
self.targetRestaurant = null;
self.currentPath = [];
self.pathIndex = 0;
// Complete the delivery (similar to player delivery)
if (self.targetOrder) {
// Order completed successfully - AI gets the delivery done
var deliveryTime = self.targetOrder.timeLimit - self.targetOrder.timeRemaining;
if (self.targetOrder.timeRemaining > 0) {
// Successful delivery by AI courier
var timeBonus = Math.floor(self.targetOrder.timeRemaining / 1000);
var bonus = 5 + timeBonus; // AI gets smaller bonus than player
money += bonus;
}
self.targetOrder.restaurant.hasOrder = false;
self.targetOrder.customer.waitingForOrder = false;
self.targetOrder.customer.visible = false;
if (self.targetOrder.customer.deliveryTarget) {
tween.stop(self.targetOrder.customer.deliveryTarget);
self.targetOrder.customer.deliveryTarget.destroy();
self.targetOrder.customer.deliveryTarget = null;
}
self.targetOrder.destroy();
for (var i = orders.length - 1; i >= 0; i--) {
if (orders[i] === self.targetOrder) {
orders.splice(i, 1);
break;
}
}
// Remove from currentOrders array
for (var i = currentOrders.length - 1; i >= 0; i--) {
if (currentOrders[i] === self.targetOrder) {
currentOrders.splice(i, 1);
break;
}
}
self.targetOrder = null;
// Create new order immediately after delivery
createNewOrder();
}
} else {
// Find path to customer building entrance if don't have one or reached end
if (self.currentPath.length === 0 || self.pathIndex >= self.currentPath.length) {
// Find the building that contains this customer
var customerBuilding = null;
for (var i = 0; i < buildings.length; i++) {
if (buildings[i].x === self.targetCustomer.x && buildings[i].y === self.targetCustomer.y) {
customerBuilding = buildings[i];
break;
}
}
if (customerBuilding) {
self.currentPath = findPath(self.x, self.y, customerBuilding.entranceX, customerBuilding.entranceY);
} else {
self.currentPath = findPath(self.x, self.y, self.targetCustomer.x, self.targetCustomer.y);
}
self.pathIndex = 0;
}
}
} else {
// When not carrying order, patrol using route network
if (self.currentPath.length === 0 || self.pathIndex >= self.currentPath.length) {
// Pick a random route node to patrol to
if (routeNodes.length > 0) {
var randomNode = routeNodes[Math.floor(Math.random() * routeNodes.length)];
self.currentPath = findPath(self.x, self.y, randomNode.x, randomNode.y);
self.pathIndex = 0;
}
}
}
// Follow current path
if (self.currentPath.length > 0 && self.pathIndex < self.currentPath.length) {
var targetNode = self.currentPath[self.pathIndex];
self.targetX = targetNode.x;
self.targetY = targetNode.y;
// Check if reached current path node
var nodeDistance = Math.sqrt(Math.pow(targetNode.x - self.x, 2) + Math.pow(targetNode.y - self.y, 2));
if (nodeDistance < 60) {
self.pathIndex++;
}
} else {
// No path or reached end - pick a random nearby target to keep moving
if (routeNodes.length > 0) {
var randomNode = routeNodes[Math.floor(Math.random() * routeNodes.length)];
self.targetX = randomNode.x;
self.targetY = randomNode.y;
} else {
self.targetX = self.x;
self.targetY = self.y;
}
}
// Move toward target using simple movement with collision detection
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
// Check collisions before moving
var canMove = true;
// Check collision with cars
for (var i = 0; i < cars.length; i++) {
var carDx = Math.abs(newX - cars[i].x);
var carDy = Math.abs(newY - cars[i].y);
if (carDx < 40 && carDy < 40) {
canMove = false;
break;
}
}
// Check collision with player courier
var playerDx = Math.abs(newX - courier.x);
var playerDy = Math.abs(newY - courier.y);
if (playerDx < 40 && playerDy < 40) {
canMove = false;
}
// Check collision with other AI couriers
for (var i = 0; i < aiCouriers.length; i++) {
if (aiCouriers[i] === self) continue;
var aiDx = Math.abs(newX - aiCouriers[i].x);
var aiDy = Math.abs(newY - aiCouriers[i].y);
if (aiDx < 40 && aiDy < 40) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
// World wrapping - if AI courier goes off left edge, appear on right edge
if (self.x < worldLeftBound) {
self.x = worldRightBound;
} else if (self.x > worldRightBound) {
self.x = worldLeftBound;
}
self.stuckTimer = 0;
} else {
// If stuck, try alternative movement
if (!self.stuckTimer) self.stuckTimer = 0;
self.stuckTimer += 16.67;
if (self.stuckTimer > 1000) {
// If stuck for more than 1 second
// Try moving in a different direction
var randomAngle = Math.random() * Math.PI * 2;
var moveDistance = 20;
self.x += Math.cos(randomAngle) * moveDistance;
self.y += Math.sin(randomAngle) * moveDistance;
self.stuckTimer = 0;
self.currentPath = []; // Clear current path to force new pathfinding
}
}
}
// Update order position if carrying one
if (self.hasOrder && self.targetOrder) {
self.targetOrder.x = self.x;
self.targetOrder.y = self.y - 40;
}
};
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('courier', {
anchorX: 0.5,
anchorY: 0.5
});
carGraphics.tint = 0x333333; // Dark gray for cars
carGraphics.scaleX = 0.8;
carGraphics.scaleY = 0.8;
self.speed = 2 + Math.random() * 4; // Speed between 2-6
self.direction = Math.random() < 0.5 ? 'horizontal' : 'vertical';
self.movingPositive = Math.random() < 0.5; // true = right/down, false = left/up
self.targetX = self.x;
self.targetY = self.y;
self.stuckTimer = 0;
self.maxStuckTime = 2000; // 2 seconds before finding new path
self.update = function () {
var oldX = self.x;
var oldY = self.y;
// Move only horizontally or vertically
if (self.direction === 'horizontal') {
if (self.movingPositive) {
self.targetX = self.x + self.speed;
} else {
self.targetX = self.x - self.speed;
}
// No boundary reversal needed - world wraps infinitely
} else {
if (self.movingPositive) {
self.targetY = self.y + self.speed;
} else {
self.targetY = self.y - self.speed;
}
// Reverse direction at boundaries
if (self.y > 2550 || self.y < 182) {
self.movingPositive = !self.movingPositive;
}
}
// Check for collisions before moving
var canMove = true;
var testX = self.targetX;
var testY = self.targetY;
// Check collision with other cars
for (var i = 0; i < cars.length; i++) {
if (cars[i] === self) continue;
var dx = Math.abs(testX - cars[i].x);
var dy = Math.abs(testY - cars[i].y);
if (dx < 50 && dy < 50) {
canMove = false;
break;
}
}
// Check collision with couriers
var courierDx = Math.abs(testX - courier.x);
var courierDy = Math.abs(testY - courier.y);
if (courierDx < 50 && courierDy < 50) {
canMove = false;
}
// Check collision with AI couriers
for (var i = 0; i < aiCouriers.length; i++) {
var aiDx = Math.abs(testX - aiCouriers[i].x);
var aiDy = Math.abs(testY - aiCouriers[i].y);
if (aiDx < 50 && aiDy < 50) {
canMove = false;
break;
}
}
if (canMove) {
self.x = testX;
self.y = testY;
self.stuckTimer = 0;
// World wrapping - if car goes off left edge, appear on right edge
if (self.x < worldLeftBound) {
self.x = worldRightBound;
} else if (self.x > worldRightBound) {
self.x = worldLeftBound;
}
} else {
// If stuck for too long, change direction
self.stuckTimer += 16.67;
if (self.stuckTimer >= self.maxStuckTime) {
self.movingPositive = !self.movingPositive;
self.stuckTimer = 0;
}
}
};
return self;
});
var Courier = Container.expand(function () {
var self = Container.call(this);
var courierGraphics = self.attachAsset('courier', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.hasOrder = false;
self.targetRestaurant = null;
self.targetCustomer = null;
self.currentPath = [];
self.pathIndex = 0;
self.lastTargetX = 0;
self.lastTargetY = 0;
self.moveToward = function (targetX, targetY) {
// Check if target changed significantly - recalculate path
var targetChanged = Math.abs(targetX - self.lastTargetX) > 50 || Math.abs(targetY - self.lastTargetY) > 50;
if (targetChanged || self.currentPath.length === 0 || self.pathIndex >= self.currentPath.length) {
self.currentPath = findPath(self.x, self.y, targetX, targetY);
self.pathIndex = 0;
self.lastTargetX = targetX;
self.lastTargetY = targetY;
}
// Follow path if available
if (self.currentPath.length > 0 && self.pathIndex < self.currentPath.length) {
var pathTarget = self.currentPath[self.pathIndex];
var dx = pathTarget.x - self.x;
var dy = pathTarget.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if reached current path node
if (distance < 60) {
self.pathIndex++;
if (self.pathIndex < self.currentPath.length) {
pathTarget = self.currentPath[self.pathIndex];
dx = pathTarget.x - self.x;
dy = pathTarget.y - self.y;
distance = Math.sqrt(dx * dx + dy * dy);
}
}
if (distance > 10 && self.pathIndex < self.currentPath.length) {
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
// Check collisions before moving
var canMove = true;
// Check collision with cars
for (var i = 0; i < cars.length; i++) {
var carDx = Math.abs(newX - cars[i].x);
var carDy = Math.abs(newY - cars[i].y);
if (carDx < 50 && carDy < 50) {
canMove = false;
break;
}
}
// Check collision with AI couriers
for (var i = 0; i < aiCouriers.length; i++) {
var aiDx = Math.abs(newX - aiCouriers[i].x);
var aiDy = Math.abs(newY - aiCouriers[i].y);
if (aiDx < 50 && aiDy < 50) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
// World wrapping - if courier goes off left edge, appear on right edge
if (self.x < worldLeftBound) {
self.x = worldRightBound;
} else if (self.x > worldRightBound) {
self.x = worldLeftBound;
}
}
}
} else {
// Direct movement as fallback
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
var newX = self.x + dx / distance * self.speed;
var newY = self.y + dy / distance * self.speed;
// Check collisions before moving
var canMove = true;
// Check collision with cars
for (var i = 0; i < cars.length; i++) {
var carDx = Math.abs(newX - cars[i].x);
var carDy = Math.abs(newY - cars[i].y);
if (carDx < 50 && carDy < 50) {
canMove = false;
break;
}
}
// Check collision with AI couriers
for (var i = 0; i < aiCouriers.length; i++) {
var aiDx = Math.abs(newX - aiCouriers[i].x);
var aiDy = Math.abs(newY - aiCouriers[i].y);
if (aiDx < 50 && aiDy < 50) {
canMove = false;
break;
}
}
if (canMove) {
self.x = newX;
self.y = newY;
// World wrapping - if courier goes off left edge, appear on right edge
if (self.x < worldLeftBound) {
self.x = worldRightBound;
} else if (self.x > worldRightBound) {
self.x = worldLeftBound;
}
}
}
}
};
return self;
});
var Customer = Container.expand(function (color) {
var self = Container.call(this);
var customerGraphics = self.attachAsset('customer', {
anchorX: 0.5,
anchorY: 0.5
});
// Set customer color
self.customerColor = color || 0x2196f3;
customerGraphics.tint = self.customerColor;
self.waitingForOrder = false;
self.orderTime = 0;
self.maxWaitTime = 10000; // 10 seconds
self.deliveryTarget = null;
self.visible = false; // Start invisible
self.update = function () {
if (self.waitingForOrder) {
self.orderTime += 16.67; // Approximate ms per frame at 60fps
}
};
return self;
});
var Order = Container.expand(function (color) {
var self = Container.call(this);
var orderGraphics = self.attachAsset('order', {
anchorX: 0.5,
anchorY: 0.5
});
// Set order color
self.orderColor = color || 0xffff00;
orderGraphics.tint = self.orderColor;
self.restaurant = null;
self.customer = null;
self.timeLimit = 25000; // 25 seconds
self.timeRemaining = self.timeLimit;
self.timerStarted = false; // Timer doesn't start until courier picks up
self.lifeTime = 0; // Track how long order has existed
self.maxLifeTime = 15000; // Order expires after 15 seconds if not picked up
self.isBeingChased = false; // Track if an AI courier is chasing this order
self.update = function () {
if (self.timerStarted && self.timeRemaining > 0) {
self.timeRemaining -= 16.67;
}
// Track order lifetime for auto-expiry
if (!self.timerStarted) {
self.lifeTime += 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 aiCouriers = [];
var cars = [];
var restaurants = [];
var customers = [];
var orders = [];
var powerUps = [];
var buildings = [];
var health = 100;
var money = 0;
var currentOrder = null;
var currentOrders = []; // Track multiple active orders
var orderColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff]; // Red, Green, Blue, Yellow, Magenta
var gameState = 'pickup'; // 'pickup', 'delivery'
var timeFreezeActive = false;
var timeFreezeRemaining = 0;
// World wrapping variables
var worldWidth = 2048; // Game screen width
var worldLeftBound = 100;
var worldRightBound = 1950;
// UI Elements
var healthBar = new Text2('Health: 100', {
size: 60,
fill: 0xFF0000
});
healthBar.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthBar);
healthBar.x = 120;
healthBar.y = 20;
var moneyText = new Text2('$0', {
size: 60,
fill: 0x00FF00
});
moneyText.anchor.set(1, 0);
LK.gui.topRight.addChild(moneyText);
moneyText.x = -20;
moneyText.y = 20;
var orderTimer = new Text2('', {
size: 50,
fill: 0xFFFF00
});
orderTimer.anchor.set(0.5, 0);
LK.gui.top.addChild(orderTimer);
orderTimer.y = 100;
// Game-wide route system
var routes = [];
var routeNodes = [];
// Create neighborhood layout
function createNeighborhood() {
// Center coordinates
var centerX = 1024;
var centerY = 1366;
// Create orderly grid layout with proper roads
var blockSize = 200; // Distance between blocks
var roadWidth = 120; // Width of roads (increased from 80)
var buildingSize = 80; // Size of buildings (reduced from 120)
// Create 4-block central park (2x2 grid of park blocks)
var parkSize = blockSize;
var parkPositions = [{
x: centerX - parkSize / 2,
y: centerY - parkSize / 2
},
// Top-left
{
x: centerX + parkSize / 2,
y: centerY - parkSize / 2
},
// Top-right
{
x: centerX - parkSize / 2,
y: centerY + parkSize / 2
},
// Bottom-left
{
x: centerX + parkSize / 2,
y: centerY + parkSize / 2
} // Bottom-right
];
// Create the 4 park blocks
for (var i = 0; i < parkPositions.length; i++) {
var park = game.addChild(LK.getAsset('building', {
anchorX: 0.5,
anchorY: 0.5,
x: parkPositions[i].x,
y: parkPositions[i].y
}));
park.tint = 0x4CAF50; // Green color for park
park.scaleX = 1.6;
park.scaleY = 1.6;
}
// Define grid layout for city blocks around the park
var gridRows = 8;
var gridCols = 6;
var startX = centerX - gridCols * blockSize / 2;
var startY = centerY - gridRows * blockSize / 2;
// Create horizontal roads
for (var row = 0; row <= gridRows; row++) {
var roadY = startY + row * blockSize;
if (roadY > 150 && roadY < 2582) {
var road = game.addChild(LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: roadY
}));
road.scaleX = 15; // Make road span full width
road.scaleY = roadWidth / 80;
}
}
// Create vertical roads
for (var col = 0; col <= gridCols; col++) {
var roadX = startX + col * blockSize;
if (roadX > 150 && roadX < 1898) {
var road = game.addChild(LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: roadX,
y: centerY
}));
road.scaleX = roadWidth / 100;
road.scaleY = 20; // Make road span full height
}
}
// Store all building and restaurant positions
var allBuildingPositions = [];
var allRestaurantPositions = [];
// Generate building positions in the center of each block
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var blockCenterX = startX + col * blockSize + blockSize / 2;
var blockCenterY = startY + row * blockSize + blockSize / 2;
// Skip blocks that overlap with the central park
var distanceToCenter = Math.sqrt(Math.pow(blockCenterX - centerX, 2) + Math.pow(blockCenterY - centerY, 2));
if (distanceToCenter < parkSize * 1.2) continue;
// Only add positions within game bounds
if (blockCenterX > 200 && blockCenterX < 1848 && blockCenterY > 250 && blockCenterY < 2482) {
// Randomly assign as building or restaurant
if (Math.random() < 0.3) {
// 30% chance for restaurant
allRestaurantPositions.push({
x: blockCenterX,
y: blockCenterY
});
} else {
// 70% chance for building
allBuildingPositions.push({
x: blockCenterX,
y: blockCenterY
});
}
}
}
}
// Create buildings at calculated positions
for (var i = 0; i < allBuildingPositions.length; i++) {
var building = game.addChild(LK.getAsset('building', {
anchorX: 0.5,
anchorY: 0.5,
x: allBuildingPositions[i].x,
y: allBuildingPositions[i].y
}));
// Add entrance asphalt in front of building (south side)
var entranceAsphalt = game.addChild(LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: allBuildingPositions[i].x,
y: allBuildingPositions[i].y + buildingSize / 2 + 30
}));
entranceAsphalt.scaleX = 1.2;
entranceAsphalt.scaleY = 0.7;
entranceAsphalt.tint = 0x606060; // Darker asphalt for entrance
building.entranceX = allBuildingPositions[i].x;
building.entranceY = allBuildingPositions[i].y + buildingSize / 2 + 30;
buildings.push(building);
}
// Create restaurants at calculated positions
for (var i = 0; i < allRestaurantPositions.length; i++) {
var restaurant = game.addChild(new Restaurant());
restaurant.x = allRestaurantPositions[i].x;
restaurant.y = allRestaurantPositions[i].y;
// Add entrance asphalt in front of restaurant (south side)
var entranceAsphalt = game.addChild(LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5,
x: allRestaurantPositions[i].x,
y: allRestaurantPositions[i].y + buildingSize / 2 + 30
}));
entranceAsphalt.scaleX = 1.2;
entranceAsphalt.scaleY = 0.7;
entranceAsphalt.tint = 0x606060; // Darker asphalt for entrance
restaurant.entranceX = allRestaurantPositions[i].x;
restaurant.entranceY = allRestaurantPositions[i].y + buildingSize / 2 + 30;
restaurants.push(restaurant);
}
// Create customers - place them exactly in the center of buildings
var customerCount = Math.min(buildings.length, 20);
for (var i = 0; i < customerCount; i++) {
var customer = game.addChild(new Customer());
// Place customer exactly at building center (red dot in middle of building)
customer.x = buildings[i].x;
customer.y = buildings[i].y;
customers.push(customer);
}
// Create route network after building placement
createRouteNetwork();
}
// Create a network of route nodes for pathfinding
function createRouteNetwork() {
routeNodes = [];
routes = [];
var centerX = 1024;
var centerY = 1366;
var blockSize = 200;
var gridRows = 8;
var gridCols = 6;
var startX = centerX - gridCols * blockSize / 2;
var startY = centerY - gridRows * blockSize / 2;
// Create route nodes at road intersections
for (var row = 0; row <= gridRows; row++) {
for (var col = 0; col <= gridCols; col++) {
var nodeX = startX + col * blockSize;
var nodeY = startY + row * blockSize;
// Only add nodes within game bounds
if (nodeX > 150 && nodeX < 1898 && nodeY > 150 && nodeY < 2582) {
routeNodes.push({
x: nodeX,
y: nodeY,
connections: [],
isIntersection: true
});
}
}
}
// Add route nodes at the center of each road segment for better pathfinding
// Horizontal road segments
for (var row = 0; row <= gridRows; row++) {
for (var col = 0; col < gridCols; col++) {
var nodeX = startX + col * blockSize + blockSize / 2;
var nodeY = startY + row * blockSize;
if (nodeX > 150 && nodeX < 1898 && nodeY > 150 && nodeY < 2582) {
routeNodes.push({
x: nodeX,
y: nodeY,
connections: []
});
}
}
}
// Vertical road segments
for (var row = 0; row < gridRows; row++) {
for (var col = 0; col <= gridCols; col++) {
var nodeX = startX + col * blockSize;
var nodeY = startY + row * blockSize + blockSize / 2;
if (nodeX > 150 && nodeX < 1898 && nodeY > 150 && nodeY < 2582) {
routeNodes.push({
x: nodeX,
y: nodeY,
connections: []
});
}
}
}
// Add building access nodes near each building and restaurant
for (var i = 0; i < buildings.length; i++) {
var building = buildings[i];
// Add access nodes on the roads around the building
var accessNodes = [{
x: building.x,
y: building.y - blockSize / 2
},
// North road
{
x: building.x,
y: building.y + blockSize / 2
},
// South road
{
x: building.x - blockSize / 2,
y: building.y
},
// West road
{
x: building.x + blockSize / 2,
y: building.y
} // East road
];
for (var j = 0; j < accessNodes.length; j++) {
var accessNode = accessNodes[j];
if (accessNode.x > 150 && accessNode.x < 1898 && accessNode.y > 150 && accessNode.y < 2582) {
routeNodes.push({
x: accessNode.x,
y: accessNode.y,
connections: [],
buildingAccess: i
});
}
}
}
for (var i = 0; i < restaurants.length; i++) {
var restaurant = restaurants[i];
// Add access nodes on the roads around the restaurant
var accessNodes = [{
x: restaurant.x,
y: restaurant.y - blockSize / 2
},
// North road
{
x: restaurant.x,
y: restaurant.y + blockSize / 2
},
// South road
{
x: restaurant.x - blockSize / 2,
y: restaurant.y
},
// West road
{
x: restaurant.x + blockSize / 2,
y: restaurant.y
} // East road
];
for (var j = 0; j < accessNodes.length; j++) {
var accessNode = accessNodes[j];
if (accessNode.x > 150 && accessNode.x < 1898 && accessNode.y > 150 && accessNode.y < 2582) {
routeNodes.push({
x: accessNode.x,
y: accessNode.y,
connections: [],
restaurantAccess: i
});
}
}
}
// Connect route nodes - connect nodes that are on the same road
for (var i = 0; i < routeNodes.length; i++) {
for (var j = i + 1; j < routeNodes.length; j++) {
var distance = getDistance(routeNodes[i], routeNodes[j]);
var node1 = routeNodes[i];
var node2 = routeNodes[j];
// Connect nodes if they're on the same horizontal or vertical road
var sameHorizontalRoad = Math.abs(node1.y - node2.y) < 20 && distance < blockSize + 50;
var sameVerticalRoad = Math.abs(node1.x - node2.x) < 20 && distance < blockSize + 50;
if ((sameHorizontalRoad || sameVerticalRoad) && distance > 10) {
routeNodes[i].connections.push(j);
routeNodes[j].connections.push(i);
}
}
}
}
// Find closest route node to a position
function findClosestRouteNode(x, y) {
var closestIndex = -1;
var closestDistance = Infinity;
for (var i = 0; i < routeNodes.length; i++) {
var distance = getDistance({
x: x,
y: y
}, routeNodes[i]);
if (distance < closestDistance) {
closestDistance = distance;
closestIndex = i;
}
}
return closestIndex;
}
// Check if path between two nodes is clear of buildings
function isPathClear(node1, node2) {
var steps = 10;
var stepX = (node2.x - node1.x) / steps;
var stepY = (node2.y - node1.y) / steps;
for (var step = 0; step <= steps; step++) {
var checkX = node1.x + stepX * step;
var checkY = node1.y + stepY * step;
// Check if this point intersects with any building
for (var i = 0; i < buildings.length; i++) {
var building = buildings[i];
var dx = Math.abs(checkX - building.x);
var dy = Math.abs(checkY - building.y);
if (dx < building.width / 2 + 30 && dy < building.height / 2 + 30) {
return false;
}
}
}
return true;
}
// Get distance between two points
function getDistance(point1, point2) {
var dx = point1.x - point2.x;
var dy = point1.y - point2.y;
return Math.sqrt(dx * dx + dy * dy);
}
// Find path between two positions using A* pathfinding
function findPath(startX, startY, endX, endY) {
var startNode = findClosestRouteNode(startX, startY);
var endNode = findClosestRouteNode(endX, endY);
if (startNode === -1 || endNode === -1) return [];
if (startNode === endNode) return [routeNodes[endNode]];
// Simple A* implementation
var openSet = [{
index: startNode,
g: 0,
h: getDistance(routeNodes[startNode], routeNodes[endNode]),
parent: null
}];
var closedSet = [];
var visited = [];
while (openSet.length > 0) {
// Find node with lowest f score
var current = openSet[0];
var currentIndex = 0;
for (var i = 1; i < openSet.length; i++) {
if (openSet[i].g + openSet[i].h < current.g + current.h) {
current = openSet[i];
currentIndex = i;
}
}
// Move current from open to closed
openSet.splice(currentIndex, 1);
closedSet.push(current);
visited.push(current.index);
// Check if we reached the goal
if (current.index === endNode) {
var path = [];
var pathNode = current;
while (pathNode) {
path.unshift(routeNodes[pathNode.index]);
pathNode = pathNode.parent;
}
return path;
}
// Check all neighbors
var connections = routeNodes[current.index].connections;
for (var i = 0; i < connections.length; i++) {
var neighborIndex = connections[i];
// Skip if already visited
if (visited.indexOf(neighborIndex) !== -1) continue;
var g = current.g + getDistance(routeNodes[current.index], routeNodes[neighborIndex]);
var h = getDistance(routeNodes[neighborIndex], routeNodes[endNode]);
// Check if this path to neighbor is better
var existingOpen = null;
for (var j = 0; j < openSet.length; j++) {
if (openSet[j].index === neighborIndex) {
existingOpen = openSet[j];
break;
}
}
if (!existingOpen || g < existingOpen.g) {
var neighbor = {
index: neighborIndex,
g: g,
h: h,
parent: current
};
if (existingOpen) {
openSet[j] = neighbor;
} else {
openSet.push(neighbor);
}
}
}
}
return []; // No path found
}
function createNewOrder() {
// Create multiple orders if we have less than 5
var ordersToCreate = Math.min(5 - currentOrders.length, 5);
for (var orderNum = 0; orderNum < ordersToCreate; orderNum++) {
var availableRestaurants = restaurants.filter(function (r) {
return !r.hasOrder;
});
if (availableRestaurants.length === 0) break;
// Select random restaurant and building for new customer
var restaurant = availableRestaurants[Math.floor(Math.random() * availableRestaurants.length)];
var availableBuildings = buildings.filter(function (b) {
// Don't use buildings that already have visible customers
for (var i = 0; i < customers.length; i++) {
if (customers[i].visible && customers[i].x === b.x && customers[i].y === b.y) {
return false;
}
}
return true;
});
if (availableBuildings.length === 0) break;
var selectedBuilding = availableBuildings[Math.floor(Math.random() * availableBuildings.length)];
// Select color for this order
var orderColor = orderColors[orderNum % orderColors.length];
// Find or create customer at selected building with matching color
var customer = null;
for (var i = 0; i < customers.length; i++) {
if (!customers[i].waitingForOrder && customers[i].x === selectedBuilding.x && customers[i].y === selectedBuilding.y) {
customer = customers[i];
break;
}
}
// If no customer at this building, create one with matching color
if (!customer) {
customer = game.addChild(new Customer(orderColor));
customer.x = selectedBuilding.x;
customer.y = selectedBuilding.y;
customers.push(customer);
} else {
// Update existing customer color to match order
customer.customerColor = orderColor;
customer.children[0].tint = orderColor;
}
var order = game.addChild(new Order(orderColor));
order.x = restaurant.x;
order.y = restaurant.y - 60;
order.restaurant = restaurant;
order.customer = customer;
restaurant.hasOrder = true;
restaurant.orderReady = true;
customer.waitingForOrder = true;
customer.visible = true; // Make customer visible when order arrives
customer.orderTime = 0;
// Create delivery target indicator with matching color
var deliveryTarget = game.addChild(LK.getAsset('deliveryTarget', {
anchorX: 0.5,
anchorY: 0.5,
x: customer.x,
y: customer.y - 50
}));
deliveryTarget.tint = orderColor; // Match target color to order/customer
customer.deliveryTarget = deliveryTarget;
// Start flashing animation
tween(deliveryTarget, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(deliveryTarget, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (deliveryTarget.parent) {
tween(deliveryTarget, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(deliveryTarget, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}
}
});
}
});
currentOrders.push(order);
orders.push(order);
// Set first order as current order for player
if (!currentOrder) {
currentOrder = order;
gameState = 'pickup';
}
}
}
function createCars() {
var targetCarCount = 10; // Start with 10 cars
if (money >= 200) {
targetCarCount = 20; // Max 20 cars at $200
} else if (money > 0) {
// Gradually increase from 10 to 20 based on money
targetCarCount = 10 + Math.floor(money / 200 * 10);
}
// Add cars if we need more
while (cars.length < targetCarCount) {
var car = game.addChild(new Car());
// Place cars on roads including near building entrances
var spawnChoice = Math.random();
if (spawnChoice < 0.4) {
// Place on horizontal road
car.x = Math.random() * 1800 + 124;
car.y = 1366 + (Math.floor(Math.random() * 8) - 4) * 200; // Various horizontal roads
} else if (spawnChoice < 0.8) {
// Place on vertical road
car.x = 1024 + (Math.floor(Math.random() * 6) - 3) * 200; // Various vertical roads
car.y = Math.random() * 2400 + 166;
} else {
// Place near building entrances to simulate cars entering neighborhood
if (buildings.length > 0) {
var randomBuilding = buildings[Math.floor(Math.random() * buildings.length)];
car.x = randomBuilding.entranceX + (Math.random() - 0.5) * 100;
car.y = randomBuilding.entranceY + (Math.random() - 0.5) * 50;
} else {
car.x = Math.random() * 1800 + 124;
car.y = Math.random() * 2400 + 166;
}
}
cars.push(car);
}
// Remove excess cars if money decreased (shouldn't happen but safety check)
while (cars.length > targetCarCount) {
var removedCar = cars.pop();
removedCar.destroy();
}
}
function spawnPowerUp() {
if (powerUps.length >= 3) return;
var type = Math.random() < 0.6 ? 'health' : 'time';
var powerUp = game.addChild(new PowerUp(type));
powerUp.x = Math.random() * 1800 + 124;
powerUp.y = Math.random() * 2400 + 166;
powerUps.push(powerUp);
}
function updateUI() {
healthBar.setText('Health: ' + Math.floor(health));
moneyText.setText('$' + money);
if (currentOrder && gameState === 'delivery') {
var timeLeft = Math.max(0, currentOrder.timeRemaining / 1000);
orderTimer.setText('Deliver in: ' + timeLeft.toFixed(1) + 's');
} else {
orderTimer.setText('');
}
}
function checkCollisions() {
// Check courier-restaurant entrance collision for pickup
if (gameState === 'pickup' && currentOrders.length > 0) {
// Check all available orders
for (var orderIdx = 0; orderIdx < currentOrders.length; orderIdx++) {
var order = currentOrders[orderIdx];
if (order.timerStarted) continue; // Skip orders already picked up
var restaurant = order.restaurant;
var pickupDistance = Math.sqrt(Math.pow(restaurant.entranceX - courier.x, 2) + Math.pow(restaurant.entranceY - courier.y, 2));
if (pickupDistance < 60) {
// Check entrance area
// Pick up order
LK.getSound('pickup').play();
courier.hasOrder = true;
currentOrder = order; // Set as current order for player
gameState = 'delivery';
currentOrder.x = courier.x;
currentOrder.y = courier.y - 40;
// Start the package timer when courier receives the package
currentOrder.timerStarted = true;
break; // Only pick up one order
}
}
}
// Check courier-customer building entrance collision for delivery
if (gameState === 'delivery' && currentOrder && courier.hasOrder) {
var customer = currentOrder.customer;
// Find the building that contains this customer
var customerBuilding = null;
for (var i = 0; i < buildings.length; i++) {
if (buildings[i].x === customer.x && buildings[i].y === customer.y) {
customerBuilding = buildings[i];
break;
}
}
if (customerBuilding) {
var deliveryDistance = Math.sqrt(Math.pow(customerBuilding.entranceX - courier.x, 2) + Math.pow(customerBuilding.entranceY - courier.y, 2));
if (deliveryDistance < 60) {
// Check entrance area
// 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
var timeBonus = Math.floor(currentOrder.timeRemaining / 1000);
bonus = 10 + timeBonus;
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;
currentOrder.customer.visible = false; // Hide customer after delivery
// Remove delivery target indicator
if (currentOrder.customer.deliveryTarget) {
tween.stop(currentOrder.customer.deliveryTarget);
currentOrder.customer.deliveryTarget.destroy();
currentOrder.customer.deliveryTarget = 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';
// Create new order immediately to maintain constant flow
createNewOrder();
}
// Initialize game elements
createNeighborhood();
// Create courier
courier = game.addChild(new Courier());
courier.x = 1024;
courier.y = 1366;
// Initialize cars
createCars();
// Create AI couriers
for (var i = 0; i < 3; i++) {
var aiCourier = game.addChild(new AICourier());
aiCourier.x = Math.random() * 1800 + 124;
aiCourier.y = Math.random() * 2400 + 166;
// Create route waypoints based on restaurant and road positions
aiCourier.routeWaypoints = [];
// Add restaurant positions as waypoints
for (var j = 0; j < restaurants.length; j++) {
aiCourier.routeWaypoints.push({
x: restaurants[j].x,
y: restaurants[j].y
});
}
// Add some road intersection points as additional waypoints
var centerX = 1024;
var centerY = 1366;
var roadRadii = [450, 750]; // Road layer radii
for (var r = 0; r < roadRadii.length; r++) {
for (var angle = 0; angle < Math.PI * 2; angle += Math.PI / 4) {
var roadX = centerX + Math.cos(angle) * roadRadii[r];
var roadY = centerY + Math.sin(angle) * roadRadii[r];
if (roadX > 100 && roadX < 1948 && roadY > 100 && roadY < 2632) {
aiCourier.routeWaypoints.push({
x: roadX,
y: roadY
});
}
}
}
aiCouriers.push(aiCourier);
}
// Touch controls
var targetX = courier.x;
var targetY = courier.y;
game.down = function (x, y, obj) {
targetX = x;
targetY = y;
};
game.move = function (x, y, obj) {
targetX = x;
targetY = y;
};
// Create first order
createNewOrder();
// Spawn initial power-up
LK.setTimeout(function () {
spawnPowerUp();
}, 3000);
game.update = function () {
// Move courier toward target
courier.moveToward(targetX, targetY);
// Update time freeze
if (timeFreezeActive) {
timeFreezeRemaining -= 16.67;
if (timeFreezeRemaining <= 0) {
timeFreezeActive = false;
}
}
// Update current order timer (if not frozen)
if (currentOrder && !timeFreezeActive) {
currentOrder.update();
// Check if order expired before pickup
if (!currentOrder.timerStarted && currentOrder.lifeTime >= currentOrder.maxLifeTime) {
// Order expired - clean up and create new one
currentOrder.restaurant.hasOrder = false;
currentOrder.customer.waitingForOrder = false;
currentOrder.customer.visible = false; // Hide customer
if (currentOrder.customer.deliveryTarget) {
tween.stop(currentOrder.customer.deliveryTarget);
currentOrder.customer.deliveryTarget.destroy();
currentOrder.customer.deliveryTarget = null;
}
currentOrder.destroy();
for (var i = orders.length - 1; i >= 0; i--) {
if (orders[i] === currentOrder) {
orders.splice(i, 1);
break;
}
}
currentOrder = null;
gameState = 'pickup';
// Create new order immediately
createNewOrder();
} else {
// Move order with courier if picked up
if (courier.hasOrder && gameState === 'delivery') {
currentOrder.x = courier.x;
currentOrder.y = courier.y - 40;
}
// Maintain delivery target flashing
if (gameState === 'delivery' && currentOrder.customer.deliveryTarget) {
var target = currentOrder.customer.deliveryTarget;
if (!target.isFlashing) {
var _flashTarget = function flashTarget() {
if (target.parent) {
tween(target, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (target.parent) {
tween(target, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: _flashTarget
});
}
}
});
}
};
target.isFlashing = true;
_flashTarget();
}
}
// Check for failed delivery
if (currentOrder.timeRemaining <= 0 && gameState === 'delivery') {
completeDelivery();
}
}
}
// Update car count based on money
createCars();
// Update cars
for (var i = 0; i < cars.length; i++) {
cars[i].update();
}
// Update AI couriers
for (var i = 0; i < aiCouriers.length; i++) {
if (aiCouriers[i] && aiCouriers[i].update) {
aiCouriers[i].update();
}
}
// Update customers
for (var i = 0; i < customers.length; i++) {
customers[i].update();
}
// 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 UI
updateUI();
// Win condition (optional - could be score based)
if (money >= 200) {
LK.showYouWin();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -162,11 +162,17 @@
if (nodeDistance < 60) {
self.pathIndex++;
}
} else {
- // No path or reached end - stay in place
- self.targetX = self.x;
- self.targetY = self.y;
+ // No path or reached end - pick a random nearby target to keep moving
+ if (routeNodes.length > 0) {
+ var randomNode = routeNodes[Math.floor(Math.random() * routeNodes.length)];
+ self.targetX = randomNode.x;
+ self.targetY = randomNode.y;
+ } else {
+ self.targetX = self.x;
+ self.targetY = self.y;
+ }
}
// Move toward target using simple movement with collision detection
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
@@ -179,25 +185,25 @@
// Check collision with cars
for (var i = 0; i < cars.length; i++) {
var carDx = Math.abs(newX - cars[i].x);
var carDy = Math.abs(newY - cars[i].y);
- if (carDx < 50 && carDy < 50) {
+ if (carDx < 40 && carDy < 40) {
canMove = false;
break;
}
}
// Check collision with player courier
var playerDx = Math.abs(newX - courier.x);
var playerDy = Math.abs(newY - courier.y);
- if (playerDx < 50 && playerDy < 50) {
+ if (playerDx < 40 && playerDy < 40) {
canMove = false;
}
// Check collision with other AI couriers
for (var i = 0; i < aiCouriers.length; i++) {
if (aiCouriers[i] === self) continue;
var aiDx = Math.abs(newX - aiCouriers[i].x);
var aiDy = Math.abs(newY - aiCouriers[i].y);
- if (aiDx < 50 && aiDy < 50) {
+ if (aiDx < 40 && aiDy < 40) {
canMove = false;
break;
}
}
@@ -209,8 +215,23 @@
self.x = worldRightBound;
} else if (self.x > worldRightBound) {
self.x = worldLeftBound;
}
+ self.stuckTimer = 0;
+ } else {
+ // If stuck, try alternative movement
+ if (!self.stuckTimer) self.stuckTimer = 0;
+ self.stuckTimer += 16.67;
+ if (self.stuckTimer > 1000) {
+ // If stuck for more than 1 second
+ // Try moving in a different direction
+ var randomAngle = Math.random() * Math.PI * 2;
+ var moveDistance = 20;
+ self.x += Math.cos(randomAngle) * moveDistance;
+ self.y += Math.sin(randomAngle) * moveDistance;
+ self.stuckTimer = 0;
+ self.currentPath = []; // Clear current path to force new pathfinding
+ }
}
}
// Update order position if carrying one
if (self.hasOrder && self.targetOrder) {
@@ -1381,9 +1402,11 @@
cars[i].update();
}
// Update AI couriers
for (var i = 0; i < aiCouriers.length; i++) {
- aiCouriers[i].update();
+ if (aiCouriers[i] && aiCouriers[i].update) {
+ aiCouriers[i].update();
+ }
}
// Update customers
for (var i = 0; i < customers.length; i++) {
customers[i].update();
ı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