User prompt
Here's how to convey that to your game AI: "For each arriving customer: Check Room Availability (One Time): The AI should perform a single check for a vacant room upon the customer's arrival. No Vacancy: If no rooms are available, the customer immediately receives $30 in compensation from the hotel's budget. This compensation is a one-time payment for that specific customer. Vacancy: If a room is available, the customer will use (occupy) that room. Customer Departure: Regardless of whether they received compensation or used a room, the customer then immediately leaves the hotel via the road. They will not repeat the room check or compensation process."
User prompt
A customer who cannot find a room should leave after taking $10.
User prompt
Disconnect the arrival of customers from the room status. In any case, customers come to the hotel.
User prompt
The game starts with 1 room open at the beginning.
User prompt
Customers started coming without adding rooms
User prompt
When the game starts, customers start arriving at random times
User prompt
If a customer arrives and finds no vacant rooms, they will receive $30 in compensation from the hotel budget.
User prompt
Lose $10 to angry customers when there are no rooms available
User prompt
Earn $10 cash back from angry customers when there are no rooms available
User prompt
No money should come when there is no customer. This is a mistake. No money should come when the plane is not caught. This is a mistake.
User prompt
Money goes up when there are no customers
User prompt
The duration of the plane is 5 seconds ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Don't give a reward without catching the plane
User prompt
add plane make the plane appear above everything else on the screen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
No plane visible
User prompt
Reduce the duration of the plane to 20 seconds. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Let a plane pass over the top of the screen and you will receive a cash prize of 100 to 1000 every 3 minutes. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
hide grocery store text
User prompt
Hide high score
User prompt
Make the room price 175
User prompt
Customers coming from the road should come from the right or left.
User prompt
repeat the path horizontally
User prompt
add neighborhood view behind hotel add road below hotel
User prompt
Open the gaps between hotel rooms vertically
User prompt
- Rooms are arranged in a **3x4 grid** (3 columns, 6 rows)
/**** * 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 >= 10) { if (hotels[currentHotelIndex].addRoom()) { totalRooms++; hotelCounts[currentHotelIndex]++; budget -= 10; // Deduct $10 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 < 10) { buttonBg.tint = 0x666666; if (hotelCounts[currentHotelIndex] >= 12) { self.buttonText.setText('Hotel Full'); } else if (budget < 10) { self.buttonText.setText('Need $10'); } 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) * 180 }); } } 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 }); self.storeText = self.addChild(new Text2('Grocery Store', { size: 40, fill: 0xFFFFFF })); self.storeText.anchor.set(0.5, 0.5); 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) * 180 // Center vertically around hotel: -270, -90, 90, 270 }); } } 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 240x180) if (distanceX < 240 && distanceY < 180) { 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 < 180) { 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) * 180 // Center vertically around hotel: -270, -90, 90, 270 }); } } 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()); } // Initialize hotel counts to match hotel room counts for (var i = 0; i < 5; i++) { hotelCounts[i] = hotels[i].roomCount; } // 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 consistent intervals if there are rooms in the current hotel customerSpawnTimer++; if (customerSpawnTimer >= 150 && hotelCounts[currentHotelIndex] > 0 && customers.length < 8) { // Every 2.5 seconds consistently, max 8 customers 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 = 0; function spawnCustomer() { var customer = game.addChild(new Customer()); customer.x = 900 + Math.random() * 248; // Random position near hotel customer.y = -100; // Start off-screen at the top 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); } } // If no rooms available, show angry bubble and make customer leave with low rating if (availableRooms.length === 0) { // Create angry word bubble var angryBubble = customer.addChild(new Text2('😠', { size: 60, fill: 0xFF0000 })); angryBubble.anchor.set(0.5, 1); angryBubble.y = -50; // Make customer leave immediately with angry animation tween(customer, { y: -200 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Give low rating before leaving var ratingKey = 'hotel' + currentHotelIndex + 'Rating'; var countKey = 'hotel' + currentHotelIndex + 'RatingCount'; var currentRating = storage[ratingKey] || 0; var currentCount = storage[countKey] || 0; var lowRating = Math.floor(Math.random() * 2) + 1; // 1-2 stars // Calculate base payment proportional to low rating ($10-$100 based on 1-5 star rating) var basePayment = Math.floor(lowRating / 5 * 90) + 10; // Maps 1-2 stars to $10-$28 // Even angry customers might have made some purchases before leaving var angryPurchases = generateShopPurchases(); var angrySpent = 0; // Angry customers only buy 1-3 items maximum var limitedPurchases = angryPurchases.slice(0, Math.floor(Math.random() * 3) + 1); for (var ap = 0; ap < limitedPurchases.length; ap++) { angrySpent += limitedPurchases[ap].price; } var totalPayment = basePayment + angrySpent; budget += totalPayment; // Update highest budget reached and save as score if (budget > highestBudget) { highestBudget = budget; storage.highestBudget = highestBudget; LK.setScore(secretHotelName + ': $' + highestBudget); } var totalScore = currentRating * currentCount + lowRating; var newCount = currentCount + 1; var newRating = totalScore / newCount; storage[ratingKey] = newRating; storage[countKey] = newCount; updateUI(); // Remove customer for (var i = customers.length - 1; i >= 0; i--) { if (customers[i] === customer) { customers.splice(i, 1); break; } } customer.destroy(); } }); return; } // Calculate target position based on room layout var targetX = 1024; // Default hotel center var targetY = 1366; // Default hotel center // Use the first available room's position var roomIndex = availableRooms[0]; // Reserve the room index but don't occupy it yet customer.occupiedRoomIndex = roomIndex; // 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) * 180 }); } } if (roomIndex < positions.length) { targetX = 1024 + positions[roomIndex].x; // Add hotel offset targetY = 1366 + positions[roomIndex].y; // Add hotel offset } // Move customer to the specific room location tween(customer, { x: targetX, y: targetY }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { // Occupy the room when customer actually arrives 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('$10', { 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(); // Create high score button var highScoreButton = new Text2('High Score', { size: 40, fill: 0xFFFFFF }); highScoreButton.anchor.set(1, 0); LK.gui.topRight.addChild(highScoreButton); highScoreButton.x = -20; highScoreButton.y = 50; // Add click handler for high score button highScoreButton.down = function (x, y, obj) { showHighScorePopup(); }; // 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 >= 10) {
if (hotels[currentHotelIndex].addRoom()) {
totalRooms++;
hotelCounts[currentHotelIndex]++;
budget -= 10; // Deduct $10 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 < 10) {
buttonBg.tint = 0x666666;
if (hotelCounts[currentHotelIndex] >= 12) {
self.buttonText.setText('Hotel Full');
} else if (budget < 10) {
self.buttonText.setText('Need $10');
} 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) * 180
});
}
}
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
});
self.storeText = self.addChild(new Text2('Grocery Store', {
size: 40,
fill: 0xFFFFFF
}));
self.storeText.anchor.set(0.5, 0.5);
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) * 180 // Center vertically around hotel: -270, -90, 90, 270
});
}
}
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 240x180)
if (distanceX < 240 && distanceY < 180) {
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 < 180) {
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) * 180 // Center vertically around hotel: -270, -90, 90, 270
});
}
}
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());
}
// Initialize hotel counts to match hotel room counts
for (var i = 0; i < 5; i++) {
hotelCounts[i] = hotels[i].roomCount;
}
// 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 consistent intervals if there are rooms in the current hotel
customerSpawnTimer++;
if (customerSpawnTimer >= 150 && hotelCounts[currentHotelIndex] > 0 && customers.length < 8) {
// Every 2.5 seconds consistently, max 8 customers
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 = 0;
function spawnCustomer() {
var customer = game.addChild(new Customer());
customer.x = 900 + Math.random() * 248; // Random position near hotel
customer.y = -100; // Start off-screen at the top
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);
}
}
// If no rooms available, show angry bubble and make customer leave with low rating
if (availableRooms.length === 0) {
// Create angry word bubble
var angryBubble = customer.addChild(new Text2('😠', {
size: 60,
fill: 0xFF0000
}));
angryBubble.anchor.set(0.5, 1);
angryBubble.y = -50;
// Make customer leave immediately with angry animation
tween(customer, {
y: -200
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Give low rating before leaving
var ratingKey = 'hotel' + currentHotelIndex + 'Rating';
var countKey = 'hotel' + currentHotelIndex + 'RatingCount';
var currentRating = storage[ratingKey] || 0;
var currentCount = storage[countKey] || 0;
var lowRating = Math.floor(Math.random() * 2) + 1; // 1-2 stars
// Calculate base payment proportional to low rating ($10-$100 based on 1-5 star rating)
var basePayment = Math.floor(lowRating / 5 * 90) + 10; // Maps 1-2 stars to $10-$28
// Even angry customers might have made some purchases before leaving
var angryPurchases = generateShopPurchases();
var angrySpent = 0;
// Angry customers only buy 1-3 items maximum
var limitedPurchases = angryPurchases.slice(0, Math.floor(Math.random() * 3) + 1);
for (var ap = 0; ap < limitedPurchases.length; ap++) {
angrySpent += limitedPurchases[ap].price;
}
var totalPayment = basePayment + angrySpent;
budget += totalPayment;
// Update highest budget reached and save as score
if (budget > highestBudget) {
highestBudget = budget;
storage.highestBudget = highestBudget;
LK.setScore(secretHotelName + ': $' + highestBudget);
}
var totalScore = currentRating * currentCount + lowRating;
var newCount = currentCount + 1;
var newRating = totalScore / newCount;
storage[ratingKey] = newRating;
storage[countKey] = newCount;
updateUI();
// Remove customer
for (var i = customers.length - 1; i >= 0; i--) {
if (customers[i] === customer) {
customers.splice(i, 1);
break;
}
}
customer.destroy();
}
});
return;
}
// Calculate target position based on room layout
var targetX = 1024; // Default hotel center
var targetY = 1366; // Default hotel center
// Use the first available room's position
var roomIndex = availableRooms[0];
// Reserve the room index but don't occupy it yet
customer.occupiedRoomIndex = roomIndex;
// 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) * 180
});
}
}
if (roomIndex < positions.length) {
targetX = 1024 + positions[roomIndex].x; // Add hotel offset
targetY = 1366 + positions[roomIndex].y; // Add hotel offset
}
// Move customer to the specific room location
tween(customer, {
x: targetX,
y: targetY
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Occupy the room when customer actually arrives
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('$10', {
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();
// Create high score button
var highScoreButton = new Text2('High Score', {
size: 40,
fill: 0xFFFFFF
});
highScoreButton.anchor.set(1, 0);
LK.gui.topRight.addChild(highScoreButton);
highScoreButton.x = -20;
highScoreButton.y = 50;
// Add click handler for high score button
highScoreButton.down = function (x, y, obj) {
showHighScorePopup();
};
// 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