/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Arrow = Container.expand(function (targetType) {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
// Set color based on target type
if (targetType === 'pickup') {
arrowGraphics.tint = 0x00ff00; // Green for package pickup
} else if (targetType === 'delivery') {
arrowGraphics.tint = 0xff0000; // Red for delivery house
}
self.targetType = targetType;
self.target = null;
self.update = function () {
if (self.target && player) {
// Calculate direction to target
var dx = self.target.x - player.x;
var dy = self.target.y - player.y;
var angle = Math.atan2(dy, dx);
// Position arrow around player
var radius = 80;
self.x = player.x + Math.cos(angle) * radius;
self.y = player.y + Math.sin(angle) * radius;
// Rotate arrow to point towards target
arrowGraphics.rotation = angle + Math.PI / 2;
// Pulse animation using tween
if (!self.tweening) {
self.tweening = true;
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.tweening = false;
}
});
}
});
}
}
};
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Set car to move in one of four directions: up, down, left, right
var directions = [{
x: 0,
y: -1
},
// up
{
x: 0,
y: 1
},
// down
{
x: -1,
y: 0
},
// left
{
x: 1,
y: 0
} // right
];
var direction = directions[Math.floor(Math.random() * directions.length)];
self.speed = 2 + Math.random() * 2; // Speed between 2-4
self.directionX = direction.x;
self.directionY = direction.y;
self.update = function () {
var oldX = self.x;
var oldY = self.y;
// Move in straight line based on direction
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Calculate rotation based on movement direction
// Front of sprite is left side, so 0 rotation = moving left
var targetRotation = Math.atan2(self.directionY, self.directionX) + Math.PI;
// Smooth rotation using tween
tween.stop(carGraphics, {
rotation: true
});
tween(carGraphics, {
rotation: targetRotation
}, {
duration: 200,
easing: tween.easeOut
});
// Check collision with houses
var hitHouse = false;
for (var h = 0; h < houses.length; h++) {
if (self.intersects(houses[h])) {
hitHouse = true;
break;
}
}
// Check if trying to enter a block (non-street area)
var enteringBlock = !isOnStreet(self.x, self.y);
// If hit house or entering block, revert movement and change direction
if (hitHouse || enteringBlock) {
self.x = oldX;
self.y = oldY;
// Choose a new random direction
var newDirection = directions[Math.floor(Math.random() * directions.length)];
self.directionX = newDirection.x;
self.directionY = newDirection.y;
}
// Change direction at edges
if (self.x < 40 || self.x > 2008) {
self.directionX *= -1;
}
if (self.y < 40 || self.y > 2692) {
self.directionY *= -1;
}
// Keep in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
};
return self;
});
var Dog = Container.expand(function () {
var self = Container.call(this);
var dogGraphics = self.attachAsset('dog', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.direction = Math.random() * Math.PI * 2;
self.update = function () {
// Check distance to player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distanceToPlayer = Math.sqrt(dx * dx + dy * dy);
var detectionRange = 150;
if (distanceToPlayer < detectionRange && player) {
// Follow player behavior
var angleToPlayer = Math.atan2(dy, dx);
self.direction = angleToPlayer;
self.speed = 2.5; // Slightly faster when chasing
} else {
// Simple AI - change direction occasionally
if (Math.random() < 0.02) {
self.direction = Math.random() * Math.PI * 2;
}
self.speed = 2; // Normal speed when wandering
}
var oldX = self.x;
var oldY = self.y;
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
// Check collision with houses
var hitHouse = false;
for (var h = 0; h < houses.length; h++) {
if (self.intersects(houses[h])) {
hitHouse = true;
break;
}
}
// If hit house, revert movement and change direction
if (hitHouse) {
self.x = oldX;
self.y = oldY;
self.direction = Math.random() * Math.PI * 2;
}
// Bounce off edges
if (self.x < 15 || self.x > 2033) {
self.direction = Math.PI - self.direction;
}
if (self.y < 15 || self.y > 2717) {
self.direction = -self.direction;
}
// Keep in bounds
self.x = Math.max(15, Math.min(2033, self.x));
self.y = Math.max(15, Math.min(2717, self.y));
};
return self;
});
var House = Container.expand(function (houseId) {
var self = Container.call(this);
var houseGraphics = self.attachAsset('house', {
anchorX: 0.5,
anchorY: 0.5
});
self.houseId = houseId;
self.hasDelivery = false;
self.down = function (x, y, obj) {
if (player.hasPackage && self.distanceTo(player) < 80) {
if (player.targetHouse === self.houseId) {
// Correct delivery
LK.setScore(LK.getScore() + 100);
player.hasPackage = false;
player.targetHouse = null;
self.hasDelivery = true;
packagesDelivered++;
// Visual feedback
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.getSound('delivery').play();
scoreText.setText(LK.getScore());
// Remove delivery arrow
if (deliveryArrow) {
deliveryArrow.destroy();
deliveryArrow = null;
}
// Create pickup arrow for next available package
if (packages.length > 0) {
pickupArrow = game.addChild(new Arrow('pickup'));
pickupArrow.target = packages[0];
}
// Check win condition
if (packagesDelivered >= totalPackages) {
if (currentLevel < 4) {
currentLevel++;
startLevel();
} else {
LK.showYouWin();
}
}
} else {
// Wrong delivery
LK.setScore(Math.max(0, LK.getScore() - 50));
scoreText.setText(LK.getScore());
LK.effects.flashObject(self, 0xff0000, 500);
}
}
};
self.distanceTo = function (other) {
var dx = self.x - other.x;
var dy = self.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
};
return self;
});
var Package = Container.expand(function (targetHouseId) {
var self = Container.call(this);
var packageGraphics = self.attachAsset('package', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.targetHouseId = targetHouseId; // Link package to specific house
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.hasPackage = false;
self.targetHouse = null;
return self;
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
self.type = type; // 'time', 'speed', 'shield'
self.collected = false;
// Use specific asset based on powerup type
var assetName = 'powerup'; // default
if (type === 'time') {
assetName = 'time_powerup';
} else if (type === 'speed') {
assetName = 'speed_powerup';
} else if (type === 'shield') {
assetName = 'shield_powerup';
} else if (type === 'life') {
assetName = 'life_powerup';
}
var powerupGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Game variables
var player;
var packages = [];
var houses = [];
var cars = [];
var dogs = [];
var powerups = [];
var currentLevel = 1;
var totalPackages = 3;
var packagesDelivered = 0;
var gameTime = 60000; // 60 seconds
var timeRemaining = gameTime;
var dragNode = null;
var lastCollisionCheck = {};
var targetX = null;
var targetY = null;
var isMoving = false;
var pickupArrow = null;
var deliveryArrow = null;
var lives = 3;
// Block system variables
var blockSize = 460; // 4x4 road tiles (115 * 4)
var blocksX = 4; // Number of blocks horizontally
var blocksY = 5; // Number of blocks vertically
var streetWidth = 115; // Width of streets between blocks
// Helper function to check if position is on a street (road)
function isOnStreet(x, y) {
var blockX = Math.floor(x / (blockSize + streetWidth));
var blockY = Math.floor(y / (blockSize + streetWidth));
var localX = x % (blockSize + streetWidth);
var localY = y % (blockSize + streetWidth);
// Check if position is in street area (between blocks)
return localX >= blockSize || localY >= blockSize;
}
// Helper function to get random street position
function getRandomStreetPosition() {
var attempts = 0;
var maxAttempts = 100;
while (attempts < maxAttempts) {
var x = Math.random() * 2048;
var y = Math.random() * 2732;
if (isOnStreet(x, y)) {
return {
x: x,
y: y
};
}
attempts++;
}
// Fallback to center street
return {
x: 1024,
y: 1366
};
}
// Helper function to get random block position for houses
function getRandomBlockPosition() {
var blockX = Math.floor(Math.random() * blocksX);
var blockY = Math.floor(Math.random() * blocksY);
var startX = blockX * (blockSize + streetWidth);
var startY = blockY * (blockSize + streetWidth);
var x = startX + 100 + Math.random() * (blockSize - 200);
var y = startY + 100 + Math.random() * (blockSize - 200);
return {
x: x,
y: y
};
}
// UI elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var timeText = new Text2('Time: 60', {
size: 60,
fill: 0xFFFFFF
});
timeText.anchor.set(1, 0);
LK.gui.topRight.addChild(timeText);
var levelText = new Text2('Level 1', {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
LK.gui.topLeft.addChild(levelText);
var livesText = new Text2('Lives: 3', {
size: 60,
fill: 0xFFFFFF
});
livesText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(livesText);
// Create varied road background with rotation based on neighbors
var roadGrid = []; // Store road references for neighbor checking
// First pass: create all roads without rotation
for (var i = 0; i < 18; i++) {
roadGrid[i] = [];
for (var j = 0; j < 24; j++) {
var road = LK.getAsset('road', {
x: i * 115,
y: j * 115,
anchorX: 0,
anchorY: 0,
orientation: 0
});
roadGrid[i][j] = road;
game.addChild(road);
}
}
// Second pass: apply rotation based on neighbors and replace with intersections where needed
for (var i = 0; i < 18; i++) {
for (var j = 0; j < 24; j++) {
var road = roadGrid[i][j];
var hasLeftNeighbor = i > 0;
var hasRightNeighbor = i < 17;
var hasTopNeighbor = j > 0;
var hasBottomNeighbor = j < 23;
// Count how many directions have neighbors
var neighborCount = 0;
if (hasLeftNeighbor) neighborCount++;
if (hasRightNeighbor) neighborCount++;
if (hasTopNeighbor) neighborCount++;
if (hasBottomNeighbor) neighborCount++;
// Check if this should be an intersection (3 or 4 way intersection)
var isIntersection = neighborCount >= 3;
if (isIntersection) {
// Remove old road and create intersection
game.removeChild(road);
var intersection = LK.getAsset('road_intersection', {
x: i * 115,
y: j * 115,
anchorX: 0,
anchorY: 0
});
roadGrid[i][j] = intersection;
game.addChild(intersection);
} else {
// Determine orientation based on neighbor pattern
var orientation = 0;
// Horizontal road (connects left-right)
if ((hasLeftNeighbor || hasRightNeighbor) && !hasTopNeighbor && !hasBottomNeighbor) {
orientation = 1; // 90 degree rotation for horizontal orientation
}
// Vertical road (connects top-bottom) - default orientation
else if ((hasTopNeighbor || hasBottomNeighbor) && !hasLeftNeighbor && !hasRightNeighbor) {
orientation = 0; // Keep vertical orientation
}
// L-shaped corners and T-junctions
else if (hasLeftNeighbor && hasTopNeighbor && !hasRightNeighbor && !hasBottomNeighbor) {
orientation = 3; // Bottom-right corner
} else if (hasRightNeighbor && hasTopNeighbor && !hasLeftNeighbor && !hasBottomNeighbor) {
orientation = 2; // Bottom-left corner
} else if (hasLeftNeighbor && hasBottomNeighbor && !hasRightNeighbor && !hasTopNeighbor) {
orientation = 0; // Top-right corner
} else if (hasRightNeighbor && hasBottomNeighbor && !hasLeftNeighbor && !hasTopNeighbor) {
orientation = 1; // Top-left corner
}
// T-junctions (2 neighbors)
else if (hasLeftNeighbor && hasRightNeighbor && (hasTopNeighbor || hasBottomNeighbor)) {
orientation = 1; // Horizontal T-junction
} else if (hasTopNeighbor && hasBottomNeighbor && (hasLeftNeighbor || hasRightNeighbor)) {
orientation = 0; // Vertical T-junction
}
// Default cases - prefer horizontal for mixed patterns
else if (hasLeftNeighbor || hasRightNeighbor) {
orientation = 1; // Horizontal orientation
} else {
orientation = 0; // Vertical orientation (default)
}
// Set the correct orientation using the asset's orientation property
game.removeChild(road);
var orientedRoad = LK.getAsset('road', {
x: i * 115,
y: j * 115,
anchorX: 0,
anchorY: 0,
orientation: orientation
});
roadGrid[i][j] = orientedRoad;
game.addChild(orientedRoad);
}
}
}
// Create street blocks in the block areas for city-like appearance
for (var blockX = 0; blockX < blocksX; blockX++) {
for (var blockY = 0; blockY < blocksY; blockY++) {
var startX = blockX * (blockSize + streetWidth);
var startY = blockY * (blockSize + streetWidth);
// Add street block asset in the center of each block
var streetBlock = LK.getAsset('street_block', {
x: startX + blockSize / 2,
y: startY + blockSize / 2,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(streetBlock);
}
}
function startLevel() {
// Clear existing game objects
for (var i = packages.length - 1; i >= 0; i--) {
packages[i].destroy();
}
for (var i = houses.length - 1; i >= 0; i--) {
houses[i].destroy();
}
for (var i = cars.length - 1; i >= 0; i--) {
cars[i].destroy();
}
for (var i = dogs.length - 1; i >= 0; i--) {
dogs[i].destroy();
}
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].destroy();
}
// Clear arrow indicators
if (pickupArrow) {
pickupArrow.destroy();
pickupArrow = null;
}
if (deliveryArrow) {
deliveryArrow.destroy();
deliveryArrow = null;
}
packages = [];
houses = [];
cars = [];
dogs = [];
powerups = [];
packagesDelivered = 0;
// Reset time
timeRemaining = gameTime;
// Update UI
levelText.setText('Level ' + currentLevel);
// Create player if not exists
if (!player) {
player = game.addChild(new Player());
// Find a safe spawn position that doesn't overlap with houses
var playerSpawned = false;
var attempts = 0;
var maxAttempts = 50;
var minPlayerDistance = 150; // Minimum distance from houses
while (!playerSpawned && attempts < maxAttempts) {
var spawnX = 200 + Math.random() * 1600;
var spawnY = 200 + Math.random() * 1200;
var positionClear = true;
// Check if spawn position is clear of houses
for (var j = 0; j < houses.length; j++) {
var dx = spawnX - houses[j].x;
var dy = spawnY - houses[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minPlayerDistance) {
positionClear = false;
break;
}
}
if (positionClear) {
player.x = spawnX;
player.y = spawnY;
playerSpawned = true;
}
attempts++;
}
// Fallback to default position if no safe spot found
if (!playerSpawned) {
player.x = 1024;
player.y = 2366;
}
}
player.hasPackage = false;
player.targetHouse = null;
player.hasShield = false;
// Helper function to check if position overlaps with existing houses
function isPositionClear(x, y, minDistance) {
for (var j = 0; j < houses.length; j++) {
var dx = x - houses[j].x;
var dy = y - houses[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
return false;
}
}
return true;
}
// Create packages and houses
totalPackages = 2 + currentLevel;
for (var i = 0; i < totalPackages; i++) {
// Create package with specific target house ID
var pkg = new Package(i);
pkg.x = 200 + Math.random() * 1600;
pkg.y = 200 + Math.random() * 1200;
packages.push(pkg);
game.addChild(pkg);
}
// Create corresponding house with overlap prevention
// Create pickup arrow indicator pointing to first available package
if (packages.length > 0) {
pickupArrow = game.addChild(new Arrow('pickup'));
pickupArrow.target = packages[0];
}
// Create packages and houses in blocks
for (var i = 0; i < totalPackages; i++) {
var house = new House(i);
var attempts = 0;
var maxAttempts = 50;
var minHouseDistance = 200; // Minimum distance between houses
do {
var blockPos = getRandomBlockPosition();
house.x = blockPos.x;
house.y = blockPos.y;
attempts++;
} while (!isPositionClear(house.x, house.y, minHouseDistance) && attempts < maxAttempts);
houses.push(house);
game.addChild(house);
}
// Create cars (more with each level) - spawn only on streets
var numCars = currentLevel + 1;
for (var i = 0; i < numCars; i++) {
var car = new Car();
var streetPos = getRandomStreetPosition();
car.x = streetPos.x;
car.y = streetPos.y;
cars.push(car);
game.addChild(car);
}
// Create dogs
var numDogs = Math.floor(currentLevel / 2) + 1;
for (var i = 0; i < numDogs; i++) {
var dog = new Dog();
dog.x = Math.random() * 2048;
dog.y = Math.random() * 2732;
dogs.push(dog);
game.addChild(dog);
}
// Create power-ups
var powerupTypes = ['time', 'speed', 'shield', 'life'];
for (var i = 0; i < 2; i++) {
var powerup = new PowerUp(powerupTypes[Math.floor(Math.random() * powerupTypes.length)]);
powerup.x = Math.random() * 2048;
powerup.y = Math.random() * 2732;
powerups.push(powerup);
game.addChild(powerup);
}
}
function handleMove(x, y, obj) {
// Set target position for smooth movement
targetX = Math.max(30, Math.min(2018, x));
targetY = Math.max(30, Math.min(2702, y));
isMoving = true;
// Orient player sprite based on mouse direction
if (player) {
var dx = targetX - player.x;
if (dx > 0) {
// Moving right - face right (normal orientation)
player.scaleX = 1;
} else if (dx < 0) {
// Moving left - face left (flip horizontally)
player.scaleX = -1;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Check if clicking on package to pick up
for (var i = 0; i < packages.length; i++) {
var pkg = packages[i];
if (!pkg.collected && pkg.intersects && player.intersects(pkg)) {
if (!player.hasPackage) {
pkg.collected = true;
pkg.destroy();
packages.splice(i, 1);
player.hasPackage = true;
// Use the package's assigned target house
player.targetHouse = pkg.targetHouseId;
LK.getSound('pickup').play();
// Remove pickup arrow and create delivery arrow
if (pickupArrow) {
pickupArrow.destroy();
pickupArrow = null;
}
// Create delivery arrow pointing to target house
if (player.targetHouse !== null && player.targetHouse < houses.length) {
deliveryArrow = game.addChild(new Arrow('delivery'));
deliveryArrow.target = houses[player.targetHouse];
}
return;
}
}
}
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
// Mouse up - no action needed for smooth movement
};
game.update = function () {
// Smooth player movement towards target
if (isMoving && targetX !== null && targetY !== null) {
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move towards target with player speed
var moveX = dx / distance * player.speed;
var moveY = dy / distance * player.speed;
var oldX = player.x;
var oldY = player.y;
player.x += moveX;
player.y += moveY;
// Check collision with houses
var hitHouse = false;
var hitTargetHouse = false;
for (var h = 0; h < houses.length; h++) {
if (player.intersects(houses[h])) {
hitHouse = true;
// Check if this is the target house for delivery
if (player.hasPackage && player.targetHouse === h) {
hitTargetHouse = true;
}
break;
}
}
// If hit house, revert movement unless it's the target house for delivery
if (hitHouse && !hitTargetHouse) {
player.x = oldX;
player.y = oldY;
isMoving = false; // Stop movement when hitting house
}
} else {
// Close enough to target, stop moving
player.x = targetX;
player.y = targetY;
isMoving = false;
}
}
// Update time
timeRemaining -= 16.67; // Approximate 60fps
var seconds = Math.ceil(timeRemaining / 1000);
timeText.setText('Time: ' + Math.max(0, seconds));
// Check time up
if (timeRemaining <= 0) {
LK.showGameOver();
return;
}
// Check package pickup
for (var i = packages.length - 1; i >= 0; i--) {
var pkg = packages[i];
if (!pkg.collected && player.intersects(pkg)) {
if (!player.hasPackage) {
pkg.collected = true;
pkg.destroy();
packages.splice(i, 1);
player.hasPackage = true;
// Use the package's assigned target house
player.targetHouse = pkg.targetHouseId;
LK.getSound('pickup').play();
// Remove pickup arrow and create delivery arrow
if (pickupArrow) {
pickupArrow.destroy();
pickupArrow = null;
}
// Create delivery arrow pointing to target house
if (player.targetHouse !== null && player.targetHouse < houses.length) {
deliveryArrow = game.addChild(new Arrow('delivery'));
deliveryArrow.target = houses[player.targetHouse];
}
}
}
}
// Check package delivery when player collides with house
if (player.hasPackage && player.targetHouse !== null) {
for (var h = 0; h < houses.length; h++) {
var house = houses[h];
var houseKey = 'house_delivery_' + h;
var currentCollision = player.intersects(house);
if (!lastCollisionCheck[houseKey] && currentCollision) {
// Just started colliding with house
if (player.targetHouse === h) {
// Correct delivery
LK.setScore(LK.getScore() + 100);
player.hasPackage = false;
player.targetHouse = null;
house.hasDelivery = true;
packagesDelivered++;
// Visual feedback
tween(house, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(house, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.getSound('delivery').play();
scoreText.setText(LK.getScore());
// Remove delivery arrow
if (deliveryArrow) {
deliveryArrow.destroy();
deliveryArrow = null;
}
// Create pickup arrow for next available package
if (packages.length > 0) {
pickupArrow = game.addChild(new Arrow('pickup'));
pickupArrow.target = packages[0];
}
// Check win condition
if (packagesDelivered >= totalPackages) {
if (currentLevel < 4) {
currentLevel++;
startLevel();
} else {
LK.showYouWin();
}
}
break; // Exit loop after successful delivery
} else {
// Wrong delivery
LK.setScore(Math.max(0, LK.getScore() - 50));
scoreText.setText(LK.getScore());
LK.effects.flashObject(house, 0xff0000, 500);
}
}
lastCollisionCheck[houseKey] = currentCollision;
}
}
// Check powerup collection
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (!powerup.collected && player.intersects(powerup)) {
powerup.collected = true;
powerup.destroy();
powerups.splice(i, 1);
if (powerup.type === 'time') {
timeRemaining += 10000; // Add 10 seconds
} else if (powerup.type === 'speed') {
player.speed = 8;
LK.setTimeout(function () {
player.speed = 5;
}, 5000);
} else if (powerup.type === 'shield') {
player.hasShield = true;
// Visual shield effect
tween(player, {
tint: 0x00FFFF
}, {
duration: 200
});
LK.setTimeout(function () {
player.hasShield = false;
tween(player, {
tint: 0xFFFFFF
}, {
duration: 200
});
}, 8000); // Shield lasts 8 seconds
} else if (powerup.type === 'life') {
lives++;
livesText.setText('Lives: ' + lives);
}
LK.getSound('powerup').play();
LK.setScore(LK.getScore() + 25);
scoreText.setText(LK.getScore());
}
}
// Check collisions with obstacles
for (var i = 0; i < cars.length; i++) {
var car = cars[i];
var carKey = 'car_' + i;
var currentCollision = player.intersects(car);
if (!lastCollisionCheck[carKey] && currentCollision) {
// Just started colliding
if (!player.hasShield) {
lives--;
livesText.setText('Lives: ' + lives);
if (lives <= 0) {
LK.showGameOver();
return;
}
LK.setScore(Math.max(0, LK.getScore() - 25));
scoreText.setText(LK.getScore());
LK.effects.flashObject(player, 0xff0000, 500);
}
}
lastCollisionCheck[carKey] = currentCollision;
}
for (var i = 0; i < dogs.length; i++) {
var dog = dogs[i];
var dogKey = 'dog_' + i;
var currentCollision = player.intersects(dog);
if (!lastCollisionCheck[dogKey] && currentCollision) {
// Just started colliding
if (!player.hasShield) {
lives--;
livesText.setText('Lives: ' + lives);
if (lives <= 0) {
LK.showGameOver();
return;
}
LK.setScore(Math.max(0, LK.getScore() - 15));
scoreText.setText(LK.getScore());
LK.effects.flashObject(player, 0xff0000, 300);
}
}
lastCollisionCheck[dogKey] = currentCollision;
}
};
// Start the first level
startLevel();
// Play background music
LK.playMusic('bgmusic'); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Arrow = Container.expand(function (targetType) {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
// Set color based on target type
if (targetType === 'pickup') {
arrowGraphics.tint = 0x00ff00; // Green for package pickup
} else if (targetType === 'delivery') {
arrowGraphics.tint = 0xff0000; // Red for delivery house
}
self.targetType = targetType;
self.target = null;
self.update = function () {
if (self.target && player) {
// Calculate direction to target
var dx = self.target.x - player.x;
var dy = self.target.y - player.y;
var angle = Math.atan2(dy, dx);
// Position arrow around player
var radius = 80;
self.x = player.x + Math.cos(angle) * radius;
self.y = player.y + Math.sin(angle) * radius;
// Rotate arrow to point towards target
arrowGraphics.rotation = angle + Math.PI / 2;
// Pulse animation using tween
if (!self.tweening) {
self.tweening = true;
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.tweening = false;
}
});
}
});
}
}
};
return self;
});
var Car = Container.expand(function () {
var self = Container.call(this);
var carGraphics = self.attachAsset('car', {
anchorX: 0.5,
anchorY: 0.5
});
// Set car to move in one of four directions: up, down, left, right
var directions = [{
x: 0,
y: -1
},
// up
{
x: 0,
y: 1
},
// down
{
x: -1,
y: 0
},
// left
{
x: 1,
y: 0
} // right
];
var direction = directions[Math.floor(Math.random() * directions.length)];
self.speed = 2 + Math.random() * 2; // Speed between 2-4
self.directionX = direction.x;
self.directionY = direction.y;
self.update = function () {
var oldX = self.x;
var oldY = self.y;
// Move in straight line based on direction
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Calculate rotation based on movement direction
// Front of sprite is left side, so 0 rotation = moving left
var targetRotation = Math.atan2(self.directionY, self.directionX) + Math.PI;
// Smooth rotation using tween
tween.stop(carGraphics, {
rotation: true
});
tween(carGraphics, {
rotation: targetRotation
}, {
duration: 200,
easing: tween.easeOut
});
// Check collision with houses
var hitHouse = false;
for (var h = 0; h < houses.length; h++) {
if (self.intersects(houses[h])) {
hitHouse = true;
break;
}
}
// Check if trying to enter a block (non-street area)
var enteringBlock = !isOnStreet(self.x, self.y);
// If hit house or entering block, revert movement and change direction
if (hitHouse || enteringBlock) {
self.x = oldX;
self.y = oldY;
// Choose a new random direction
var newDirection = directions[Math.floor(Math.random() * directions.length)];
self.directionX = newDirection.x;
self.directionY = newDirection.y;
}
// Change direction at edges
if (self.x < 40 || self.x > 2008) {
self.directionX *= -1;
}
if (self.y < 40 || self.y > 2692) {
self.directionY *= -1;
}
// Keep in bounds
self.x = Math.max(40, Math.min(2008, self.x));
self.y = Math.max(40, Math.min(2692, self.y));
};
return self;
});
var Dog = Container.expand(function () {
var self = Container.call(this);
var dogGraphics = self.attachAsset('dog', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.direction = Math.random() * Math.PI * 2;
self.update = function () {
// Check distance to player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distanceToPlayer = Math.sqrt(dx * dx + dy * dy);
var detectionRange = 150;
if (distanceToPlayer < detectionRange && player) {
// Follow player behavior
var angleToPlayer = Math.atan2(dy, dx);
self.direction = angleToPlayer;
self.speed = 2.5; // Slightly faster when chasing
} else {
// Simple AI - change direction occasionally
if (Math.random() < 0.02) {
self.direction = Math.random() * Math.PI * 2;
}
self.speed = 2; // Normal speed when wandering
}
var oldX = self.x;
var oldY = self.y;
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
// Check collision with houses
var hitHouse = false;
for (var h = 0; h < houses.length; h++) {
if (self.intersects(houses[h])) {
hitHouse = true;
break;
}
}
// If hit house, revert movement and change direction
if (hitHouse) {
self.x = oldX;
self.y = oldY;
self.direction = Math.random() * Math.PI * 2;
}
// Bounce off edges
if (self.x < 15 || self.x > 2033) {
self.direction = Math.PI - self.direction;
}
if (self.y < 15 || self.y > 2717) {
self.direction = -self.direction;
}
// Keep in bounds
self.x = Math.max(15, Math.min(2033, self.x));
self.y = Math.max(15, Math.min(2717, self.y));
};
return self;
});
var House = Container.expand(function (houseId) {
var self = Container.call(this);
var houseGraphics = self.attachAsset('house', {
anchorX: 0.5,
anchorY: 0.5
});
self.houseId = houseId;
self.hasDelivery = false;
self.down = function (x, y, obj) {
if (player.hasPackage && self.distanceTo(player) < 80) {
if (player.targetHouse === self.houseId) {
// Correct delivery
LK.setScore(LK.getScore() + 100);
player.hasPackage = false;
player.targetHouse = null;
self.hasDelivery = true;
packagesDelivered++;
// Visual feedback
tween(self, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.getSound('delivery').play();
scoreText.setText(LK.getScore());
// Remove delivery arrow
if (deliveryArrow) {
deliveryArrow.destroy();
deliveryArrow = null;
}
// Create pickup arrow for next available package
if (packages.length > 0) {
pickupArrow = game.addChild(new Arrow('pickup'));
pickupArrow.target = packages[0];
}
// Check win condition
if (packagesDelivered >= totalPackages) {
if (currentLevel < 4) {
currentLevel++;
startLevel();
} else {
LK.showYouWin();
}
}
} else {
// Wrong delivery
LK.setScore(Math.max(0, LK.getScore() - 50));
scoreText.setText(LK.getScore());
LK.effects.flashObject(self, 0xff0000, 500);
}
}
};
self.distanceTo = function (other) {
var dx = self.x - other.x;
var dy = self.y - other.y;
return Math.sqrt(dx * dx + dy * dy);
};
return self;
});
var Package = Container.expand(function (targetHouseId) {
var self = Container.call(this);
var packageGraphics = self.attachAsset('package', {
anchorX: 0.5,
anchorY: 0.5
});
self.collected = false;
self.targetHouseId = targetHouseId; // Link package to specific house
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.hasPackage = false;
self.targetHouse = null;
return self;
});
var PowerUp = Container.expand(function (type) {
var self = Container.call(this);
self.type = type; // 'time', 'speed', 'shield'
self.collected = false;
// Use specific asset based on powerup type
var assetName = 'powerup'; // default
if (type === 'time') {
assetName = 'time_powerup';
} else if (type === 'speed') {
assetName = 'speed_powerup';
} else if (type === 'shield') {
assetName = 'shield_powerup';
} else if (type === 'life') {
assetName = 'life_powerup';
}
var powerupGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
// Game variables
var player;
var packages = [];
var houses = [];
var cars = [];
var dogs = [];
var powerups = [];
var currentLevel = 1;
var totalPackages = 3;
var packagesDelivered = 0;
var gameTime = 60000; // 60 seconds
var timeRemaining = gameTime;
var dragNode = null;
var lastCollisionCheck = {};
var targetX = null;
var targetY = null;
var isMoving = false;
var pickupArrow = null;
var deliveryArrow = null;
var lives = 3;
// Block system variables
var blockSize = 460; // 4x4 road tiles (115 * 4)
var blocksX = 4; // Number of blocks horizontally
var blocksY = 5; // Number of blocks vertically
var streetWidth = 115; // Width of streets between blocks
// Helper function to check if position is on a street (road)
function isOnStreet(x, y) {
var blockX = Math.floor(x / (blockSize + streetWidth));
var blockY = Math.floor(y / (blockSize + streetWidth));
var localX = x % (blockSize + streetWidth);
var localY = y % (blockSize + streetWidth);
// Check if position is in street area (between blocks)
return localX >= blockSize || localY >= blockSize;
}
// Helper function to get random street position
function getRandomStreetPosition() {
var attempts = 0;
var maxAttempts = 100;
while (attempts < maxAttempts) {
var x = Math.random() * 2048;
var y = Math.random() * 2732;
if (isOnStreet(x, y)) {
return {
x: x,
y: y
};
}
attempts++;
}
// Fallback to center street
return {
x: 1024,
y: 1366
};
}
// Helper function to get random block position for houses
function getRandomBlockPosition() {
var blockX = Math.floor(Math.random() * blocksX);
var blockY = Math.floor(Math.random() * blocksY);
var startX = blockX * (blockSize + streetWidth);
var startY = blockY * (blockSize + streetWidth);
var x = startX + 100 + Math.random() * (blockSize - 200);
var y = startY + 100 + Math.random() * (blockSize - 200);
return {
x: x,
y: y
};
}
// UI elements
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
var timeText = new Text2('Time: 60', {
size: 60,
fill: 0xFFFFFF
});
timeText.anchor.set(1, 0);
LK.gui.topRight.addChild(timeText);
var levelText = new Text2('Level 1', {
size: 60,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
LK.gui.topLeft.addChild(levelText);
var livesText = new Text2('Lives: 3', {
size: 60,
fill: 0xFFFFFF
});
livesText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(livesText);
// Create varied road background with rotation based on neighbors
var roadGrid = []; // Store road references for neighbor checking
// First pass: create all roads without rotation
for (var i = 0; i < 18; i++) {
roadGrid[i] = [];
for (var j = 0; j < 24; j++) {
var road = LK.getAsset('road', {
x: i * 115,
y: j * 115,
anchorX: 0,
anchorY: 0,
orientation: 0
});
roadGrid[i][j] = road;
game.addChild(road);
}
}
// Second pass: apply rotation based on neighbors and replace with intersections where needed
for (var i = 0; i < 18; i++) {
for (var j = 0; j < 24; j++) {
var road = roadGrid[i][j];
var hasLeftNeighbor = i > 0;
var hasRightNeighbor = i < 17;
var hasTopNeighbor = j > 0;
var hasBottomNeighbor = j < 23;
// Count how many directions have neighbors
var neighborCount = 0;
if (hasLeftNeighbor) neighborCount++;
if (hasRightNeighbor) neighborCount++;
if (hasTopNeighbor) neighborCount++;
if (hasBottomNeighbor) neighborCount++;
// Check if this should be an intersection (3 or 4 way intersection)
var isIntersection = neighborCount >= 3;
if (isIntersection) {
// Remove old road and create intersection
game.removeChild(road);
var intersection = LK.getAsset('road_intersection', {
x: i * 115,
y: j * 115,
anchorX: 0,
anchorY: 0
});
roadGrid[i][j] = intersection;
game.addChild(intersection);
} else {
// Determine orientation based on neighbor pattern
var orientation = 0;
// Horizontal road (connects left-right)
if ((hasLeftNeighbor || hasRightNeighbor) && !hasTopNeighbor && !hasBottomNeighbor) {
orientation = 1; // 90 degree rotation for horizontal orientation
}
// Vertical road (connects top-bottom) - default orientation
else if ((hasTopNeighbor || hasBottomNeighbor) && !hasLeftNeighbor && !hasRightNeighbor) {
orientation = 0; // Keep vertical orientation
}
// L-shaped corners and T-junctions
else if (hasLeftNeighbor && hasTopNeighbor && !hasRightNeighbor && !hasBottomNeighbor) {
orientation = 3; // Bottom-right corner
} else if (hasRightNeighbor && hasTopNeighbor && !hasLeftNeighbor && !hasBottomNeighbor) {
orientation = 2; // Bottom-left corner
} else if (hasLeftNeighbor && hasBottomNeighbor && !hasRightNeighbor && !hasTopNeighbor) {
orientation = 0; // Top-right corner
} else if (hasRightNeighbor && hasBottomNeighbor && !hasLeftNeighbor && !hasTopNeighbor) {
orientation = 1; // Top-left corner
}
// T-junctions (2 neighbors)
else if (hasLeftNeighbor && hasRightNeighbor && (hasTopNeighbor || hasBottomNeighbor)) {
orientation = 1; // Horizontal T-junction
} else if (hasTopNeighbor && hasBottomNeighbor && (hasLeftNeighbor || hasRightNeighbor)) {
orientation = 0; // Vertical T-junction
}
// Default cases - prefer horizontal for mixed patterns
else if (hasLeftNeighbor || hasRightNeighbor) {
orientation = 1; // Horizontal orientation
} else {
orientation = 0; // Vertical orientation (default)
}
// Set the correct orientation using the asset's orientation property
game.removeChild(road);
var orientedRoad = LK.getAsset('road', {
x: i * 115,
y: j * 115,
anchorX: 0,
anchorY: 0,
orientation: orientation
});
roadGrid[i][j] = orientedRoad;
game.addChild(orientedRoad);
}
}
}
// Create street blocks in the block areas for city-like appearance
for (var blockX = 0; blockX < blocksX; blockX++) {
for (var blockY = 0; blockY < blocksY; blockY++) {
var startX = blockX * (blockSize + streetWidth);
var startY = blockY * (blockSize + streetWidth);
// Add street block asset in the center of each block
var streetBlock = LK.getAsset('street_block', {
x: startX + blockSize / 2,
y: startY + blockSize / 2,
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(streetBlock);
}
}
function startLevel() {
// Clear existing game objects
for (var i = packages.length - 1; i >= 0; i--) {
packages[i].destroy();
}
for (var i = houses.length - 1; i >= 0; i--) {
houses[i].destroy();
}
for (var i = cars.length - 1; i >= 0; i--) {
cars[i].destroy();
}
for (var i = dogs.length - 1; i >= 0; i--) {
dogs[i].destroy();
}
for (var i = powerups.length - 1; i >= 0; i--) {
powerups[i].destroy();
}
// Clear arrow indicators
if (pickupArrow) {
pickupArrow.destroy();
pickupArrow = null;
}
if (deliveryArrow) {
deliveryArrow.destroy();
deliveryArrow = null;
}
packages = [];
houses = [];
cars = [];
dogs = [];
powerups = [];
packagesDelivered = 0;
// Reset time
timeRemaining = gameTime;
// Update UI
levelText.setText('Level ' + currentLevel);
// Create player if not exists
if (!player) {
player = game.addChild(new Player());
// Find a safe spawn position that doesn't overlap with houses
var playerSpawned = false;
var attempts = 0;
var maxAttempts = 50;
var minPlayerDistance = 150; // Minimum distance from houses
while (!playerSpawned && attempts < maxAttempts) {
var spawnX = 200 + Math.random() * 1600;
var spawnY = 200 + Math.random() * 1200;
var positionClear = true;
// Check if spawn position is clear of houses
for (var j = 0; j < houses.length; j++) {
var dx = spawnX - houses[j].x;
var dy = spawnY - houses[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minPlayerDistance) {
positionClear = false;
break;
}
}
if (positionClear) {
player.x = spawnX;
player.y = spawnY;
playerSpawned = true;
}
attempts++;
}
// Fallback to default position if no safe spot found
if (!playerSpawned) {
player.x = 1024;
player.y = 2366;
}
}
player.hasPackage = false;
player.targetHouse = null;
player.hasShield = false;
// Helper function to check if position overlaps with existing houses
function isPositionClear(x, y, minDistance) {
for (var j = 0; j < houses.length; j++) {
var dx = x - houses[j].x;
var dy = y - houses[j].y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
return false;
}
}
return true;
}
// Create packages and houses
totalPackages = 2 + currentLevel;
for (var i = 0; i < totalPackages; i++) {
// Create package with specific target house ID
var pkg = new Package(i);
pkg.x = 200 + Math.random() * 1600;
pkg.y = 200 + Math.random() * 1200;
packages.push(pkg);
game.addChild(pkg);
}
// Create corresponding house with overlap prevention
// Create pickup arrow indicator pointing to first available package
if (packages.length > 0) {
pickupArrow = game.addChild(new Arrow('pickup'));
pickupArrow.target = packages[0];
}
// Create packages and houses in blocks
for (var i = 0; i < totalPackages; i++) {
var house = new House(i);
var attempts = 0;
var maxAttempts = 50;
var minHouseDistance = 200; // Minimum distance between houses
do {
var blockPos = getRandomBlockPosition();
house.x = blockPos.x;
house.y = blockPos.y;
attempts++;
} while (!isPositionClear(house.x, house.y, minHouseDistance) && attempts < maxAttempts);
houses.push(house);
game.addChild(house);
}
// Create cars (more with each level) - spawn only on streets
var numCars = currentLevel + 1;
for (var i = 0; i < numCars; i++) {
var car = new Car();
var streetPos = getRandomStreetPosition();
car.x = streetPos.x;
car.y = streetPos.y;
cars.push(car);
game.addChild(car);
}
// Create dogs
var numDogs = Math.floor(currentLevel / 2) + 1;
for (var i = 0; i < numDogs; i++) {
var dog = new Dog();
dog.x = Math.random() * 2048;
dog.y = Math.random() * 2732;
dogs.push(dog);
game.addChild(dog);
}
// Create power-ups
var powerupTypes = ['time', 'speed', 'shield', 'life'];
for (var i = 0; i < 2; i++) {
var powerup = new PowerUp(powerupTypes[Math.floor(Math.random() * powerupTypes.length)]);
powerup.x = Math.random() * 2048;
powerup.y = Math.random() * 2732;
powerups.push(powerup);
game.addChild(powerup);
}
}
function handleMove(x, y, obj) {
// Set target position for smooth movement
targetX = Math.max(30, Math.min(2018, x));
targetY = Math.max(30, Math.min(2702, y));
isMoving = true;
// Orient player sprite based on mouse direction
if (player) {
var dx = targetX - player.x;
if (dx > 0) {
// Moving right - face right (normal orientation)
player.scaleX = 1;
} else if (dx < 0) {
// Moving left - face left (flip horizontally)
player.scaleX = -1;
}
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
// Check if clicking on package to pick up
for (var i = 0; i < packages.length; i++) {
var pkg = packages[i];
if (!pkg.collected && pkg.intersects && player.intersects(pkg)) {
if (!player.hasPackage) {
pkg.collected = true;
pkg.destroy();
packages.splice(i, 1);
player.hasPackage = true;
// Use the package's assigned target house
player.targetHouse = pkg.targetHouseId;
LK.getSound('pickup').play();
// Remove pickup arrow and create delivery arrow
if (pickupArrow) {
pickupArrow.destroy();
pickupArrow = null;
}
// Create delivery arrow pointing to target house
if (player.targetHouse !== null && player.targetHouse < houses.length) {
deliveryArrow = game.addChild(new Arrow('delivery'));
deliveryArrow.target = houses[player.targetHouse];
}
return;
}
}
}
handleMove(x, y, obj);
};
game.up = function (x, y, obj) {
// Mouse up - no action needed for smooth movement
};
game.update = function () {
// Smooth player movement towards target
if (isMoving && targetX !== null && targetY !== null) {
var dx = targetX - player.x;
var dy = targetY - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 5) {
// Move towards target with player speed
var moveX = dx / distance * player.speed;
var moveY = dy / distance * player.speed;
var oldX = player.x;
var oldY = player.y;
player.x += moveX;
player.y += moveY;
// Check collision with houses
var hitHouse = false;
var hitTargetHouse = false;
for (var h = 0; h < houses.length; h++) {
if (player.intersects(houses[h])) {
hitHouse = true;
// Check if this is the target house for delivery
if (player.hasPackage && player.targetHouse === h) {
hitTargetHouse = true;
}
break;
}
}
// If hit house, revert movement unless it's the target house for delivery
if (hitHouse && !hitTargetHouse) {
player.x = oldX;
player.y = oldY;
isMoving = false; // Stop movement when hitting house
}
} else {
// Close enough to target, stop moving
player.x = targetX;
player.y = targetY;
isMoving = false;
}
}
// Update time
timeRemaining -= 16.67; // Approximate 60fps
var seconds = Math.ceil(timeRemaining / 1000);
timeText.setText('Time: ' + Math.max(0, seconds));
// Check time up
if (timeRemaining <= 0) {
LK.showGameOver();
return;
}
// Check package pickup
for (var i = packages.length - 1; i >= 0; i--) {
var pkg = packages[i];
if (!pkg.collected && player.intersects(pkg)) {
if (!player.hasPackage) {
pkg.collected = true;
pkg.destroy();
packages.splice(i, 1);
player.hasPackage = true;
// Use the package's assigned target house
player.targetHouse = pkg.targetHouseId;
LK.getSound('pickup').play();
// Remove pickup arrow and create delivery arrow
if (pickupArrow) {
pickupArrow.destroy();
pickupArrow = null;
}
// Create delivery arrow pointing to target house
if (player.targetHouse !== null && player.targetHouse < houses.length) {
deliveryArrow = game.addChild(new Arrow('delivery'));
deliveryArrow.target = houses[player.targetHouse];
}
}
}
}
// Check package delivery when player collides with house
if (player.hasPackage && player.targetHouse !== null) {
for (var h = 0; h < houses.length; h++) {
var house = houses[h];
var houseKey = 'house_delivery_' + h;
var currentCollision = player.intersects(house);
if (!lastCollisionCheck[houseKey] && currentCollision) {
// Just started colliding with house
if (player.targetHouse === h) {
// Correct delivery
LK.setScore(LK.getScore() + 100);
player.hasPackage = false;
player.targetHouse = null;
house.hasDelivery = true;
packagesDelivered++;
// Visual feedback
tween(house, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(house, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.getSound('delivery').play();
scoreText.setText(LK.getScore());
// Remove delivery arrow
if (deliveryArrow) {
deliveryArrow.destroy();
deliveryArrow = null;
}
// Create pickup arrow for next available package
if (packages.length > 0) {
pickupArrow = game.addChild(new Arrow('pickup'));
pickupArrow.target = packages[0];
}
// Check win condition
if (packagesDelivered >= totalPackages) {
if (currentLevel < 4) {
currentLevel++;
startLevel();
} else {
LK.showYouWin();
}
}
break; // Exit loop after successful delivery
} else {
// Wrong delivery
LK.setScore(Math.max(0, LK.getScore() - 50));
scoreText.setText(LK.getScore());
LK.effects.flashObject(house, 0xff0000, 500);
}
}
lastCollisionCheck[houseKey] = currentCollision;
}
}
// Check powerup collection
for (var i = powerups.length - 1; i >= 0; i--) {
var powerup = powerups[i];
if (!powerup.collected && player.intersects(powerup)) {
powerup.collected = true;
powerup.destroy();
powerups.splice(i, 1);
if (powerup.type === 'time') {
timeRemaining += 10000; // Add 10 seconds
} else if (powerup.type === 'speed') {
player.speed = 8;
LK.setTimeout(function () {
player.speed = 5;
}, 5000);
} else if (powerup.type === 'shield') {
player.hasShield = true;
// Visual shield effect
tween(player, {
tint: 0x00FFFF
}, {
duration: 200
});
LK.setTimeout(function () {
player.hasShield = false;
tween(player, {
tint: 0xFFFFFF
}, {
duration: 200
});
}, 8000); // Shield lasts 8 seconds
} else if (powerup.type === 'life') {
lives++;
livesText.setText('Lives: ' + lives);
}
LK.getSound('powerup').play();
LK.setScore(LK.getScore() + 25);
scoreText.setText(LK.getScore());
}
}
// Check collisions with obstacles
for (var i = 0; i < cars.length; i++) {
var car = cars[i];
var carKey = 'car_' + i;
var currentCollision = player.intersects(car);
if (!lastCollisionCheck[carKey] && currentCollision) {
// Just started colliding
if (!player.hasShield) {
lives--;
livesText.setText('Lives: ' + lives);
if (lives <= 0) {
LK.showGameOver();
return;
}
LK.setScore(Math.max(0, LK.getScore() - 25));
scoreText.setText(LK.getScore());
LK.effects.flashObject(player, 0xff0000, 500);
}
}
lastCollisionCheck[carKey] = currentCollision;
}
for (var i = 0; i < dogs.length; i++) {
var dog = dogs[i];
var dogKey = 'dog_' + i;
var currentCollision = player.intersects(dog);
if (!lastCollisionCheck[dogKey] && currentCollision) {
// Just started colliding
if (!player.hasShield) {
lives--;
livesText.setText('Lives: ' + lives);
if (lives <= 0) {
LK.showGameOver();
return;
}
LK.setScore(Math.max(0, LK.getScore() - 15));
scoreText.setText(LK.getScore());
LK.effects.flashObject(player, 0xff0000, 300);
}
}
lastCollisionCheck[dogKey] = currentCollision;
}
};
// Start the first level
startLevel();
// Play background music
LK.playMusic('bgmusic');
shield icon inside of a blue energy bubble. In-Game asset. 2d. High contrast. No shadows
1+ up heart icon. In-Game asset. 2d. High contrast. No shadows
fast foward icon. In-Game asset. 2d. High contrast. No shadows
+10 inside of a clock icon. In-Game asset. 2d. High contrast. No shadows
Street block, garden with concrete walkway on the border. In-Game asset. 2d. High contrast. No shadows