/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { hotel0Rating: 0, hotel1Rating: 0, hotel2Rating: 0, hotel3Rating: 0, hotel4Rating: 0, hotel0RatingCount: 0, hotel1RatingCount: 0, hotel2RatingCount: 0, hotel3RatingCount: 0, hotel4RatingCount: 0, highestBudget: 1000 }); /**** * Classes ****/ var AddRoomButton = Container.expand(function () { var self = Container.call(this); var buttonBg = self.attachAsset('add_room_button', { anchorX: 0.5, anchorY: 0.5 }); self.buttonText = self.addChild(new Text2('Add Room', { size: 50, fill: 0xFFFFFF })); self.buttonText.anchor.set(0.5, 0.5); self.buttonText.x = 0; self.buttonText.y = 0; self.down = function (x, y, obj) { if (totalRooms < 60 && hotelCounts[currentHotelIndex] < 12 && budget >= 175) { if (hotels[currentHotelIndex].addRoom()) { totalRooms++; hotelCounts[currentHotelIndex]++; budget -= 175; // Deduct $175 for each room updateUI(); } } }; self.updateButton = function () { // Check if there are damaged rooms in current hotel var damagedRooms = []; var currentHotelObj = hotels[currentHotelIndex]; if (currentHotelObj.roomDamaged) { for (var i = 0; i < currentHotelObj.roomDamaged.length; i++) { if (currentHotelObj.roomDamaged[i]) { var repairCost = currentHotelObj.roomRepairCosts && currentHotelObj.roomRepairCosts[i] || 0; damagedRooms.push('Room ' + (i + 1) + ': $' + repairCost); } } } if (damagedRooms.length > 0) { buttonBg.tint = 0xff4444; // Red tint to indicate repairs needed self.buttonText.setText('Repairs Needed'); } else if (totalRooms >= 60 || hotelCounts[currentHotelIndex] >= 12 || budget < 175) { buttonBg.tint = 0x666666; if (hotelCounts[currentHotelIndex] >= 12) { self.buttonText.setText('Hotel Full'); } else if (budget < 175) { self.buttonText.setText('Need $175'); } else { self.buttonText.setText('Limit Reached'); } } else { buttonBg.tint = 0xffffff; self.buttonText.setText('Add Room'); } // Ensure text is vertically centered in button self.buttonText.y = 0; }; return self; }); var Customer = Container.expand(function () { var self = Container.call(this); var customerGraphics = self.attachAsset('customer', { anchorX: 0.5, anchorY: 1.0 }); self.isAtHotel = true; self.isMoving = false; self.moveToGroceryStore = function () { if (self.isMoving || !self.isAtHotel) return; self.isMoving = true; tween(self, { y: 2400 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { self.isAtHotel = false; self.isMoving = false; // Free the room when customer leaves if (self.occupiedRoomIndex !== undefined) { hotels[currentHotelIndex].setRoomOccupation(self.occupiedRoomIndex, false); self.occupiedRoomIndex = undefined; } // Stay at grocery store for a moment, then return LK.setTimeout(function () { self.returnToHotel(); }, 1500); } }); }; self.returnToHotel = function () { if (self.isMoving || self.isAtHotel) return; self.isMoving = true; // Calculate target position based on room location (customer already has a room) var targetX = 1024; // Default hotel center var targetY = 1366; // Default hotel center if (self.occupiedRoomIndex !== undefined) { // Re-occupy the same room when returning hotels[currentHotelIndex].setRoomOccupation(self.occupiedRoomIndex, true); // Calculate room position using same logic as in Hotel.updateDisplay var positions = []; for (var row = 0; row < 4; row++) { for (var col = 0; col < 3; col++) { positions.push({ x: (col - 1) * 240, y: (row - 1.5) * 240 }); } } if (self.occupiedRoomIndex < positions.length) { targetX = 1024 + positions[self.occupiedRoomIndex].x; // Add hotel offset targetY = 1366 + positions[self.occupiedRoomIndex].y; // Add hotel offset } } tween(self, { x: targetX, y: targetY }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { self.isAtHotel = true; self.isMoving = false; // Complete scoring task and give rating if (self.scoringTask && !self.hasCompletedTask) { self.hasCompletedTask = true; var taskSuccess = checkTaskCompletion(self.scoringTask); var rating = taskSuccess ? Math.floor(Math.random() * 2) + 4 : Math.floor(Math.random() * 3) + 1; // 4-5 for success, 1-3 for failure // Calculate base payment proportional to rating ($10-$100 based on 1-5 star rating) var basePayment = Math.floor(rating / 5 * 90) + 10; // Maps 1-5 stars to $10-$100 // Add shop purchases to total payment var totalPayment = basePayment + (self.totalSpent || 0); budget += totalPayment; // Update highest budget reached and save as score if (budget > highestBudget) { highestBudget = budget; storage.highestBudget = highestBudget; LK.setScore(secretHotelName + ': $' + highestBudget); } // Auto-rate the hotel based on task completion var ratingKey = 'hotel' + currentHotelIndex + 'Rating'; var countKey = 'hotel' + currentHotelIndex + 'RatingCount'; var currentRating = storage[ratingKey] || 0; var currentCount = storage[countKey] || 0; var totalScore = currentRating * currentCount + rating; var newCount = currentCount + 1; var newRating = totalScore / newCount; storage[ratingKey] = newRating; storage[countKey] = newCount; updateUI(); } } }); }; return self; }); var GroceryStore = Container.expand(function () { var self = Container.call(this); var storeBg = self.attachAsset('grocery_store', { anchorX: 0.5, anchorY: 0.5 }); // Grocery store text hidden return self; }); var Hotel = Container.expand(function () { var self = Container.call(this); // Hotel background var hotelBg = self.attachAsset('hotel_bg', { anchorX: 0.5, anchorY: 0.5 }); // Properties self.hotelId = 1; self.roomCount = 0; self.rooms = []; self.occupiedRooms = []; // Track which rooms are occupied self.roomDamageCounters = []; // Track guest count for each room self.roomDamaged = []; // Track which rooms are damaged self.setHotel = function (id) { self.hotelId = id; self.updateDisplay(); }; self.addRoom = function () { if (self.roomCount < 12) { // Max 12 rooms per hotel (3x4 grid) var room = self.addChild(LK.getAsset('room_empty', { anchorX: 0.5, anchorY: 0.5 })); // Position rooms in 3x4 grid layout (3 columns, 4 rows) var positions = []; for (var row = 0; row < 4; row++) { for (var col = 0; col < 3; col++) { positions.push({ x: (col - 1) * 240, // Center the grid: -240, 0, 240 y: (row - 1.5) * 240 // Center vertically around hotel with larger gaps }); } } var targetX = positions[self.roomCount].x; var targetY = positions[self.roomCount].y; // Check for overlapping with existing rooms var isOverlapping = false; for (var i = 0; i < self.rooms.length; i++) { var existingRoom = self.rooms[i]; var distanceX = Math.abs(existingRoom.x - targetX); var distanceY = Math.abs(existingRoom.y - targetY); // Check if rooms would overlap (room width is 150, height is 112.5, grid spacing is 240x240) if (distanceX < 240 && distanceY < 240) { isOverlapping = true; break; } } // If overlapping, try to find an alternative position if (isOverlapping) { var foundPosition = false; // Try different directional positions for (var tryPos = 0; tryPos < positions.length && !foundPosition; tryPos++) { if (tryPos === self.roomCount) continue; // Skip the current position targetX = positions[tryPos].x; targetY = positions[tryPos].y; isOverlapping = false; // Check this position against all existing rooms for (var i = 0; i < self.rooms.length; i++) { var existingRoom = self.rooms[i]; var distanceX = Math.abs(existingRoom.x - targetX); var distanceY = Math.abs(existingRoom.y - targetY); if (distanceX < 240 && distanceY < 240) { isOverlapping = true; break; } } if (!isOverlapping) { foundPosition = true; } } // If no position found, don't add the room if (!foundPosition) { room.destroy(); return false; } } room.x = targetX; room.y = targetY; // Room starts as empty (using room_empty asset) self.rooms.push(room); self.occupiedRooms.push(false); // Room starts as empty self.roomDamageCounters.push(0); // Initialize damage counter self.roomDamaged.push(false); // Initialize as not damaged self.roomCount++; return true; } return false; }; self.updateDisplay = function () { // Clear existing rooms for (var i = 0; i < self.rooms.length; i++) { self.rooms[i].destroy(); } self.rooms = []; // Keep damage tracking arrays intact during display updates // Add rooms based on current count using 3x4 grid layout var positions = []; for (var row = 0; row < 4; row++) { for (var col = 0; col < 3; col++) { positions.push({ x: (col - 1) * 240, // Center the grid: -240, 0, 240 y: (row - 1.5) * 240 // Center vertically around hotel with larger gaps }); } } for (var i = 0; i < self.roomCount; i++) { // Use appropriate room asset based on damage and occupation status var assetName; if (self.roomDamaged[i]) { assetName = 'room_broken'; } else { assetName = self.occupiedRooms[i] ? 'room_full' : 'room_empty'; } var room = self.addChild(LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5 })); room.x = positions[i].x; room.y = positions[i].y; // Add click interaction to handle room repairs or occupation room.roomIndex = i; room.down = function (x, y, obj) { if (self.roomDamaged[this.roomIndex]) { self.repairRoom(this.roomIndex); } else { self.toggleRoomOccupation(this.roomIndex); } }; self.rooms.push(room); } }; self.toggleRoomOccupation = function (roomIndex) { if (roomIndex >= 0 && roomIndex < self.roomCount) { self.occupiedRooms[roomIndex] = !self.occupiedRooms[roomIndex]; // Update room visual by refreshing display self.updateDisplay(); } }; self.setRoomOccupation = function (roomIndex, isOccupied) { if (roomIndex >= 0 && roomIndex < self.roomCount) { self.occupiedRooms[roomIndex] = isOccupied; // If room is being occupied, increment damage counter if (isOccupied) { self.roomDamageCounters[roomIndex]++; // Random chance for room to get damaged after guest stays var damageThreshold = Math.floor(Math.random() * 8) + 3; // 3-10 guests before damage if (self.roomDamageCounters[roomIndex] >= damageThreshold && !self.roomDamaged[roomIndex]) { self.roomDamaged[roomIndex] = true; // Generate repair cost between $20-$300 var repairCost = Math.floor(Math.random() * 281) + 20; self.roomRepairCosts = self.roomRepairCosts || []; self.roomRepairCosts[roomIndex] = repairCost; } } // Update room visual by refreshing display self.updateDisplay(); } }; self.repairRoom = function (roomIndex) { if (roomIndex >= 0 && roomIndex < self.roomCount && self.roomDamaged[roomIndex]) { self.roomRepairCosts = self.roomRepairCosts || []; var repairCost = self.roomRepairCosts[roomIndex] || 0; if (budget >= repairCost) { budget -= repairCost; self.roomDamaged[roomIndex] = false; self.roomDamageCounters[roomIndex] = 0; // Reset damage counter delete self.roomRepairCosts[roomIndex]; updateUI(); self.updateDisplay(); } } }; return self; }); var NavigationButton = Container.expand(function () { var self = Container.call(this); self.isLeft = true; self.buttonBg = null; self.buttonText = null; self.setDirection = function (isLeft) { self.isLeft = isLeft; // Remove existing background if it exists if (self.buttonBg) { self.removeChild(self.buttonBg); } // Create new background with appropriate asset var assetName = isLeft ? 'nav_button_left' : 'nav_button_right'; self.buttonBg = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); }; // Initialize with default direction self.setDirection(true); self.setText = function (text) { if (self.buttonText) { self.buttonText.destroy(); } self.buttonText = self.addChild(new Text2(text, { size: 40, fill: 0xFFFFFF })); self.buttonText.anchor.set(0.5, 0.5); }; self.down = function (x, y, obj) { if (self.isLeft) { currentHotelIndex = (currentHotelIndex - 1 + 5) % 5; } else { currentHotelIndex = (currentHotelIndex + 1) % 5; } updateHotelDisplay(); }; return self; }); var RatingButton = Container.expand(function () { var self = Container.call(this); self.stars = []; self.hotelIndex = 0; self.currentRating = 0; // Create 5 star buttons for (var i = 0; i < 5; i++) { var star = self.addChild(LK.getAsset('nav_button_left', { anchorX: 0.5, anchorY: 0.5 })); star.scaleX = 0.6; star.scaleY = 0.6; star.x = (i - 2) * 80; // Space stars horizontally star.tint = 0x666666; // Gray by default star.starIndex = i + 1; // Add star text var starText = star.addChild(new Text2('★', { size: 30, fill: 0xFFFFFF })); starText.anchor.set(0.5, 0.5); star.down = function (x, y, obj) { self.setRating(this.starIndex); }; self.stars.push(star); } self.setHotelIndex = function (index) { self.hotelIndex = index; self.updateDisplay(); }; self.setRating = function (rating) { self.currentRating = rating; // Update visual stars for (var i = 0; i < 5; i++) { if (i < rating) { self.stars[i].tint = 0xFFD700; // Gold for selected stars } else { self.stars[i].tint = 0x666666; // Gray for unselected } } // Save rating to storage var ratingKey = 'hotel' + self.hotelIndex + 'Rating'; var countKey = 'hotel' + self.hotelIndex + 'RatingCount'; var currentRating = storage[ratingKey] || 0; var currentCount = storage[countKey] || 0; // Calculate new average rating var totalScore = currentRating * currentCount + rating; var newCount = currentCount + 1; var newRating = totalScore / newCount; storage[ratingKey] = newRating; storage[countKey] = newCount; // Update UI updateUI(); }; self.updateDisplay = function () { var ratingKey = 'hotel' + self.hotelIndex + 'Rating'; var rating = storage[ratingKey] || 0; // Show current average rating for (var i = 0; i < 5; i++) { if (i < Math.round(rating)) { self.stars[i].tint = 0xFFD700; // Gold for rated stars } else { self.stars[i].tint = 0x666666; // Gray for unrated } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ // Game state variables var hotels = []; var currentHotelIndex = 0; var totalRooms = 0; var hotelCounts = [0, 0, 0, 0, 0]; var budget = 1000; var highestBudget = storage.highestBudget || 1000; var hotelNames = ['Grand Palace', 'Ocean View', 'Mountain Lodge', 'City Center', 'Royal Garden']; var secretHotelName = hotelNames[Math.floor(Math.random() * hotelNames.length)]; // UI elements var hotelNameText; var currentRoomsText; var totalRoomsText; var budgetText; var ratingText; var leftButton; var rightButton; var addRoomButton; var ratingButton; var currentHotel; // Initialize hotels for (var i = 0; i < 5; i++) { hotels.push(new Hotel()); } // Add 1 room to the first hotel at game start if (hotels[0].addRoom()) { totalRooms++; hotelCounts[0]++; } // Initialize hotel counts to match hotel room counts for (var i = 0; i < 5; i++) { hotelCounts[i] = hotels[i].roomCount; } // Add neighborhood background behind hotel var neighborhoodBg = game.addChild(LK.getAsset('neighborhood_bg', { anchorX: 0.5, anchorY: 0.5 })); neighborhoodBg.x = 1024; neighborhoodBg.y = 1000; // Position behind hotel area // Add road below hotel - create multiple segments horizontally var roads = []; var roadCount = 6; // Number of road segments var roadWidth = 400; // Width of each road segment var startX = 1024 - roadCount * roadWidth / 2 + roadWidth / 2; // Center the road pattern for (var r = 0; r < roadCount; r++) { var road = game.addChild(LK.getAsset('road', { anchorX: 0.5, anchorY: 0.5 })); road.x = startX + r * roadWidth; road.y = 2200; // Position below hotel area roads.push(road); } // Position current hotel in center currentHotel = game.addChild(hotels[0]); currentHotel.x = 1024; currentHotel.y = 1366; // Create navigation buttons leftButton = game.addChild(new NavigationButton()); leftButton.setDirection(true); leftButton.setText('<'); leftButton.x = 400; leftButton.y = 1366; rightButton = game.addChild(new NavigationButton()); rightButton.setDirection(false); rightButton.setText('>'); rightButton.x = 1648; rightButton.y = 1366; // Create add room button addRoomButton = game.addChild(new AddRoomButton()); addRoomButton.x = 1024; addRoomButton.y = 2020; // Create rating button (hidden) ratingButton = new RatingButton(); ratingButton.x = 1024; ratingButton.y = 2100; ratingButton.setHotelIndex(0); // Create UI text elements hotelNameText = new Text2('Hotel 1', { size: 80, fill: 0xFFFFFF }); hotelNameText.anchor.set(0.5, 0); LK.gui.top.addChild(hotelNameText); hotelNameText.y = 150; currentRoomsText = new Text2('Rooms in this hotel: 0', { size: 50, fill: 0xFFFFFF }); currentRoomsText.anchor.set(0.5, 0); LK.gui.top.addChild(currentRoomsText); currentRoomsText.y = 250; totalRoomsText = new Text2('Total rooms: 0/60', { size: 50, fill: 0xFFFFFF }); totalRoomsText.anchor.set(0.5, 0); LK.gui.top.addChild(totalRoomsText); totalRoomsText.y = 320; budgetText = new Text2('Budget: $1000', { size: 50, fill: 0xFFFFFF }); budgetText.anchor.set(0, 0); LK.gui.topLeft.addChild(budgetText); budgetText.x = 120; budgetText.y = 50; ratingText = new Text2('Rating: 0.0/5 (0 reviews)', { size: 50, fill: 0xFFFFFF }); ratingText.anchor.set(0.5, 0); ratingText.y = 390; function updateHotelDisplay() { // Remove current hotel game.removeChild(currentHotel); // Add new hotel currentHotel = game.addChild(hotels[currentHotelIndex]); currentHotel.x = 1024; currentHotel.y = 1366; currentHotel.setHotel(currentHotelIndex + 1); updateUI(); } function updateUI() { hotelNameText.setText('Hotel ' + (currentHotelIndex + 1)); currentRoomsText.setText('Rooms in this hotel: ' + hotelCounts[currentHotelIndex]); totalRoomsText.setText('Total rooms: ' + totalRooms + '/60'); budgetText.setText('Budget: $' + budget); addRoomButton.updateButton(); // Update rating display (hidden) var ratingKey = 'hotel' + currentHotelIndex + 'Rating'; var countKey = 'hotel' + currentHotelIndex + 'RatingCount'; var rating = storage[ratingKey] || 0; var count = storage[countKey] || 0; } // Update hotel counts when rooms are added function updateHotelCounts() { totalRooms = 0; for (var i = 0; i < 5; i++) { hotelCounts[i] = hotels[i].roomCount; totalRooms += hotelCounts[i]; } } game.update = function () { updateHotelCounts(); // Spawn customers at random intervals when game starts customerSpawnTimer++; var randomSpawnDelay = Math.floor(Math.random() * 120) + 60; // Random between 1-3 seconds (60-180 frames) // Spawn customers regardless of room availability if (customerSpawnTimer >= randomSpawnDelay && hotelCounts[currentHotelIndex] > 0 && customers.length < 8) { customerSpawnTimer = 0; spawnCustomer(); } // Clean up customers that have been around too long for (var i = customers.length - 1; i >= 0; i--) { // Remove customers after they've made several trips (optional cleanup) if (Math.random() < 0.001) { // Very small chance to remove each frame customers[i].destroy(); customers.splice(i, 1); } } }; // Create grocery store at bottom of screen var groceryStore = game.addChild(new GroceryStore()); groceryStore.x = 1024; groceryStore.y = 2500; // Shop items that customers can purchase var shopItems = [{ name: 'Room Service', price: 25 }, { name: 'Spa Treatment', price: 80 }, { name: 'Restaurant Meal', price: 45 }, { name: 'Minibar Snacks', price: 15 }, { name: 'Laundry Service', price: 20 }, { name: 'WiFi Premium', price: 10 }, { name: 'Pool Access', price: 30 }, { name: 'Gym Access', price: 25 }, { name: 'Parking', price: 35 }, { name: 'Late Checkout', price: 40 }, { name: 'Room Upgrade', price: 75 }, { name: 'Airport Shuttle', price: 50 }, { name: 'Tour Guide', price: 90 }, { name: 'Concierge Service', price: 60 }, { name: 'Baby Crib', price: 20 }]; // Customer management var customers = []; var customerSpawnTimer = Math.floor(Math.random() * 150); // Start with random delay function spawnCustomer() { var customer = game.addChild(new Customer()); // Spawn from left or right side of the road var fromLeft = Math.random() < 0.5; if (fromLeft) { customer.x = -100; // Start off-screen on the left } else { customer.x = 2148; // Start off-screen on the right (2048 + 100) } customer.y = 2200; // Start at road level customers.push(customer); // Find an available room position for the customer (excluding damaged rooms) var availableRooms = []; for (var roomIdx = 0; roomIdx < hotels[currentHotelIndex].roomCount; roomIdx++) { if (!hotels[currentHotelIndex].occupiedRooms[roomIdx] && !hotels[currentHotelIndex].roomDamaged[roomIdx]) { availableRooms.push(roomIdx); } } var roomIndex = -1; // Default: no room assigned var hasRoom = availableRooms.length > 0; if (hasRoom) { // Assign the first available room roomIndex = availableRooms[0]; customer.occupiedRoomIndex = roomIndex; } // Calculate target position - always go to hotel center first var targetX = 1024; // Default hotel center var targetY = 1366; // Default hotel center // If customer has a room, calculate specific room position if (hasRoom && roomIndex >= 0) { // Calculate room position using same logic as in Hotel.updateDisplay var positions = []; for (var row = 0; row < 4; row++) { for (var col = 0; col < 3; col++) { positions.push({ x: (col - 1) * 240, y: (row - 1.5) * 240 }); } } if (roomIndex < positions.length) { targetX = 1024 + positions[roomIndex].x; // Add hotel offset targetY = 1366 + positions[roomIndex].y; // Add hotel offset } } // First move customer to center of road tween(customer, { x: 1024, // Move to center of screen horizontally y: 2200 // Stay at road level }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Then move customer to the specific room location tween(customer, { x: targetX, y: targetY }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Customer has arrived at hotel - now check if they have a room if (!hasRoom) { // No room available - give $30 compensation and make customer leave budget -= 30; updateUI(); // Make customer leave immediately via the road var exitX = customer.x < 1024 ? -100 : 2148; // Exit same side they came from tween(customer, { x: exitX, y: 2200 // Move back to road level }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Remove customer for (var i = customers.length - 1; i >= 0; i--) { if (customers[i] === customer) { customers.splice(i, 1); break; } } customer.destroy(); } }); return; } // Customer has a room - occupy it hotels[currentHotelIndex].setRoomOccupation(customer.occupiedRoomIndex, true); // Give customer a scoring task when they arrive at hotel customer.scoringTask = generateScoringTask(); customer.hasCompletedTask = false; // Generate shop purchases for this customer customer.shopPurchases = generateShopPurchases(); customer.totalSpent = 0; for (var p = 0; p < customer.shopPurchases.length; p++) { customer.totalSpent += customer.shopPurchases[p].price; } // Customer goes to grocery store after arriving at hotel and a random delay LK.setTimeout(function () { customer.moveToGroceryStore(); }, Math.random() * 3000 + 1000); } }); } }); } function generateShopPurchases() { var purchases = []; var numItems = Math.floor(Math.random() * 10) + 1; // 1-10 items var usedItems = []; for (var i = 0; i < numItems; i++) { var availableItems = []; for (var j = 0; j < shopItems.length; j++) { if (usedItems.indexOf(j) === -1) { availableItems.push(j); } } if (availableItems.length > 0) { var randomIndex = availableItems[Math.floor(Math.random() * availableItems.length)]; purchases.push(shopItems[randomIndex]); usedItems.push(randomIndex); } } return purchases; } function generateScoringTask() { var tasks = [{ type: 'room_count', target: Math.floor(Math.random() * 10) + 5, description: 'Wants at least X rooms' }, { type: 'rating', target: Math.random() * 3 + 2, description: 'Expects rating above X' }, { type: 'budget', target: Math.floor(Math.random() * 500) + 200, description: 'Hotel should have budget above $X' }]; var task = tasks[Math.floor(Math.random() * tasks.length)]; // Customize target based on task type if (task.type === 'room_count') { task.target = Math.min(task.target, 15); // Max reasonable room expectation task.description = 'Wants at least ' + task.target + ' rooms'; } else if (task.type === 'rating') { task.target = Math.min(task.target, 4.5); // Max reasonable rating expectation task.description = 'Expects rating above ' + task.target.toFixed(1); } else if (task.type === 'budget') { task.description = 'Hotel should have budget above $' + task.target; } return task; } function checkTaskCompletion(task) { if (task.type === 'room_count') { return hotelCounts[currentHotelIndex] >= task.target; } else if (task.type === 'rating') { var ratingKey = 'hotel' + currentHotelIndex + 'Rating'; var currentRating = storage[ratingKey] || 0; return currentRating >= task.target; } else if (task.type === 'budget') { return budget >= task.target; } return false; } function showHighScorePopup() { // Create popup background var popupBg = game.addChild(LK.getAsset('hotel_bg', { anchorX: 0.5, anchorY: 0.5 })); popupBg.x = 1024; popupBg.y = 1366; popupBg.scaleX = 0.8; popupBg.scaleY = 0.6; popupBg.tint = 0x000000; popupBg.alpha = 0.9; // Create popup title var popupTitle = popupBg.addChild(new Text2('High Score', { size: 80, fill: 0xFFFFFF })); popupTitle.anchor.set(0.5, 0.5); popupTitle.y = -150; // Create high score text var highScoreText = popupBg.addChild(new Text2('$' + highestBudget, { size: 120, fill: 0xFFD700 })); highScoreText.anchor.set(0.5, 0.5); highScoreText.y = -50; // Create current budget text var currentBudgetText = popupBg.addChild(new Text2('Current: $' + budget, { size: 60, fill: 0xFFFFFF })); currentBudgetText.anchor.set(0.5, 0.5); currentBudgetText.y = 50; // Create close button var closeButton = popupBg.addChild(new Text2('Close', { size: 50, fill: 0xFFFFFF })); closeButton.anchor.set(0.5, 0.5); closeButton.y = 150; // Add close functionality closeButton.down = function (x, y, obj) { popupBg.destroy(); }; // Add background click to close popupBg.down = function (x, y, obj) { popupBg.destroy(); }; } // Create cost text below add room button var costText = new Text2('$175', { size: 60, fill: 0xFFFFFF }); costText.anchor.set(0.5, 0); game.addChild(costText); costText.x = 1024; costText.y = 2050; // 30 pixels below add room button at y: 2020 // Initialize UI updateUI(); // High score button removed - no longer visible in UI // Initialize score with secret hotel name and highest budget reached LK.setScore(secretHotelName + ': $' + highestBudget);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
hotel0Rating: 0,
hotel1Rating: 0,
hotel2Rating: 0,
hotel3Rating: 0,
hotel4Rating: 0,
hotel0RatingCount: 0,
hotel1RatingCount: 0,
hotel2RatingCount: 0,
hotel3RatingCount: 0,
hotel4RatingCount: 0,
highestBudget: 1000
});
/****
* Classes
****/
var AddRoomButton = Container.expand(function () {
var self = Container.call(this);
var buttonBg = self.attachAsset('add_room_button', {
anchorX: 0.5,
anchorY: 0.5
});
self.buttonText = self.addChild(new Text2('Add Room', {
size: 50,
fill: 0xFFFFFF
}));
self.buttonText.anchor.set(0.5, 0.5);
self.buttonText.x = 0;
self.buttonText.y = 0;
self.down = function (x, y, obj) {
if (totalRooms < 60 && hotelCounts[currentHotelIndex] < 12 && budget >= 175) {
if (hotels[currentHotelIndex].addRoom()) {
totalRooms++;
hotelCounts[currentHotelIndex]++;
budget -= 175; // Deduct $175 for each room
updateUI();
}
}
};
self.updateButton = function () {
// Check if there are damaged rooms in current hotel
var damagedRooms = [];
var currentHotelObj = hotels[currentHotelIndex];
if (currentHotelObj.roomDamaged) {
for (var i = 0; i < currentHotelObj.roomDamaged.length; i++) {
if (currentHotelObj.roomDamaged[i]) {
var repairCost = currentHotelObj.roomRepairCosts && currentHotelObj.roomRepairCosts[i] || 0;
damagedRooms.push('Room ' + (i + 1) + ': $' + repairCost);
}
}
}
if (damagedRooms.length > 0) {
buttonBg.tint = 0xff4444; // Red tint to indicate repairs needed
self.buttonText.setText('Repairs Needed');
} else if (totalRooms >= 60 || hotelCounts[currentHotelIndex] >= 12 || budget < 175) {
buttonBg.tint = 0x666666;
if (hotelCounts[currentHotelIndex] >= 12) {
self.buttonText.setText('Hotel Full');
} else if (budget < 175) {
self.buttonText.setText('Need $175');
} else {
self.buttonText.setText('Limit Reached');
}
} else {
buttonBg.tint = 0xffffff;
self.buttonText.setText('Add Room');
}
// Ensure text is vertically centered in button
self.buttonText.y = 0;
};
return self;
});
var Customer = Container.expand(function () {
var self = Container.call(this);
var customerGraphics = self.attachAsset('customer', {
anchorX: 0.5,
anchorY: 1.0
});
self.isAtHotel = true;
self.isMoving = false;
self.moveToGroceryStore = function () {
if (self.isMoving || !self.isAtHotel) return;
self.isMoving = true;
tween(self, {
y: 2400
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isAtHotel = false;
self.isMoving = false;
// Free the room when customer leaves
if (self.occupiedRoomIndex !== undefined) {
hotels[currentHotelIndex].setRoomOccupation(self.occupiedRoomIndex, false);
self.occupiedRoomIndex = undefined;
}
// Stay at grocery store for a moment, then return
LK.setTimeout(function () {
self.returnToHotel();
}, 1500);
}
});
};
self.returnToHotel = function () {
if (self.isMoving || self.isAtHotel) return;
self.isMoving = true;
// Calculate target position based on room location (customer already has a room)
var targetX = 1024; // Default hotel center
var targetY = 1366; // Default hotel center
if (self.occupiedRoomIndex !== undefined) {
// Re-occupy the same room when returning
hotels[currentHotelIndex].setRoomOccupation(self.occupiedRoomIndex, true);
// Calculate room position using same logic as in Hotel.updateDisplay
var positions = [];
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 3; col++) {
positions.push({
x: (col - 1) * 240,
y: (row - 1.5) * 240
});
}
}
if (self.occupiedRoomIndex < positions.length) {
targetX = 1024 + positions[self.occupiedRoomIndex].x; // Add hotel offset
targetY = 1366 + positions[self.occupiedRoomIndex].y; // Add hotel offset
}
}
tween(self, {
x: targetX,
y: targetY
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.isAtHotel = true;
self.isMoving = false;
// Complete scoring task and give rating
if (self.scoringTask && !self.hasCompletedTask) {
self.hasCompletedTask = true;
var taskSuccess = checkTaskCompletion(self.scoringTask);
var rating = taskSuccess ? Math.floor(Math.random() * 2) + 4 : Math.floor(Math.random() * 3) + 1; // 4-5 for success, 1-3 for failure
// Calculate base payment proportional to rating ($10-$100 based on 1-5 star rating)
var basePayment = Math.floor(rating / 5 * 90) + 10; // Maps 1-5 stars to $10-$100
// Add shop purchases to total payment
var totalPayment = basePayment + (self.totalSpent || 0);
budget += totalPayment;
// Update highest budget reached and save as score
if (budget > highestBudget) {
highestBudget = budget;
storage.highestBudget = highestBudget;
LK.setScore(secretHotelName + ': $' + highestBudget);
}
// Auto-rate the hotel based on task completion
var ratingKey = 'hotel' + currentHotelIndex + 'Rating';
var countKey = 'hotel' + currentHotelIndex + 'RatingCount';
var currentRating = storage[ratingKey] || 0;
var currentCount = storage[countKey] || 0;
var totalScore = currentRating * currentCount + rating;
var newCount = currentCount + 1;
var newRating = totalScore / newCount;
storage[ratingKey] = newRating;
storage[countKey] = newCount;
updateUI();
}
}
});
};
return self;
});
var GroceryStore = Container.expand(function () {
var self = Container.call(this);
var storeBg = self.attachAsset('grocery_store', {
anchorX: 0.5,
anchorY: 0.5
});
// Grocery store text hidden
return self;
});
var Hotel = Container.expand(function () {
var self = Container.call(this);
// Hotel background
var hotelBg = self.attachAsset('hotel_bg', {
anchorX: 0.5,
anchorY: 0.5
});
// Properties
self.hotelId = 1;
self.roomCount = 0;
self.rooms = [];
self.occupiedRooms = []; // Track which rooms are occupied
self.roomDamageCounters = []; // Track guest count for each room
self.roomDamaged = []; // Track which rooms are damaged
self.setHotel = function (id) {
self.hotelId = id;
self.updateDisplay();
};
self.addRoom = function () {
if (self.roomCount < 12) {
// Max 12 rooms per hotel (3x4 grid)
var room = self.addChild(LK.getAsset('room_empty', {
anchorX: 0.5,
anchorY: 0.5
}));
// Position rooms in 3x4 grid layout (3 columns, 4 rows)
var positions = [];
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 3; col++) {
positions.push({
x: (col - 1) * 240,
// Center the grid: -240, 0, 240
y: (row - 1.5) * 240 // Center vertically around hotel with larger gaps
});
}
}
var targetX = positions[self.roomCount].x;
var targetY = positions[self.roomCount].y;
// Check for overlapping with existing rooms
var isOverlapping = false;
for (var i = 0; i < self.rooms.length; i++) {
var existingRoom = self.rooms[i];
var distanceX = Math.abs(existingRoom.x - targetX);
var distanceY = Math.abs(existingRoom.y - targetY);
// Check if rooms would overlap (room width is 150, height is 112.5, grid spacing is 240x240)
if (distanceX < 240 && distanceY < 240) {
isOverlapping = true;
break;
}
}
// If overlapping, try to find an alternative position
if (isOverlapping) {
var foundPosition = false;
// Try different directional positions
for (var tryPos = 0; tryPos < positions.length && !foundPosition; tryPos++) {
if (tryPos === self.roomCount) continue; // Skip the current position
targetX = positions[tryPos].x;
targetY = positions[tryPos].y;
isOverlapping = false;
// Check this position against all existing rooms
for (var i = 0; i < self.rooms.length; i++) {
var existingRoom = self.rooms[i];
var distanceX = Math.abs(existingRoom.x - targetX);
var distanceY = Math.abs(existingRoom.y - targetY);
if (distanceX < 240 && distanceY < 240) {
isOverlapping = true;
break;
}
}
if (!isOverlapping) {
foundPosition = true;
}
}
// If no position found, don't add the room
if (!foundPosition) {
room.destroy();
return false;
}
}
room.x = targetX;
room.y = targetY;
// Room starts as empty (using room_empty asset)
self.rooms.push(room);
self.occupiedRooms.push(false); // Room starts as empty
self.roomDamageCounters.push(0); // Initialize damage counter
self.roomDamaged.push(false); // Initialize as not damaged
self.roomCount++;
return true;
}
return false;
};
self.updateDisplay = function () {
// Clear existing rooms
for (var i = 0; i < self.rooms.length; i++) {
self.rooms[i].destroy();
}
self.rooms = [];
// Keep damage tracking arrays intact during display updates
// Add rooms based on current count using 3x4 grid layout
var positions = [];
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 3; col++) {
positions.push({
x: (col - 1) * 240,
// Center the grid: -240, 0, 240
y: (row - 1.5) * 240 // Center vertically around hotel with larger gaps
});
}
}
for (var i = 0; i < self.roomCount; i++) {
// Use appropriate room asset based on damage and occupation status
var assetName;
if (self.roomDamaged[i]) {
assetName = 'room_broken';
} else {
assetName = self.occupiedRooms[i] ? 'room_full' : 'room_empty';
}
var room = self.addChild(LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
}));
room.x = positions[i].x;
room.y = positions[i].y;
// Add click interaction to handle room repairs or occupation
room.roomIndex = i;
room.down = function (x, y, obj) {
if (self.roomDamaged[this.roomIndex]) {
self.repairRoom(this.roomIndex);
} else {
self.toggleRoomOccupation(this.roomIndex);
}
};
self.rooms.push(room);
}
};
self.toggleRoomOccupation = function (roomIndex) {
if (roomIndex >= 0 && roomIndex < self.roomCount) {
self.occupiedRooms[roomIndex] = !self.occupiedRooms[roomIndex];
// Update room visual by refreshing display
self.updateDisplay();
}
};
self.setRoomOccupation = function (roomIndex, isOccupied) {
if (roomIndex >= 0 && roomIndex < self.roomCount) {
self.occupiedRooms[roomIndex] = isOccupied;
// If room is being occupied, increment damage counter
if (isOccupied) {
self.roomDamageCounters[roomIndex]++;
// Random chance for room to get damaged after guest stays
var damageThreshold = Math.floor(Math.random() * 8) + 3; // 3-10 guests before damage
if (self.roomDamageCounters[roomIndex] >= damageThreshold && !self.roomDamaged[roomIndex]) {
self.roomDamaged[roomIndex] = true;
// Generate repair cost between $20-$300
var repairCost = Math.floor(Math.random() * 281) + 20;
self.roomRepairCosts = self.roomRepairCosts || [];
self.roomRepairCosts[roomIndex] = repairCost;
}
}
// Update room visual by refreshing display
self.updateDisplay();
}
};
self.repairRoom = function (roomIndex) {
if (roomIndex >= 0 && roomIndex < self.roomCount && self.roomDamaged[roomIndex]) {
self.roomRepairCosts = self.roomRepairCosts || [];
var repairCost = self.roomRepairCosts[roomIndex] || 0;
if (budget >= repairCost) {
budget -= repairCost;
self.roomDamaged[roomIndex] = false;
self.roomDamageCounters[roomIndex] = 0; // Reset damage counter
delete self.roomRepairCosts[roomIndex];
updateUI();
self.updateDisplay();
}
}
};
return self;
});
var NavigationButton = Container.expand(function () {
var self = Container.call(this);
self.isLeft = true;
self.buttonBg = null;
self.buttonText = null;
self.setDirection = function (isLeft) {
self.isLeft = isLeft;
// Remove existing background if it exists
if (self.buttonBg) {
self.removeChild(self.buttonBg);
}
// Create new background with appropriate asset
var assetName = isLeft ? 'nav_button_left' : 'nav_button_right';
self.buttonBg = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
};
// Initialize with default direction
self.setDirection(true);
self.setText = function (text) {
if (self.buttonText) {
self.buttonText.destroy();
}
self.buttonText = self.addChild(new Text2(text, {
size: 40,
fill: 0xFFFFFF
}));
self.buttonText.anchor.set(0.5, 0.5);
};
self.down = function (x, y, obj) {
if (self.isLeft) {
currentHotelIndex = (currentHotelIndex - 1 + 5) % 5;
} else {
currentHotelIndex = (currentHotelIndex + 1) % 5;
}
updateHotelDisplay();
};
return self;
});
var RatingButton = Container.expand(function () {
var self = Container.call(this);
self.stars = [];
self.hotelIndex = 0;
self.currentRating = 0;
// Create 5 star buttons
for (var i = 0; i < 5; i++) {
var star = self.addChild(LK.getAsset('nav_button_left', {
anchorX: 0.5,
anchorY: 0.5
}));
star.scaleX = 0.6;
star.scaleY = 0.6;
star.x = (i - 2) * 80; // Space stars horizontally
star.tint = 0x666666; // Gray by default
star.starIndex = i + 1;
// Add star text
var starText = star.addChild(new Text2('★', {
size: 30,
fill: 0xFFFFFF
}));
starText.anchor.set(0.5, 0.5);
star.down = function (x, y, obj) {
self.setRating(this.starIndex);
};
self.stars.push(star);
}
self.setHotelIndex = function (index) {
self.hotelIndex = index;
self.updateDisplay();
};
self.setRating = function (rating) {
self.currentRating = rating;
// Update visual stars
for (var i = 0; i < 5; i++) {
if (i < rating) {
self.stars[i].tint = 0xFFD700; // Gold for selected stars
} else {
self.stars[i].tint = 0x666666; // Gray for unselected
}
}
// Save rating to storage
var ratingKey = 'hotel' + self.hotelIndex + 'Rating';
var countKey = 'hotel' + self.hotelIndex + 'RatingCount';
var currentRating = storage[ratingKey] || 0;
var currentCount = storage[countKey] || 0;
// Calculate new average rating
var totalScore = currentRating * currentCount + rating;
var newCount = currentCount + 1;
var newRating = totalScore / newCount;
storage[ratingKey] = newRating;
storage[countKey] = newCount;
// Update UI
updateUI();
};
self.updateDisplay = function () {
var ratingKey = 'hotel' + self.hotelIndex + 'Rating';
var rating = storage[ratingKey] || 0;
// Show current average rating
for (var i = 0; i < 5; i++) {
if (i < Math.round(rating)) {
self.stars[i].tint = 0xFFD700; // Gold for rated stars
} else {
self.stars[i].tint = 0x666666; // Gray for unrated
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Game state variables
var hotels = [];
var currentHotelIndex = 0;
var totalRooms = 0;
var hotelCounts = [0, 0, 0, 0, 0];
var budget = 1000;
var highestBudget = storage.highestBudget || 1000;
var hotelNames = ['Grand Palace', 'Ocean View', 'Mountain Lodge', 'City Center', 'Royal Garden'];
var secretHotelName = hotelNames[Math.floor(Math.random() * hotelNames.length)];
// UI elements
var hotelNameText;
var currentRoomsText;
var totalRoomsText;
var budgetText;
var ratingText;
var leftButton;
var rightButton;
var addRoomButton;
var ratingButton;
var currentHotel;
// Initialize hotels
for (var i = 0; i < 5; i++) {
hotels.push(new Hotel());
}
// Add 1 room to the first hotel at game start
if (hotels[0].addRoom()) {
totalRooms++;
hotelCounts[0]++;
}
// Initialize hotel counts to match hotel room counts
for (var i = 0; i < 5; i++) {
hotelCounts[i] = hotels[i].roomCount;
}
// Add neighborhood background behind hotel
var neighborhoodBg = game.addChild(LK.getAsset('neighborhood_bg', {
anchorX: 0.5,
anchorY: 0.5
}));
neighborhoodBg.x = 1024;
neighborhoodBg.y = 1000; // Position behind hotel area
// Add road below hotel - create multiple segments horizontally
var roads = [];
var roadCount = 6; // Number of road segments
var roadWidth = 400; // Width of each road segment
var startX = 1024 - roadCount * roadWidth / 2 + roadWidth / 2; // Center the road pattern
for (var r = 0; r < roadCount; r++) {
var road = game.addChild(LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0.5
}));
road.x = startX + r * roadWidth;
road.y = 2200; // Position below hotel area
roads.push(road);
}
// Position current hotel in center
currentHotel = game.addChild(hotels[0]);
currentHotel.x = 1024;
currentHotel.y = 1366;
// Create navigation buttons
leftButton = game.addChild(new NavigationButton());
leftButton.setDirection(true);
leftButton.setText('<');
leftButton.x = 400;
leftButton.y = 1366;
rightButton = game.addChild(new NavigationButton());
rightButton.setDirection(false);
rightButton.setText('>');
rightButton.x = 1648;
rightButton.y = 1366;
// Create add room button
addRoomButton = game.addChild(new AddRoomButton());
addRoomButton.x = 1024;
addRoomButton.y = 2020;
// Create rating button (hidden)
ratingButton = new RatingButton();
ratingButton.x = 1024;
ratingButton.y = 2100;
ratingButton.setHotelIndex(0);
// Create UI text elements
hotelNameText = new Text2('Hotel 1', {
size: 80,
fill: 0xFFFFFF
});
hotelNameText.anchor.set(0.5, 0);
LK.gui.top.addChild(hotelNameText);
hotelNameText.y = 150;
currentRoomsText = new Text2('Rooms in this hotel: 0', {
size: 50,
fill: 0xFFFFFF
});
currentRoomsText.anchor.set(0.5, 0);
LK.gui.top.addChild(currentRoomsText);
currentRoomsText.y = 250;
totalRoomsText = new Text2('Total rooms: 0/60', {
size: 50,
fill: 0xFFFFFF
});
totalRoomsText.anchor.set(0.5, 0);
LK.gui.top.addChild(totalRoomsText);
totalRoomsText.y = 320;
budgetText = new Text2('Budget: $1000', {
size: 50,
fill: 0xFFFFFF
});
budgetText.anchor.set(0, 0);
LK.gui.topLeft.addChild(budgetText);
budgetText.x = 120;
budgetText.y = 50;
ratingText = new Text2('Rating: 0.0/5 (0 reviews)', {
size: 50,
fill: 0xFFFFFF
});
ratingText.anchor.set(0.5, 0);
ratingText.y = 390;
function updateHotelDisplay() {
// Remove current hotel
game.removeChild(currentHotel);
// Add new hotel
currentHotel = game.addChild(hotels[currentHotelIndex]);
currentHotel.x = 1024;
currentHotel.y = 1366;
currentHotel.setHotel(currentHotelIndex + 1);
updateUI();
}
function updateUI() {
hotelNameText.setText('Hotel ' + (currentHotelIndex + 1));
currentRoomsText.setText('Rooms in this hotel: ' + hotelCounts[currentHotelIndex]);
totalRoomsText.setText('Total rooms: ' + totalRooms + '/60');
budgetText.setText('Budget: $' + budget);
addRoomButton.updateButton();
// Update rating display (hidden)
var ratingKey = 'hotel' + currentHotelIndex + 'Rating';
var countKey = 'hotel' + currentHotelIndex + 'RatingCount';
var rating = storage[ratingKey] || 0;
var count = storage[countKey] || 0;
}
// Update hotel counts when rooms are added
function updateHotelCounts() {
totalRooms = 0;
for (var i = 0; i < 5; i++) {
hotelCounts[i] = hotels[i].roomCount;
totalRooms += hotelCounts[i];
}
}
game.update = function () {
updateHotelCounts();
// Spawn customers at random intervals when game starts
customerSpawnTimer++;
var randomSpawnDelay = Math.floor(Math.random() * 120) + 60; // Random between 1-3 seconds (60-180 frames)
// Spawn customers regardless of room availability
if (customerSpawnTimer >= randomSpawnDelay && hotelCounts[currentHotelIndex] > 0 && customers.length < 8) {
customerSpawnTimer = 0;
spawnCustomer();
}
// Clean up customers that have been around too long
for (var i = customers.length - 1; i >= 0; i--) {
// Remove customers after they've made several trips (optional cleanup)
if (Math.random() < 0.001) {
// Very small chance to remove each frame
customers[i].destroy();
customers.splice(i, 1);
}
}
};
// Create grocery store at bottom of screen
var groceryStore = game.addChild(new GroceryStore());
groceryStore.x = 1024;
groceryStore.y = 2500;
// Shop items that customers can purchase
var shopItems = [{
name: 'Room Service',
price: 25
}, {
name: 'Spa Treatment',
price: 80
}, {
name: 'Restaurant Meal',
price: 45
}, {
name: 'Minibar Snacks',
price: 15
}, {
name: 'Laundry Service',
price: 20
}, {
name: 'WiFi Premium',
price: 10
}, {
name: 'Pool Access',
price: 30
}, {
name: 'Gym Access',
price: 25
}, {
name: 'Parking',
price: 35
}, {
name: 'Late Checkout',
price: 40
}, {
name: 'Room Upgrade',
price: 75
}, {
name: 'Airport Shuttle',
price: 50
}, {
name: 'Tour Guide',
price: 90
}, {
name: 'Concierge Service',
price: 60
}, {
name: 'Baby Crib',
price: 20
}];
// Customer management
var customers = [];
var customerSpawnTimer = Math.floor(Math.random() * 150); // Start with random delay
function spawnCustomer() {
var customer = game.addChild(new Customer());
// Spawn from left or right side of the road
var fromLeft = Math.random() < 0.5;
if (fromLeft) {
customer.x = -100; // Start off-screen on the left
} else {
customer.x = 2148; // Start off-screen on the right (2048 + 100)
}
customer.y = 2200; // Start at road level
customers.push(customer);
// Find an available room position for the customer (excluding damaged rooms)
var availableRooms = [];
for (var roomIdx = 0; roomIdx < hotels[currentHotelIndex].roomCount; roomIdx++) {
if (!hotels[currentHotelIndex].occupiedRooms[roomIdx] && !hotels[currentHotelIndex].roomDamaged[roomIdx]) {
availableRooms.push(roomIdx);
}
}
var roomIndex = -1; // Default: no room assigned
var hasRoom = availableRooms.length > 0;
if (hasRoom) {
// Assign the first available room
roomIndex = availableRooms[0];
customer.occupiedRoomIndex = roomIndex;
}
// Calculate target position - always go to hotel center first
var targetX = 1024; // Default hotel center
var targetY = 1366; // Default hotel center
// If customer has a room, calculate specific room position
if (hasRoom && roomIndex >= 0) {
// Calculate room position using same logic as in Hotel.updateDisplay
var positions = [];
for (var row = 0; row < 4; row++) {
for (var col = 0; col < 3; col++) {
positions.push({
x: (col - 1) * 240,
y: (row - 1.5) * 240
});
}
}
if (roomIndex < positions.length) {
targetX = 1024 + positions[roomIndex].x; // Add hotel offset
targetY = 1366 + positions[roomIndex].y; // Add hotel offset
}
}
// First move customer to center of road
tween(customer, {
x: 1024,
// Move to center of screen horizontally
y: 2200 // Stay at road level
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Then move customer to the specific room location
tween(customer, {
x: targetX,
y: targetY
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Customer has arrived at hotel - now check if they have a room
if (!hasRoom) {
// No room available - give $30 compensation and make customer leave
budget -= 30;
updateUI();
// Make customer leave immediately via the road
var exitX = customer.x < 1024 ? -100 : 2148; // Exit same side they came from
tween(customer, {
x: exitX,
y: 2200 // Move back to road level
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Remove customer
for (var i = customers.length - 1; i >= 0; i--) {
if (customers[i] === customer) {
customers.splice(i, 1);
break;
}
}
customer.destroy();
}
});
return;
}
// Customer has a room - occupy it
hotels[currentHotelIndex].setRoomOccupation(customer.occupiedRoomIndex, true);
// Give customer a scoring task when they arrive at hotel
customer.scoringTask = generateScoringTask();
customer.hasCompletedTask = false;
// Generate shop purchases for this customer
customer.shopPurchases = generateShopPurchases();
customer.totalSpent = 0;
for (var p = 0; p < customer.shopPurchases.length; p++) {
customer.totalSpent += customer.shopPurchases[p].price;
}
// Customer goes to grocery store after arriving at hotel and a random delay
LK.setTimeout(function () {
customer.moveToGroceryStore();
}, Math.random() * 3000 + 1000);
}
});
}
});
}
function generateShopPurchases() {
var purchases = [];
var numItems = Math.floor(Math.random() * 10) + 1; // 1-10 items
var usedItems = [];
for (var i = 0; i < numItems; i++) {
var availableItems = [];
for (var j = 0; j < shopItems.length; j++) {
if (usedItems.indexOf(j) === -1) {
availableItems.push(j);
}
}
if (availableItems.length > 0) {
var randomIndex = availableItems[Math.floor(Math.random() * availableItems.length)];
purchases.push(shopItems[randomIndex]);
usedItems.push(randomIndex);
}
}
return purchases;
}
function generateScoringTask() {
var tasks = [{
type: 'room_count',
target: Math.floor(Math.random() * 10) + 5,
description: 'Wants at least X rooms'
}, {
type: 'rating',
target: Math.random() * 3 + 2,
description: 'Expects rating above X'
}, {
type: 'budget',
target: Math.floor(Math.random() * 500) + 200,
description: 'Hotel should have budget above $X'
}];
var task = tasks[Math.floor(Math.random() * tasks.length)];
// Customize target based on task type
if (task.type === 'room_count') {
task.target = Math.min(task.target, 15); // Max reasonable room expectation
task.description = 'Wants at least ' + task.target + ' rooms';
} else if (task.type === 'rating') {
task.target = Math.min(task.target, 4.5); // Max reasonable rating expectation
task.description = 'Expects rating above ' + task.target.toFixed(1);
} else if (task.type === 'budget') {
task.description = 'Hotel should have budget above $' + task.target;
}
return task;
}
function checkTaskCompletion(task) {
if (task.type === 'room_count') {
return hotelCounts[currentHotelIndex] >= task.target;
} else if (task.type === 'rating') {
var ratingKey = 'hotel' + currentHotelIndex + 'Rating';
var currentRating = storage[ratingKey] || 0;
return currentRating >= task.target;
} else if (task.type === 'budget') {
return budget >= task.target;
}
return false;
}
function showHighScorePopup() {
// Create popup background
var popupBg = game.addChild(LK.getAsset('hotel_bg', {
anchorX: 0.5,
anchorY: 0.5
}));
popupBg.x = 1024;
popupBg.y = 1366;
popupBg.scaleX = 0.8;
popupBg.scaleY = 0.6;
popupBg.tint = 0x000000;
popupBg.alpha = 0.9;
// Create popup title
var popupTitle = popupBg.addChild(new Text2('High Score', {
size: 80,
fill: 0xFFFFFF
}));
popupTitle.anchor.set(0.5, 0.5);
popupTitle.y = -150;
// Create high score text
var highScoreText = popupBg.addChild(new Text2('$' + highestBudget, {
size: 120,
fill: 0xFFD700
}));
highScoreText.anchor.set(0.5, 0.5);
highScoreText.y = -50;
// Create current budget text
var currentBudgetText = popupBg.addChild(new Text2('Current: $' + budget, {
size: 60,
fill: 0xFFFFFF
}));
currentBudgetText.anchor.set(0.5, 0.5);
currentBudgetText.y = 50;
// Create close button
var closeButton = popupBg.addChild(new Text2('Close', {
size: 50,
fill: 0xFFFFFF
}));
closeButton.anchor.set(0.5, 0.5);
closeButton.y = 150;
// Add close functionality
closeButton.down = function (x, y, obj) {
popupBg.destroy();
};
// Add background click to close
popupBg.down = function (x, y, obj) {
popupBg.destroy();
};
}
// Create cost text below add room button
var costText = new Text2('$175', {
size: 60,
fill: 0xFFFFFF
});
costText.anchor.set(0.5, 0);
game.addChild(costText);
costText.x = 1024;
costText.y = 2050; // 30 pixels below add room button at y: 2020
// Initialize UI
updateUI();
// High score button removed - no longer visible in UI
// Initialize score with secret hotel name and highest budget reached
LK.setScore(secretHotelName + ': $' + highestBudget);
stickman top view. In-Game asset
empty room top view. In-Game asset. 2d. High contrast. No shadows
full room top view. In-Game asset. 2d. High contrast. No shadows
left arrow. In-Game asset
destroyed and damaged red flag cross the stripe
Hotel wall view from the front with flowers and mosses. In-Game asset
16:9
Building silhouettes. In-Game asset
hotel bar top view. In-Game asset