/**** * 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