User prompt
put more spaces to station spawns and put take travellers button when we next to the station
User prompt
make street smaller. fix stations spawn on street. Put up and down overside of the street and same line all stations
User prompt
make background and station not same assets
User prompt
fix street is not unlimited
User prompt
make always bus on middle of the street. The screen follow the bus
User prompt
fix bus can go to overscreen
User prompt
when we hold on the screen bus go
User prompt
when we tap right of the screen bus move. not automatic
User prompt
draw a street background
Code edit (1 edits merged)
Please save this source code
User prompt
Rightward Road: Bus Stop Shuffle
Initial prompt
draw a street and we only can go right, put bus station random places and random 1-10 amount random traveller and make travellers leave random bus stations.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bus (player) class var Bus = Container.expand(function () { var self = Container.call(this); var busAsset = self.attachAsset('bus', { anchorX: 0.5, anchorY: 1 }); // For touch drag self.dragging = false; self.dragOffsetY = 0; // Called every tick self.update = function () { // Clamp bus to road (vertical bounds) if (self.y < 400) self.y = 400; if (self.y > 2200) self.y = 2200; }; // Touch down on bus self.down = function (x, y, obj) { self.dragging = true; self.dragOffsetY = y - self.y; }; // Touch up on bus self.up = function (x, y, obj) { self.dragging = false; }; return self; }); // Bus Station class var BusStation = Container.expand(function () { var self = Container.call(this); // Use dropStation asset if isPickup is false, otherwise use station var assetId = typeof self.isPickup !== "undefined" && self.isPickup === false ? 'dropStation' : 'station'; var stationAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 1 }); // List of travelers at this station self.travelers = []; // Travelers are now spawned in spawnStation, not here // Remove traveler from this station self.removeTraveler = function (traveler) { for (var i = 0; i < self.travelers.length; i++) { if (self.travelers[i] === traveler) { self.travelers.splice(i, 1); break; } } }; // Called every tick self.update = function () { // No longer remove station when bus passes; stations persist }; return self; }); // Traveler class var Traveler = Container.expand(function () { var self = Container.call(this); // Attach correct asset based on gender (default to man if not set yet) var assetId = typeof self.gender !== "undefined" && self.gender === "woman" ? "travelerWoman" : "traveler"; var travelerAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 1 }); // Reference to the station this traveler belongs to self.station = null; // Whether this traveler is still waiting at the station self.waiting = true; // Track if this traveler was dropped (for possible future logic) self.dropped = false; // Time (in ticks) until this traveler leaves self.leaveTick = 0; // Called every tick self.update = function () { // If not waiting, do nothing if (!self.waiting) return; // Travelers no longer leave by themselves // Remove the leave-tick logic entirely }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x222222 }); /**** * Game Code ****/ // green // orange // visually distinguish drop station (flipX as example) // Play baba music at game start LK.playMusic('baba', { loop: true, fade: { start: 0, end: 0.01, duration: 1000 } }); var streetBgWidth = 900; var streetBgHeight = 400; var streetBgY = 1500; var streetBgs = []; for (var i = 0; i < 3; i++) { var streetBg = LK.getAsset('streetBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + (i - 1) * streetBgWidth, y: streetBgY }); game.addChild(streetBg); streetBgs.push(streetBg); } // Traveler // Bus station // Bus (player vehicle) // Game world scroll variables var scrollX = 0; // How far the world has scrolled right var scrollSpeed = 10; // Pixels per tick // Bus (player) var bus = new Bus(); game.addChild(bus); bus.x = 400; bus.y = 1500; // List of stations in the world var stations = []; // Spawn fewer stations, with much greater spacing, and ensure max 2 stations visible on screen var lastStationX = 1200; var NUM_STATIONS = 6; // fewer stations for less crowding var minStationSpacing = 1200; // minimum distance between stations (at least 1 screen width) var maxStationSpacing = 1600; // maximum distance between stations var stationWidth = 400; for (var i = 0; i < NUM_STATIONS; i++) { var isPickup = i % 2 === 0; var tryCount = 0; var valid = false; var tryX = 0; while (!valid && tryCount < 100) { var spacing = minStationSpacing + Math.floor(Math.random() * (maxStationSpacing - minStationSpacing + 1)); if (i === 0) { tryX = 1200; } else { tryX = lastStationX + spacing; } // Check for overlap with all previous stations (ensure at least minStationSpacing between centers) valid = true; for (var j = 0; j < stations.length; j++) { var prev = stations[j]; if (Math.abs(tryX - prev.x) < minStationSpacing) { valid = false; break; } } tryCount++; } lastStationX = tryX; spawnStation(lastStationX, isPickup); } // GUI: Score (number of travelers picked up) var score = 0; var scoreTxt = new Text2('0', { size: 120, fill: 0xFFFFFF, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- Meter Bar UI --- var meterBarBg = LK.getAsset('meterBarBg', { anchorX: 0, anchorY: 0.5, x: 200, y: 120 }); var meterBarFill = LK.getAsset('meterBarBg', { anchorX: 0, anchorY: 0.5, x: 200, y: 120, width: 600, height: 40 }); meterBarFill.width = 0; LK.gui.top.addChild(meterBarBg); LK.gui.top.addChild(meterBarFill); // Add a text label to the meter bar showing the remaining distance in meters var meterBarText = new Text2('0m', { size: 60, fill: 0xffffff, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); meterBarText.anchor.set(0.5, 0.5); meterBarText.x = 200 + 600 / 2; meterBarText.y = 120; LK.gui.top.addChild(meterBarText); // --- Bus Capacity Bar UI --- var busCapacityBarBg = LK.getAsset('capacityBarBg', { anchorX: 0, anchorY: 0.5, x: 200, y: 200 }); // Plus capacity bar fill (dark green, for new travelers picked up this round) var plusCapacityBarFill = LK.getAsset('plusCapacityBarBg', { anchorX: 0, anchorY: 0.5, x: 200, y: 200, width: 600, height: 32 }); plusCapacityBarFill.width = 0; var busCapacityBarFill = LK.getAsset('plusCapacityBarBg', { anchorX: 0, anchorY: 0.5, x: 200, y: 200, width: 600, height: 32 }); busCapacityBarFill.width = 0; LK.gui.top.addChild(busCapacityBarBg); LK.gui.top.addChild(plusCapacityBarFill); LK.gui.top.addChild(busCapacityBarFill); // Add a bus capacity text label to the bus capacity bar in the main UI var busCapacityBarText = new Text2('0/10', { size: 48, fill: 0xffffff, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); busCapacityBarText.anchor.set(0.5, 0.5); busCapacityBarText.x = 200 + 600 / 2; busCapacityBarText.y = 200; LK.gui.top.addChild(busCapacityBarText); // For dragging bus var dragBus = false; // --- Take Travelers Button --- var takeBtnBg = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 320, y: 2732 - 200 }); var takeBtnTxt = new Text2('Take Travelers', { size: 60, fill: 0xffffff }); takeBtnTxt.anchor.set(0.5, 0.5); takeBtnBg.addChild(takeBtnTxt); game.addChild(takeBtnBg); // --- Drop Travelers Button --- var dropBtnBg = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 320, y: 2732 - 200 }); var dropBtnTxt = new Text2('Drop Travelers', { size: 60, fill: 0xffffff }); dropBtnTxt.anchor.set(0.5, 0.5); dropBtnBg.addChild(dropBtnTxt); game.addChild(dropBtnBg); // --- Shop Button --- var shopBtnBg = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 320, // Move to left side of the screen, away from top left 100x100 area y: 2732 - 200 }); var shopBtnTxt = new Text2('Shop', { size: 60, fill: 0xffffff }); shopBtnTxt.anchor.set(0.5, 0.5); shopBtnBg.addChild(shopBtnTxt); game.addChild(shopBtnBg); // --- Settings Button --- var settingsBtnBg = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 320, y: 2732 - 200 }); var settingsBtnTxt = new Text2('Settings', { size: 60, fill: 0xffffff }); settingsBtnTxt.anchor.set(0.5, 0.5); settingsBtnBg.addChild(settingsBtnTxt); game.addChild(settingsBtnBg); // --- Settings Panel --- var settingsPanelBg = LK.getAsset('whitePanel', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); settingsPanelBg.visible = false; game.addChild(settingsPanelBg); // Settings title var settingsTitle = new Text2('Settings', { size: 90, fill: 0x222222 }); settingsTitle.anchor.set(0.5, 0); settingsTitle.x = 0; settingsTitle.y = -350; settingsPanelBg.addChild(settingsTitle); // Music toggle button var musicToggleBtn = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -100, width: 400, height: 120 }); var musicToggleTxt = new Text2('Music: On', { size: 56, fill: 0xffffff }); musicToggleTxt.anchor.set(0.5, 0.5); musicToggleBtn.addChild(musicToggleTxt); settingsPanelBg.addChild(musicToggleBtn); // Sound toggle button var soundToggleBtn = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 80, width: 400, height: 120 }); var soundToggleTxt = new Text2('Sound: On', { size: 56, fill: 0xffffff }); soundToggleTxt.anchor.set(0.5, 0.5); soundToggleBtn.addChild(soundToggleTxt); settingsPanelBg.addChild(soundToggleBtn); // Set initial text for music and sound toggles musicToggleTxt.setText('Music: On'); soundToggleTxt.setText('Sound: On'); // Settings close button var settingsCloseBtn = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 260, width: 300, height: 90 }); var settingsCloseTxt = new Text2('Close', { size: 48, fill: 0x222222 }); settingsCloseTxt.anchor.set(0.5, 0.5); settingsCloseBtn.addChild(settingsCloseTxt); settingsPanelBg.addChild(settingsCloseBtn); // Track settings state var settingsBtnPressed = false; var settingsClosePressed = false; var musicTogglePressed = false; var soundTogglePressed = false; var musicEnabled = true; var soundEnabled = true; // Shop UI elements (hidden by default) var shopPanelBg = LK.getAsset('whitePanel', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); shopPanelBg.visible = false; game.addChild(shopPanelBg); // Shop title, centered near the top of the panel var shopTitle = new Text2('Shop', { size: 90, fill: 0x222222 }); shopTitle.anchor.set(0.5, 0); shopTitle.x = 0; // Move the shop title higher, since capacity text is removed shopTitle.y = -380; shopPanelBg.addChild(shopTitle); // Shop upgrade buttons and texts var shopUpgradeBtns = []; var shopUpgradeTxts = []; var shopUpgradeCosts = [30, 30, 30]; // initial costs var shopUpgradeLevels = [0, 0, 0]; // [capacity, speed, money] var shopUpgradeMax = [10, 10, 10]; var shopUpgradeNames = ['Capacity', 'Speed', 'Money']; var shopUpgradeDesc = ['Increase bus capacity by 2', 'Increase speed by 2', 'Increase money per traveler by 1']; for (var i = 0; i < 3; i++) { // Create a container for each row var row = new Container(); // Vertically space rows within the white panel, below the title, with even spacing var rowHeight = 180; // Move the upgrade rows higher as well var panelContentTop = shopTitle.y + shopTitle.height + 16; row.y = panelContentTop + i * rowHeight; // Center row horizontally in the panel row.x = 0; // --- Lay out texts from left to right, all inside the white panel --- // Name var nameTxt = new Text2(shopUpgradeNames[i], { size: 56, fill: 0x222222 }); nameTxt.anchor.set(0, 0.5); nameTxt.x = -900 / 2 + 40; nameTxt.y = 0; // Level var levelTxt = new Text2('(Lv.0)', { size: 56, fill: 0x222222 }); levelTxt.anchor.set(0, 0.5); levelTxt.x = nameTxt.x + nameTxt.width + 24; levelTxt.y = 0; // Cost (now below desc, but desc removed) var costTxt = new Text2('Cost: 30', { size: 44, fill: 0x888888 }); costTxt.anchor.set(0, 0.5); // Place cost where desc would have started costTxt.x = levelTxt.x + levelTxt.width + 24; costTxt.y = 0; // Upgrade button (right side, inside panel, with margin) var btn = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 900 / 2 - 120, y: 0, width: 180, height: 70 }); btn.visible = true; // Button label var btnLabel = new Text2('Upgrade', { size: 32, fill: 0xffffff }); btnLabel.anchor.set(0.5, 0.5); btn.addChild(btnLabel); // Add all texts and button to row row.addChild(nameTxt); row.addChild(levelTxt); row.addChild(costTxt); row.addChild(btn); // Add row to shop panel shopPanelBg.addChild(row); // Track for logic shopUpgradeBtns.push(btn); // Store all text objects for update logic (desc removed) shopUpgradeTxts.push({ name: nameTxt, level: levelTxt, cost: costTxt }); } shopPanelBg.visible = false; // Shop close button var shopCloseBtn = LK.getAsset('takeBtnBg', { anchorX: 0.5, anchorY: 0.5, x: 900 / 2, y: 820 }); var shopCloseTxt = new Text2('Close', { size: 48, fill: 0x222222 }); shopCloseTxt.anchor.set(0.5, 0.5); shopCloseBtn.addChild(shopCloseTxt); shopPanelBg.addChild(shopCloseBtn); // Track if take or drop or shop button is pressed this frame var takeBtnPressed = false; var dropBtnPressed = false; var shopBtnPressed = false; var shopUpgradePressed = [false, false, false]; var shopClosePressed = false; // Button press handlers takeBtnBg.down = function (x, y, obj) { if (takeBtnBg.interactive !== false && !shopPanelBg.visible) { takeBtnPressed = true; } }; dropBtnBg.down = function (x, y, obj) { if (dropBtnBg.interactive !== false && !shopPanelBg.visible) { dropBtnPressed = true; } }; // Shop button handler shopBtnBg.down = function (x, y, obj) { if (!shopPanelBg.visible && !settingsPanelBg.visible) { shopBtnPressed = true; } }; // Settings button handler settingsBtnBg.down = function (x, y, obj) { if (!settingsPanelBg.visible && !shopPanelBg.visible) { settingsBtnPressed = true; } }; // Settings close button handler settingsCloseBtn.down = function (x, y, obj) { if (settingsPanelBg.visible) { settingsClosePressed = true; } }; // Music toggle button handler musicToggleBtn.down = function (x, y, obj) { if (settingsPanelBg.visible) { musicTogglePressed = true; } }; // Sound toggle button handler soundToggleBtn.down = function (x, y, obj) { if (settingsPanelBg.visible) { soundTogglePressed = true; } }; // Shop upgrade button handlers for (var i = 0; i < 3; i++) { (function (idx) { shopUpgradeBtns[idx].down = function (x, y, obj) { if (shopPanelBg.visible) { shopUpgradePressed[idx] = true; } }; })(i); } // Shop close button handler shopCloseBtn.down = function (x, y, obj) { if (shopPanelBg.visible) { shopClosePressed = true; } }; // Generate a station at a given x, alternating pickup/drop and up/down function spawnStation(x, isPickup) { var station = new BusStation(); station.isPickup = !!isPickup; // Re-attach correct asset if drop station (for existing BusStation instances) if (!station.isPickup) { if (station.children.length > 0) { station.removeChild(station.children[0]); } station.attachAsset('dropStation', { anchorX: 0.5, anchorY: 1 }); } // Alternate up/down for each station, placing them exactly outside the street (not inside) var streetCenterY = streetBgY; var stationAssetHeight = 160; // matches asset height for station/dropStation if (stations.length % 2 === 0) { // Place station so its bottom edge is flush with the top edge of the street (just above street) station.y = streetCenterY - streetBgHeight / 2; } else { // Place station so its top edge is flush with the bottom edge of the street (just below street) station.y = streetCenterY + streetBgHeight / 2 + stationAssetHeight; } // Place at given x station.x = x; // Spawn travelers immediately if pickup station if (station.isPickup) { var numTravelers = 1 + Math.floor(Math.random() * 10); var travelerYOffset = station.y < streetCenterY ? -140 : 140; for (var i = 0; i < numTravelers; i++) { // Randomly choose gender: 0 = man, 1 = woman var gender = Math.random() < 0.5 ? "man" : "woman"; var traveler = new Traveler(); traveler.gender = gender; // Re-attach correct asset based on gender if (traveler.children.length > 0) { traveler.removeChild(traveler.children[0]); } var assetId = gender === "woman" ? "travelerWoman" : "traveler"; traveler.attachAsset(assetId, { anchorX: 0.5, anchorY: 1 }); traveler.station = station; traveler.x = 0; traveler.y = travelerYOffset + (station.y < streetCenterY ? -i * 130 : i * 130); station.addChild(traveler); station.travelers.push(traveler); } } // Add station above the shop panel so stations are always on top of shop UI var shopPanelIndex = game.children.indexOf(shopPanelBg); if (shopPanelIndex !== -1) { game.addChildAt(station, shopPanelIndex); } else { game.addChild(station); } stations.push(station); } // (removed duplicate old station spawn code) // Move handler for dragging bus function handleMove(x, y, obj) { // Only drag if started on bus if (bus.dragging) { bus.y = y - bus.dragOffsetY; } } game.move = handleMove; // Down handler: start drag if on bus, or start holding for rightward movement var holdingRight = false; game.down = function (x, y, obj) { // Convert to bus local coordinates var local = bus.toLocal(game.toGlobal({ x: x, y: y })); // If within bus bounds, start drag if (local.x > -bus.width / 2 && local.x < bus.width / 2 && local.y > -bus.height && local.y < 0) { bus.dragging = true; bus.dragOffsetY = y - bus.y; } else { // Start holding for rightward movement holdingRight = true; } }; // Up handler: stop drag and stop holding rightward movement game.up = function (x, y, obj) { bus.dragging = false; holdingRight = false; }; // Main game update loop game.update = function () { // --- SETTINGS LOGIC --- // Show/hide settings panel if (settingsBtnPressed) { settingsPanelBg.visible = true; takeBtnBg.visible = false; dropBtnBg.visible = false; shopBtnBg.visible = false; settingsBtnBg.visible = false; settingsBtnPressed = false; } // Settings close if (settingsClosePressed) { settingsPanelBg.visible = false; takeBtnBg.visible = true; dropBtnBg.visible = true; shopBtnBg.visible = true; settingsBtnBg.visible = true; settingsClosePressed = false; } // Music toggle if (musicTogglePressed) { musicEnabled = !musicEnabled; musicToggleTxt.setText('Music: ' + (musicEnabled ? 'On' : 'Off')); if (musicEnabled) { LK.playMusic('baba', { loop: true, fade: { start: 0, end: 0.01, duration: 1000 } }); } else { LK.stopMusic(); } musicTogglePressed = false; } // Sound toggle if (soundTogglePressed) { soundEnabled = !soundEnabled; soundToggleTxt.setText('Sound: ' + (soundEnabled ? 'On' : 'Off')); soundTogglePressed = false; } // --- SHOP LOGIC --- // Show/hide shop panel if (shopBtnPressed) { shopPanelBg.visible = true; takeBtnBg.visible = false; dropBtnBg.visible = false; shopBtnBg.visible = false; settingsBtnBg.visible = false; shopBtnPressed = false; } // Shop close if (shopClosePressed) { shopPanelBg.visible = false; takeBtnBg.visible = true; dropBtnBg.visible = true; shopBtnBg.visible = true; settingsBtnBg.visible = true; shopClosePressed = false; } // Update shop upgrade button texts for (var i = 0; i < 3; i++) { var level = shopUpgradeLevels[i]; var cost = shopUpgradeCosts[i] + level * 20; var max = shopUpgradeMax[i]; var name = shopUpgradeNames[i]; var desc = shopUpgradeDesc[i]; // Update each text field shopUpgradeTxts[i].name.setText(name); shopUpgradeTxts[i].level.setText('(Lv.' + level + (level >= max ? ' MAX' : '') + ')'); shopUpgradeTxts[i].cost.setText('Cost: ' + (level >= max ? '-' : cost)); // Re-layout: update x positions in case text width changed shopUpgradeTxts[i].level.x = shopUpgradeTxts[i].name.x + shopUpgradeTxts[i].name.width + 32; shopUpgradeTxts[i].cost.x = shopUpgradeTxts[i].level.x + shopUpgradeTxts[i].level.width + 32; shopUpgradeTxts[i].cost.y = 0; shopUpgradeBtns[i].alpha = level < max ? 1 : 0.5; shopUpgradeBtns[i].interactive = level < max; } // Update bus capacity text in main UI var travelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0; if (typeof maxCapacity === "undefined") maxCapacity = 10; busCapacityBarText.setText(travelersOnBoardCount + "/" + maxCapacity); // (shopBusCapacityText removed, no update needed) // Handle shop upgrades for (var i = 0; i < 3; i++) { if (shopUpgradePressed[i]) { var level = shopUpgradeLevels[i]; var cost = shopUpgradeCosts[i] + level * 20; var max = shopUpgradeMax[i]; if (level < max && score >= cost) { score -= cost; scoreTxt.setText(score); shopUpgradeLevels[i]++; // Apply upgrade effect if (i === 0) { // Capacity maxCapacity += 2; } else if (i === 1) { // Speed scrollSpeed += 2; } else if (i === 2) { // Money if (typeof moneyPerTraveler === "undefined") moneyPerTraveler = 1; moneyPerTraveler += 1; } } shopUpgradePressed[i] = false; } } if (shopPanelBg.visible) { // Pause game logic when shop is open return; } // Move bus/world right if holding if (typeof holdingRight !== "undefined" && holdingRight) { var moveAmount = scrollSpeed; // Instead of moving the bus, move the world and keep bus centered scrollX += moveAmount; for (var i = 0; i < stations.length; i++) { stations[i].x -= moveAmount; } // Move and repeat street backgrounds for (var i = 0; i < streetBgs.length; i++) { streetBgs[i].x -= moveAmount; // If streetBg is fully off the left, move it to the right end if (streetBgs[i].x < -streetBgWidth / 2) { // Find the rightmost streetBg var maxX = streetBgs[0].x; for (var j = 1; j < streetBgs.length; j++) { if (streetBgs[j].x > maxX) maxX = streetBgs[j].x; } streetBgs[i].x = maxX + streetBgWidth; } } } // Always keep bus in the center of the screen horizontally bus.x = 2048 / 2; // Move all stations and their travelers (no auto scroll) for (var i = 0; i < stations.length; i++) { var station = stations[i]; // No station.x -= scrollSpeed; station.update(); for (var j = 0; j < station.travelers.length; j++) { station.travelers[j].update(); } } // Update bus bus.update(); // --- Meter Bar Update: Show distance to nearest upcoming station --- var nearestStation = null; var nearestDist = null; for (var i = 0; i < stations.length; i++) { var station = stations[i]; // Only consider stations ahead of the bus (to the right) var dx = station.x - bus.x; if (dx >= 0) { if (nearestStation === null || dx < nearestDist) { nearestStation = station; nearestDist = dx; } } } if (nearestStation) { var dist = Math.max(0, Math.floor(nearestDist)); var maxDist = 1200; // Max distance for full bar var barWidth = Math.max(0, Math.min(600, 600 * (1 - dist / maxDist))); meterBarFill.width = barWidth; meterBarText.setText(dist + "m"); } else { meterBarFill.width = 0; meterBarText.setText("0m"); } // Endless station spawning logic // Find the rightmost station's x var rightmostStationX = 0; for (var i = 0; i < stations.length; i++) { if (stations[i].x > rightmostStationX) rightmostStationX = stations[i].x; } // If the rightmost station is less than 2.5 screens ahead, spawn a new one if (rightmostStationX < scrollX + 2048 * 2.5) { // Alternate pickup/drop var isPickup = stations.length % 2 === 0; // Place new station at least minStationSpacing after the last one, with some randomization var spacing = minStationSpacing + Math.floor(Math.random() * (maxStationSpacing - minStationSpacing + 1)); var newX = rightmostStationX + spacing; spawnStation(newX, isPickup); } // --- Manual pickup and drop-off logic with Take Travelers button --- // --- Manual pickup and drop-off logic with Take Travelers button --- if (typeof bus.travelersOnBoard === "undefined") { bus.travelersOnBoard = []; bus.lastPickupStationIndex = -1; bus.lastDropStationIndex = -1; } // --- Enable/disable Take Travelers button based on action possibility --- var canTake = false; var canDrop = false; var dropStationIndex = -1; // Check if we can take travelers at a nearby station for (var i = 0; i < stations.length; i++) { var station = stations[i]; // Take: must be pickup station, has travelers, close, and not already picked up here if (station.isPickup && station.travelers.length > 0 && Math.abs(station.x - bus.x) < 180 && bus.lastPickupStationIndex !== i) { canTake = true; } // Drop: must be drop station, bus has travelers, close, and not already dropped here if (!station.isPickup && bus.travelersOnBoard && bus.travelersOnBoard.length > 0 && Math.abs(station.x - bus.x) < 180 && bus.lastDropStationIndex !== i) { canDrop = true; dropStationIndex = i; } } // --- Bus Capacity Bar Update --- if (typeof maxCapacity === "undefined") maxCapacity = 10; var travelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0; var capBarWidth = Math.max(0, Math.min(600, 600 * (travelersOnBoardCount / maxCapacity))); busCapacityBarFill.width = capBarWidth; // Show plusCapacityBarFill for new travelers picked up this round if (typeof bus.lastTravelersOnBoardCount === "undefined") bus.lastTravelersOnBoardCount = 0; var plusCount = travelersOnBoardCount - bus.lastTravelersOnBoardCount; if (plusCount > 0) { var plusBarWidth = Math.max(0, Math.min(600, 600 * (plusCount / maxCapacity))); plusCapacityBarFill.width = plusBarWidth; plusCapacityBarFill.visible = true; } else { plusCapacityBarFill.width = 0; plusCapacityBarFill.visible = false; } // busCapacityBarText removed from main UI; only update shopBusCapacityText in shop panel // Visually enable/disable the take and drop buttons takeBtnBg.alpha = canTake ? 1 : 0.4; takeBtnBg.interactive = canTake; dropBtnBg.alpha = canDrop ? 1 : 0.4; dropBtnBg.interactive = canDrop; // --- Manual Pickup logic: only when button pressed --- if (typeof maxCapacity === "undefined") maxCapacity = 10; if (typeof moneyPerTraveler === "undefined") moneyPerTraveler = 1; if (takeBtnPressed) { for (var i = 0; i < stations.length; i++) { var station = stations[i]; // Only pick up if bus is close to pickup station, this is a new station, and this station has travelers if (station.isPickup && station.travelers.length > 0 && Math.abs(station.x - bus.x) < 180 && bus.lastPickupStationIndex !== i) { // Pick up all waiting travelers at this station var waitingTravelers = []; for (var j = station.travelers.length - 1; j >= 0; j--) { var traveler = station.travelers[j]; if (traveler.waiting) { waitingTravelers.push(traveler); } } // Actually pick up the travelers, but only up to available capacity var travelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0; var availableCapacity = maxCapacity - travelersOnBoardCount; var numToPick = Math.min(availableCapacity, waitingTravelers.length); for (var w = 0; w < numToPick; w++) { var traveler = waitingTravelers[w]; traveler.waiting = false; // Animate traveler to bus, then destroy tween(traveler, { x: bus.x - station.x, y: bus.y - station.y - 100, alpha: 0 }, { duration: 400, easing: tween.easeIn, onFinish: function (trav) { return function () { trav.destroy(); }; }(traveler) }); // Add to bus's onboard list if (!bus.travelersOnBoard) bus.travelersOnBoard = []; bus.travelersOnBoard.push({ traveler: traveler, pickedUpAt: i }); // Remove from station station.removeTraveler(traveler); // No score for pickup; award score on drop-off instead } // Mark this station as the last pickup bus.lastPickupStationIndex = i; // Update lastTravelersOnBoardCount for plus bar bus.lastTravelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0; // Play cheer sound when travelers are picked up if (soundEnabled) { LK.getSound('cheer').play(); } break; // Only pick up at one station per press } } takeBtnPressed = false; // Reset button press } // --- Manual Drop-off logic: only when drop button pressed --- if (dropBtnPressed && canDrop && dropStationIndex !== -1) { var station = stations[dropStationIndex]; // Drop all travelers currently on the bus if (bus.travelersOnBoard && bus.travelersOnBoard.length > 0) { // Calculate base Y for stacking var baseY = 0; var stackDir = 1; // If station is above street, stack upwards; if below, stack downwards var streetCenterY = streetBgY; if (station.y < streetCenterY) { baseY = -140; stackDir = -1; } else { baseY = 140; stackDir = 1; } // Count how many travelers are already at this drop station (for stacking) var alreadyDropped = 0; for (var tt = 0; tt < station.travelers.length; tt++) { if (station.travelers[tt].dropped) alreadyDropped++; } // Count how many men and women are on board var menToDrop = 0; var womenToDrop = 0; for (var t = 0; t < bus.travelersOnBoard.length; t++) { var travelerObj = bus.travelersOnBoard[t]; var traveler = travelerObj.traveler; if (traveler.gender === "woman") { womenToDrop++; } else { menToDrop++; } } // Drop correct number of men and women, stacking them var dropIndex = 0; for (var t = 0; t < menToDrop; t++, dropIndex++) { var newTraveler = new Traveler(); newTraveler.gender = "man"; // Remove default asset and attach correct one if (newTraveler.children.length > 0) { newTraveler.removeChild(newTraveler.children[0]); } newTraveler.attachAsset('traveler', { anchorX: 0.5, anchorY: 1 }); newTraveler.x = bus.x - station.x; newTraveler.y = bus.y - station.y - 100; newTraveler.alpha = 0; newTraveler.waiting = false; newTraveler.dropped = true; newTraveler.station = station; // Stack vertically at the station var stackIndex = alreadyDropped + dropIndex; newTraveler.x = 0; newTraveler.y = baseY + stackDir * stackIndex * 130; newTraveler.alpha = 0; station.addChild(newTraveler); station.travelers.push(newTraveler); tween(newTraveler, { x: 0, y: baseY + stackDir * stackIndex * 130, alpha: 1 }, { duration: 400, easing: tween.easeOut }); // Award score for each traveler dropped score += moneyPerTraveler; scoreTxt.setText(score); } for (var t = 0; t < womenToDrop; t++, dropIndex++) { var newTraveler = new Traveler(); newTraveler.gender = "woman"; // Remove default asset and attach correct one if (newTraveler.children.length > 0) { newTraveler.removeChild(newTraveler.children[0]); } newTraveler.attachAsset('travelerWoman', { anchorX: 0.5, anchorY: 1 }); newTraveler.x = bus.x - station.x; newTraveler.y = bus.y - station.y - 100; newTraveler.alpha = 0; newTraveler.waiting = false; newTraveler.dropped = true; newTraveler.station = station; // Stack vertically at the station var stackIndex = alreadyDropped + dropIndex; newTraveler.x = 0; newTraveler.y = baseY + stackDir * stackIndex * 130; newTraveler.alpha = 0; station.addChild(newTraveler); station.travelers.push(newTraveler); tween(newTraveler, { x: 0, y: baseY + stackDir * stackIndex * 130, alpha: 1 }, { duration: 400, easing: tween.easeOut }); // Award score for each traveler dropped score += moneyPerTraveler; scoreTxt.setText(score); } // Clear bus bus.travelersOnBoard = []; // Reset lastTravelersOnBoardCount for plus bar bus.lastTravelersOnBoardCount = 0; // Mark this station as the last drop bus.lastDropStationIndex = dropStationIndex; // Play cheer sound when travelers are dropped off if (soundEnabled) { LK.getSound('cheer').play(); } } dropBtnPressed = false; // Reset button press } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Bus (player) class
var Bus = Container.expand(function () {
var self = Container.call(this);
var busAsset = self.attachAsset('bus', {
anchorX: 0.5,
anchorY: 1
});
// For touch drag
self.dragging = false;
self.dragOffsetY = 0;
// Called every tick
self.update = function () {
// Clamp bus to road (vertical bounds)
if (self.y < 400) self.y = 400;
if (self.y > 2200) self.y = 2200;
};
// Touch down on bus
self.down = function (x, y, obj) {
self.dragging = true;
self.dragOffsetY = y - self.y;
};
// Touch up on bus
self.up = function (x, y, obj) {
self.dragging = false;
};
return self;
});
// Bus Station class
var BusStation = Container.expand(function () {
var self = Container.call(this);
// Use dropStation asset if isPickup is false, otherwise use station
var assetId = typeof self.isPickup !== "undefined" && self.isPickup === false ? 'dropStation' : 'station';
var stationAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 1
});
// List of travelers at this station
self.travelers = [];
// Travelers are now spawned in spawnStation, not here
// Remove traveler from this station
self.removeTraveler = function (traveler) {
for (var i = 0; i < self.travelers.length; i++) {
if (self.travelers[i] === traveler) {
self.travelers.splice(i, 1);
break;
}
}
};
// Called every tick
self.update = function () {
// No longer remove station when bus passes; stations persist
};
return self;
});
// Traveler class
var Traveler = Container.expand(function () {
var self = Container.call(this);
// Attach correct asset based on gender (default to man if not set yet)
var assetId = typeof self.gender !== "undefined" && self.gender === "woman" ? "travelerWoman" : "traveler";
var travelerAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 1
});
// Reference to the station this traveler belongs to
self.station = null;
// Whether this traveler is still waiting at the station
self.waiting = true;
// Track if this traveler was dropped (for possible future logic)
self.dropped = false;
// Time (in ticks) until this traveler leaves
self.leaveTick = 0;
// Called every tick
self.update = function () {
// If not waiting, do nothing
if (!self.waiting) return;
// Travelers no longer leave by themselves
// Remove the leave-tick logic entirely
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222222
});
/****
* Game Code
****/
// green
// orange
// visually distinguish drop station (flipX as example)
// Play baba music at game start
LK.playMusic('baba', {
loop: true,
fade: {
start: 0,
end: 0.01,
duration: 1000
}
});
var streetBgWidth = 900;
var streetBgHeight = 400;
var streetBgY = 1500;
var streetBgs = [];
for (var i = 0; i < 3; i++) {
var streetBg = LK.getAsset('streetBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + (i - 1) * streetBgWidth,
y: streetBgY
});
game.addChild(streetBg);
streetBgs.push(streetBg);
}
// Traveler
// Bus station
// Bus (player vehicle)
// Game world scroll variables
var scrollX = 0; // How far the world has scrolled right
var scrollSpeed = 10; // Pixels per tick
// Bus (player)
var bus = new Bus();
game.addChild(bus);
bus.x = 400;
bus.y = 1500;
// List of stations in the world
var stations = [];
// Spawn fewer stations, with much greater spacing, and ensure max 2 stations visible on screen
var lastStationX = 1200;
var NUM_STATIONS = 6; // fewer stations for less crowding
var minStationSpacing = 1200; // minimum distance between stations (at least 1 screen width)
var maxStationSpacing = 1600; // maximum distance between stations
var stationWidth = 400;
for (var i = 0; i < NUM_STATIONS; i++) {
var isPickup = i % 2 === 0;
var tryCount = 0;
var valid = false;
var tryX = 0;
while (!valid && tryCount < 100) {
var spacing = minStationSpacing + Math.floor(Math.random() * (maxStationSpacing - minStationSpacing + 1));
if (i === 0) {
tryX = 1200;
} else {
tryX = lastStationX + spacing;
}
// Check for overlap with all previous stations (ensure at least minStationSpacing between centers)
valid = true;
for (var j = 0; j < stations.length; j++) {
var prev = stations[j];
if (Math.abs(tryX - prev.x) < minStationSpacing) {
valid = false;
break;
}
}
tryCount++;
}
lastStationX = tryX;
spawnStation(lastStationX, isPickup);
}
// GUI: Score (number of travelers picked up)
var score = 0;
var scoreTxt = new Text2('0', {
size: 120,
fill: 0xFFFFFF,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- Meter Bar UI ---
var meterBarBg = LK.getAsset('meterBarBg', {
anchorX: 0,
anchorY: 0.5,
x: 200,
y: 120
});
var meterBarFill = LK.getAsset('meterBarBg', {
anchorX: 0,
anchorY: 0.5,
x: 200,
y: 120,
width: 600,
height: 40
});
meterBarFill.width = 0;
LK.gui.top.addChild(meterBarBg);
LK.gui.top.addChild(meterBarFill);
// Add a text label to the meter bar showing the remaining distance in meters
var meterBarText = new Text2('0m', {
size: 60,
fill: 0xffffff,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
meterBarText.anchor.set(0.5, 0.5);
meterBarText.x = 200 + 600 / 2;
meterBarText.y = 120;
LK.gui.top.addChild(meterBarText);
// --- Bus Capacity Bar UI ---
var busCapacityBarBg = LK.getAsset('capacityBarBg', {
anchorX: 0,
anchorY: 0.5,
x: 200,
y: 200
});
// Plus capacity bar fill (dark green, for new travelers picked up this round)
var plusCapacityBarFill = LK.getAsset('plusCapacityBarBg', {
anchorX: 0,
anchorY: 0.5,
x: 200,
y: 200,
width: 600,
height: 32
});
plusCapacityBarFill.width = 0;
var busCapacityBarFill = LK.getAsset('plusCapacityBarBg', {
anchorX: 0,
anchorY: 0.5,
x: 200,
y: 200,
width: 600,
height: 32
});
busCapacityBarFill.width = 0;
LK.gui.top.addChild(busCapacityBarBg);
LK.gui.top.addChild(plusCapacityBarFill);
LK.gui.top.addChild(busCapacityBarFill);
// Add a bus capacity text label to the bus capacity bar in the main UI
var busCapacityBarText = new Text2('0/10', {
size: 48,
fill: 0xffffff,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
busCapacityBarText.anchor.set(0.5, 0.5);
busCapacityBarText.x = 200 + 600 / 2;
busCapacityBarText.y = 200;
LK.gui.top.addChild(busCapacityBarText);
// For dragging bus
var dragBus = false;
// --- Take Travelers Button ---
var takeBtnBg = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 320,
y: 2732 - 200
});
var takeBtnTxt = new Text2('Take Travelers', {
size: 60,
fill: 0xffffff
});
takeBtnTxt.anchor.set(0.5, 0.5);
takeBtnBg.addChild(takeBtnTxt);
game.addChild(takeBtnBg);
// --- Drop Travelers Button ---
var dropBtnBg = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 320,
y: 2732 - 200
});
var dropBtnTxt = new Text2('Drop Travelers', {
size: 60,
fill: 0xffffff
});
dropBtnTxt.anchor.set(0.5, 0.5);
dropBtnBg.addChild(dropBtnTxt);
game.addChild(dropBtnBg);
// --- Shop Button ---
var shopBtnBg = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 320,
// Move to left side of the screen, away from top left 100x100 area
y: 2732 - 200
});
var shopBtnTxt = new Text2('Shop', {
size: 60,
fill: 0xffffff
});
shopBtnTxt.anchor.set(0.5, 0.5);
shopBtnBg.addChild(shopBtnTxt);
game.addChild(shopBtnBg);
// --- Settings Button ---
var settingsBtnBg = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 320,
y: 2732 - 200
});
var settingsBtnTxt = new Text2('Settings', {
size: 60,
fill: 0xffffff
});
settingsBtnTxt.anchor.set(0.5, 0.5);
settingsBtnBg.addChild(settingsBtnTxt);
game.addChild(settingsBtnBg);
// --- Settings Panel ---
var settingsPanelBg = LK.getAsset('whitePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
settingsPanelBg.visible = false;
game.addChild(settingsPanelBg);
// Settings title
var settingsTitle = new Text2('Settings', {
size: 90,
fill: 0x222222
});
settingsTitle.anchor.set(0.5, 0);
settingsTitle.x = 0;
settingsTitle.y = -350;
settingsPanelBg.addChild(settingsTitle);
// Music toggle button
var musicToggleBtn = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -100,
width: 400,
height: 120
});
var musicToggleTxt = new Text2('Music: On', {
size: 56,
fill: 0xffffff
});
musicToggleTxt.anchor.set(0.5, 0.5);
musicToggleBtn.addChild(musicToggleTxt);
settingsPanelBg.addChild(musicToggleBtn);
// Sound toggle button
var soundToggleBtn = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 80,
width: 400,
height: 120
});
var soundToggleTxt = new Text2('Sound: On', {
size: 56,
fill: 0xffffff
});
soundToggleTxt.anchor.set(0.5, 0.5);
soundToggleBtn.addChild(soundToggleTxt);
settingsPanelBg.addChild(soundToggleBtn);
// Set initial text for music and sound toggles
musicToggleTxt.setText('Music: On');
soundToggleTxt.setText('Sound: On');
// Settings close button
var settingsCloseBtn = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 260,
width: 300,
height: 90
});
var settingsCloseTxt = new Text2('Close', {
size: 48,
fill: 0x222222
});
settingsCloseTxt.anchor.set(0.5, 0.5);
settingsCloseBtn.addChild(settingsCloseTxt);
settingsPanelBg.addChild(settingsCloseBtn);
// Track settings state
var settingsBtnPressed = false;
var settingsClosePressed = false;
var musicTogglePressed = false;
var soundTogglePressed = false;
var musicEnabled = true;
var soundEnabled = true;
// Shop UI elements (hidden by default)
var shopPanelBg = LK.getAsset('whitePanel', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
shopPanelBg.visible = false;
game.addChild(shopPanelBg);
// Shop title, centered near the top of the panel
var shopTitle = new Text2('Shop', {
size: 90,
fill: 0x222222
});
shopTitle.anchor.set(0.5, 0);
shopTitle.x = 0;
// Move the shop title higher, since capacity text is removed
shopTitle.y = -380;
shopPanelBg.addChild(shopTitle);
// Shop upgrade buttons and texts
var shopUpgradeBtns = [];
var shopUpgradeTxts = [];
var shopUpgradeCosts = [30, 30, 30]; // initial costs
var shopUpgradeLevels = [0, 0, 0]; // [capacity, speed, money]
var shopUpgradeMax = [10, 10, 10];
var shopUpgradeNames = ['Capacity', 'Speed', 'Money'];
var shopUpgradeDesc = ['Increase bus capacity by 2', 'Increase speed by 2', 'Increase money per traveler by 1'];
for (var i = 0; i < 3; i++) {
// Create a container for each row
var row = new Container();
// Vertically space rows within the white panel, below the title, with even spacing
var rowHeight = 180;
// Move the upgrade rows higher as well
var panelContentTop = shopTitle.y + shopTitle.height + 16;
row.y = panelContentTop + i * rowHeight;
// Center row horizontally in the panel
row.x = 0;
// --- Lay out texts from left to right, all inside the white panel ---
// Name
var nameTxt = new Text2(shopUpgradeNames[i], {
size: 56,
fill: 0x222222
});
nameTxt.anchor.set(0, 0.5);
nameTxt.x = -900 / 2 + 40;
nameTxt.y = 0;
// Level
var levelTxt = new Text2('(Lv.0)', {
size: 56,
fill: 0x222222
});
levelTxt.anchor.set(0, 0.5);
levelTxt.x = nameTxt.x + nameTxt.width + 24;
levelTxt.y = 0;
// Cost (now below desc, but desc removed)
var costTxt = new Text2('Cost: 30', {
size: 44,
fill: 0x888888
});
costTxt.anchor.set(0, 0.5);
// Place cost where desc would have started
costTxt.x = levelTxt.x + levelTxt.width + 24;
costTxt.y = 0;
// Upgrade button (right side, inside panel, with margin)
var btn = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 900 / 2 - 120,
y: 0,
width: 180,
height: 70
});
btn.visible = true;
// Button label
var btnLabel = new Text2('Upgrade', {
size: 32,
fill: 0xffffff
});
btnLabel.anchor.set(0.5, 0.5);
btn.addChild(btnLabel);
// Add all texts and button to row
row.addChild(nameTxt);
row.addChild(levelTxt);
row.addChild(costTxt);
row.addChild(btn);
// Add row to shop panel
shopPanelBg.addChild(row);
// Track for logic
shopUpgradeBtns.push(btn);
// Store all text objects for update logic (desc removed)
shopUpgradeTxts.push({
name: nameTxt,
level: levelTxt,
cost: costTxt
});
}
shopPanelBg.visible = false;
// Shop close button
var shopCloseBtn = LK.getAsset('takeBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
x: 900 / 2,
y: 820
});
var shopCloseTxt = new Text2('Close', {
size: 48,
fill: 0x222222
});
shopCloseTxt.anchor.set(0.5, 0.5);
shopCloseBtn.addChild(shopCloseTxt);
shopPanelBg.addChild(shopCloseBtn);
// Track if take or drop or shop button is pressed this frame
var takeBtnPressed = false;
var dropBtnPressed = false;
var shopBtnPressed = false;
var shopUpgradePressed = [false, false, false];
var shopClosePressed = false;
// Button press handlers
takeBtnBg.down = function (x, y, obj) {
if (takeBtnBg.interactive !== false && !shopPanelBg.visible) {
takeBtnPressed = true;
}
};
dropBtnBg.down = function (x, y, obj) {
if (dropBtnBg.interactive !== false && !shopPanelBg.visible) {
dropBtnPressed = true;
}
};
// Shop button handler
shopBtnBg.down = function (x, y, obj) {
if (!shopPanelBg.visible && !settingsPanelBg.visible) {
shopBtnPressed = true;
}
};
// Settings button handler
settingsBtnBg.down = function (x, y, obj) {
if (!settingsPanelBg.visible && !shopPanelBg.visible) {
settingsBtnPressed = true;
}
};
// Settings close button handler
settingsCloseBtn.down = function (x, y, obj) {
if (settingsPanelBg.visible) {
settingsClosePressed = true;
}
};
// Music toggle button handler
musicToggleBtn.down = function (x, y, obj) {
if (settingsPanelBg.visible) {
musicTogglePressed = true;
}
};
// Sound toggle button handler
soundToggleBtn.down = function (x, y, obj) {
if (settingsPanelBg.visible) {
soundTogglePressed = true;
}
};
// Shop upgrade button handlers
for (var i = 0; i < 3; i++) {
(function (idx) {
shopUpgradeBtns[idx].down = function (x, y, obj) {
if (shopPanelBg.visible) {
shopUpgradePressed[idx] = true;
}
};
})(i);
}
// Shop close button handler
shopCloseBtn.down = function (x, y, obj) {
if (shopPanelBg.visible) {
shopClosePressed = true;
}
};
// Generate a station at a given x, alternating pickup/drop and up/down
function spawnStation(x, isPickup) {
var station = new BusStation();
station.isPickup = !!isPickup;
// Re-attach correct asset if drop station (for existing BusStation instances)
if (!station.isPickup) {
if (station.children.length > 0) {
station.removeChild(station.children[0]);
}
station.attachAsset('dropStation', {
anchorX: 0.5,
anchorY: 1
});
}
// Alternate up/down for each station, placing them exactly outside the street (not inside)
var streetCenterY = streetBgY;
var stationAssetHeight = 160; // matches asset height for station/dropStation
if (stations.length % 2 === 0) {
// Place station so its bottom edge is flush with the top edge of the street (just above street)
station.y = streetCenterY - streetBgHeight / 2;
} else {
// Place station so its top edge is flush with the bottom edge of the street (just below street)
station.y = streetCenterY + streetBgHeight / 2 + stationAssetHeight;
}
// Place at given x
station.x = x;
// Spawn travelers immediately if pickup station
if (station.isPickup) {
var numTravelers = 1 + Math.floor(Math.random() * 10);
var travelerYOffset = station.y < streetCenterY ? -140 : 140;
for (var i = 0; i < numTravelers; i++) {
// Randomly choose gender: 0 = man, 1 = woman
var gender = Math.random() < 0.5 ? "man" : "woman";
var traveler = new Traveler();
traveler.gender = gender;
// Re-attach correct asset based on gender
if (traveler.children.length > 0) {
traveler.removeChild(traveler.children[0]);
}
var assetId = gender === "woman" ? "travelerWoman" : "traveler";
traveler.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 1
});
traveler.station = station;
traveler.x = 0;
traveler.y = travelerYOffset + (station.y < streetCenterY ? -i * 130 : i * 130);
station.addChild(traveler);
station.travelers.push(traveler);
}
}
// Add station above the shop panel so stations are always on top of shop UI
var shopPanelIndex = game.children.indexOf(shopPanelBg);
if (shopPanelIndex !== -1) {
game.addChildAt(station, shopPanelIndex);
} else {
game.addChild(station);
}
stations.push(station);
}
// (removed duplicate old station spawn code)
// Move handler for dragging bus
function handleMove(x, y, obj) {
// Only drag if started on bus
if (bus.dragging) {
bus.y = y - bus.dragOffsetY;
}
}
game.move = handleMove;
// Down handler: start drag if on bus, or start holding for rightward movement
var holdingRight = false;
game.down = function (x, y, obj) {
// Convert to bus local coordinates
var local = bus.toLocal(game.toGlobal({
x: x,
y: y
}));
// If within bus bounds, start drag
if (local.x > -bus.width / 2 && local.x < bus.width / 2 && local.y > -bus.height && local.y < 0) {
bus.dragging = true;
bus.dragOffsetY = y - bus.y;
} else {
// Start holding for rightward movement
holdingRight = true;
}
};
// Up handler: stop drag and stop holding rightward movement
game.up = function (x, y, obj) {
bus.dragging = false;
holdingRight = false;
};
// Main game update loop
game.update = function () {
// --- SETTINGS LOGIC ---
// Show/hide settings panel
if (settingsBtnPressed) {
settingsPanelBg.visible = true;
takeBtnBg.visible = false;
dropBtnBg.visible = false;
shopBtnBg.visible = false;
settingsBtnBg.visible = false;
settingsBtnPressed = false;
}
// Settings close
if (settingsClosePressed) {
settingsPanelBg.visible = false;
takeBtnBg.visible = true;
dropBtnBg.visible = true;
shopBtnBg.visible = true;
settingsBtnBg.visible = true;
settingsClosePressed = false;
}
// Music toggle
if (musicTogglePressed) {
musicEnabled = !musicEnabled;
musicToggleTxt.setText('Music: ' + (musicEnabled ? 'On' : 'Off'));
if (musicEnabled) {
LK.playMusic('baba', {
loop: true,
fade: {
start: 0,
end: 0.01,
duration: 1000
}
});
} else {
LK.stopMusic();
}
musicTogglePressed = false;
}
// Sound toggle
if (soundTogglePressed) {
soundEnabled = !soundEnabled;
soundToggleTxt.setText('Sound: ' + (soundEnabled ? 'On' : 'Off'));
soundTogglePressed = false;
}
// --- SHOP LOGIC ---
// Show/hide shop panel
if (shopBtnPressed) {
shopPanelBg.visible = true;
takeBtnBg.visible = false;
dropBtnBg.visible = false;
shopBtnBg.visible = false;
settingsBtnBg.visible = false;
shopBtnPressed = false;
}
// Shop close
if (shopClosePressed) {
shopPanelBg.visible = false;
takeBtnBg.visible = true;
dropBtnBg.visible = true;
shopBtnBg.visible = true;
settingsBtnBg.visible = true;
shopClosePressed = false;
}
// Update shop upgrade button texts
for (var i = 0; i < 3; i++) {
var level = shopUpgradeLevels[i];
var cost = shopUpgradeCosts[i] + level * 20;
var max = shopUpgradeMax[i];
var name = shopUpgradeNames[i];
var desc = shopUpgradeDesc[i];
// Update each text field
shopUpgradeTxts[i].name.setText(name);
shopUpgradeTxts[i].level.setText('(Lv.' + level + (level >= max ? ' MAX' : '') + ')');
shopUpgradeTxts[i].cost.setText('Cost: ' + (level >= max ? '-' : cost));
// Re-layout: update x positions in case text width changed
shopUpgradeTxts[i].level.x = shopUpgradeTxts[i].name.x + shopUpgradeTxts[i].name.width + 32;
shopUpgradeTxts[i].cost.x = shopUpgradeTxts[i].level.x + shopUpgradeTxts[i].level.width + 32;
shopUpgradeTxts[i].cost.y = 0;
shopUpgradeBtns[i].alpha = level < max ? 1 : 0.5;
shopUpgradeBtns[i].interactive = level < max;
}
// Update bus capacity text in main UI
var travelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0;
if (typeof maxCapacity === "undefined") maxCapacity = 10;
busCapacityBarText.setText(travelersOnBoardCount + "/" + maxCapacity);
// (shopBusCapacityText removed, no update needed)
// Handle shop upgrades
for (var i = 0; i < 3; i++) {
if (shopUpgradePressed[i]) {
var level = shopUpgradeLevels[i];
var cost = shopUpgradeCosts[i] + level * 20;
var max = shopUpgradeMax[i];
if (level < max && score >= cost) {
score -= cost;
scoreTxt.setText(score);
shopUpgradeLevels[i]++;
// Apply upgrade effect
if (i === 0) {
// Capacity
maxCapacity += 2;
} else if (i === 1) {
// Speed
scrollSpeed += 2;
} else if (i === 2) {
// Money
if (typeof moneyPerTraveler === "undefined") moneyPerTraveler = 1;
moneyPerTraveler += 1;
}
}
shopUpgradePressed[i] = false;
}
}
if (shopPanelBg.visible) {
// Pause game logic when shop is open
return;
}
// Move bus/world right if holding
if (typeof holdingRight !== "undefined" && holdingRight) {
var moveAmount = scrollSpeed;
// Instead of moving the bus, move the world and keep bus centered
scrollX += moveAmount;
for (var i = 0; i < stations.length; i++) {
stations[i].x -= moveAmount;
}
// Move and repeat street backgrounds
for (var i = 0; i < streetBgs.length; i++) {
streetBgs[i].x -= moveAmount;
// If streetBg is fully off the left, move it to the right end
if (streetBgs[i].x < -streetBgWidth / 2) {
// Find the rightmost streetBg
var maxX = streetBgs[0].x;
for (var j = 1; j < streetBgs.length; j++) {
if (streetBgs[j].x > maxX) maxX = streetBgs[j].x;
}
streetBgs[i].x = maxX + streetBgWidth;
}
}
}
// Always keep bus in the center of the screen horizontally
bus.x = 2048 / 2;
// Move all stations and their travelers (no auto scroll)
for (var i = 0; i < stations.length; i++) {
var station = stations[i];
// No station.x -= scrollSpeed;
station.update();
for (var j = 0; j < station.travelers.length; j++) {
station.travelers[j].update();
}
}
// Update bus
bus.update();
// --- Meter Bar Update: Show distance to nearest upcoming station ---
var nearestStation = null;
var nearestDist = null;
for (var i = 0; i < stations.length; i++) {
var station = stations[i];
// Only consider stations ahead of the bus (to the right)
var dx = station.x - bus.x;
if (dx >= 0) {
if (nearestStation === null || dx < nearestDist) {
nearestStation = station;
nearestDist = dx;
}
}
}
if (nearestStation) {
var dist = Math.max(0, Math.floor(nearestDist));
var maxDist = 1200; // Max distance for full bar
var barWidth = Math.max(0, Math.min(600, 600 * (1 - dist / maxDist)));
meterBarFill.width = barWidth;
meterBarText.setText(dist + "m");
} else {
meterBarFill.width = 0;
meterBarText.setText("0m");
}
// Endless station spawning logic
// Find the rightmost station's x
var rightmostStationX = 0;
for (var i = 0; i < stations.length; i++) {
if (stations[i].x > rightmostStationX) rightmostStationX = stations[i].x;
}
// If the rightmost station is less than 2.5 screens ahead, spawn a new one
if (rightmostStationX < scrollX + 2048 * 2.5) {
// Alternate pickup/drop
var isPickup = stations.length % 2 === 0;
// Place new station at least minStationSpacing after the last one, with some randomization
var spacing = minStationSpacing + Math.floor(Math.random() * (maxStationSpacing - minStationSpacing + 1));
var newX = rightmostStationX + spacing;
spawnStation(newX, isPickup);
}
// --- Manual pickup and drop-off logic with Take Travelers button ---
// --- Manual pickup and drop-off logic with Take Travelers button ---
if (typeof bus.travelersOnBoard === "undefined") {
bus.travelersOnBoard = [];
bus.lastPickupStationIndex = -1;
bus.lastDropStationIndex = -1;
}
// --- Enable/disable Take Travelers button based on action possibility ---
var canTake = false;
var canDrop = false;
var dropStationIndex = -1;
// Check if we can take travelers at a nearby station
for (var i = 0; i < stations.length; i++) {
var station = stations[i];
// Take: must be pickup station, has travelers, close, and not already picked up here
if (station.isPickup && station.travelers.length > 0 && Math.abs(station.x - bus.x) < 180 && bus.lastPickupStationIndex !== i) {
canTake = true;
}
// Drop: must be drop station, bus has travelers, close, and not already dropped here
if (!station.isPickup && bus.travelersOnBoard && bus.travelersOnBoard.length > 0 && Math.abs(station.x - bus.x) < 180 && bus.lastDropStationIndex !== i) {
canDrop = true;
dropStationIndex = i;
}
}
// --- Bus Capacity Bar Update ---
if (typeof maxCapacity === "undefined") maxCapacity = 10;
var travelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0;
var capBarWidth = Math.max(0, Math.min(600, 600 * (travelersOnBoardCount / maxCapacity)));
busCapacityBarFill.width = capBarWidth;
// Show plusCapacityBarFill for new travelers picked up this round
if (typeof bus.lastTravelersOnBoardCount === "undefined") bus.lastTravelersOnBoardCount = 0;
var plusCount = travelersOnBoardCount - bus.lastTravelersOnBoardCount;
if (plusCount > 0) {
var plusBarWidth = Math.max(0, Math.min(600, 600 * (plusCount / maxCapacity)));
plusCapacityBarFill.width = plusBarWidth;
plusCapacityBarFill.visible = true;
} else {
plusCapacityBarFill.width = 0;
plusCapacityBarFill.visible = false;
}
// busCapacityBarText removed from main UI; only update shopBusCapacityText in shop panel
// Visually enable/disable the take and drop buttons
takeBtnBg.alpha = canTake ? 1 : 0.4;
takeBtnBg.interactive = canTake;
dropBtnBg.alpha = canDrop ? 1 : 0.4;
dropBtnBg.interactive = canDrop;
// --- Manual Pickup logic: only when button pressed ---
if (typeof maxCapacity === "undefined") maxCapacity = 10;
if (typeof moneyPerTraveler === "undefined") moneyPerTraveler = 1;
if (takeBtnPressed) {
for (var i = 0; i < stations.length; i++) {
var station = stations[i];
// Only pick up if bus is close to pickup station, this is a new station, and this station has travelers
if (station.isPickup && station.travelers.length > 0 && Math.abs(station.x - bus.x) < 180 && bus.lastPickupStationIndex !== i) {
// Pick up all waiting travelers at this station
var waitingTravelers = [];
for (var j = station.travelers.length - 1; j >= 0; j--) {
var traveler = station.travelers[j];
if (traveler.waiting) {
waitingTravelers.push(traveler);
}
}
// Actually pick up the travelers, but only up to available capacity
var travelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0;
var availableCapacity = maxCapacity - travelersOnBoardCount;
var numToPick = Math.min(availableCapacity, waitingTravelers.length);
for (var w = 0; w < numToPick; w++) {
var traveler = waitingTravelers[w];
traveler.waiting = false;
// Animate traveler to bus, then destroy
tween(traveler, {
x: bus.x - station.x,
y: bus.y - station.y - 100,
alpha: 0
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function (trav) {
return function () {
trav.destroy();
};
}(traveler)
});
// Add to bus's onboard list
if (!bus.travelersOnBoard) bus.travelersOnBoard = [];
bus.travelersOnBoard.push({
traveler: traveler,
pickedUpAt: i
});
// Remove from station
station.removeTraveler(traveler);
// No score for pickup; award score on drop-off instead
}
// Mark this station as the last pickup
bus.lastPickupStationIndex = i;
// Update lastTravelersOnBoardCount for plus bar
bus.lastTravelersOnBoardCount = bus.travelersOnBoard ? bus.travelersOnBoard.length : 0;
// Play cheer sound when travelers are picked up
if (soundEnabled) {
LK.getSound('cheer').play();
}
break; // Only pick up at one station per press
}
}
takeBtnPressed = false; // Reset button press
}
// --- Manual Drop-off logic: only when drop button pressed ---
if (dropBtnPressed && canDrop && dropStationIndex !== -1) {
var station = stations[dropStationIndex];
// Drop all travelers currently on the bus
if (bus.travelersOnBoard && bus.travelersOnBoard.length > 0) {
// Calculate base Y for stacking
var baseY = 0;
var stackDir = 1;
// If station is above street, stack upwards; if below, stack downwards
var streetCenterY = streetBgY;
if (station.y < streetCenterY) {
baseY = -140;
stackDir = -1;
} else {
baseY = 140;
stackDir = 1;
}
// Count how many travelers are already at this drop station (for stacking)
var alreadyDropped = 0;
for (var tt = 0; tt < station.travelers.length; tt++) {
if (station.travelers[tt].dropped) alreadyDropped++;
}
// Count how many men and women are on board
var menToDrop = 0;
var womenToDrop = 0;
for (var t = 0; t < bus.travelersOnBoard.length; t++) {
var travelerObj = bus.travelersOnBoard[t];
var traveler = travelerObj.traveler;
if (traveler.gender === "woman") {
womenToDrop++;
} else {
menToDrop++;
}
}
// Drop correct number of men and women, stacking them
var dropIndex = 0;
for (var t = 0; t < menToDrop; t++, dropIndex++) {
var newTraveler = new Traveler();
newTraveler.gender = "man";
// Remove default asset and attach correct one
if (newTraveler.children.length > 0) {
newTraveler.removeChild(newTraveler.children[0]);
}
newTraveler.attachAsset('traveler', {
anchorX: 0.5,
anchorY: 1
});
newTraveler.x = bus.x - station.x;
newTraveler.y = bus.y - station.y - 100;
newTraveler.alpha = 0;
newTraveler.waiting = false;
newTraveler.dropped = true;
newTraveler.station = station;
// Stack vertically at the station
var stackIndex = alreadyDropped + dropIndex;
newTraveler.x = 0;
newTraveler.y = baseY + stackDir * stackIndex * 130;
newTraveler.alpha = 0;
station.addChild(newTraveler);
station.travelers.push(newTraveler);
tween(newTraveler, {
x: 0,
y: baseY + stackDir * stackIndex * 130,
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
// Award score for each traveler dropped
score += moneyPerTraveler;
scoreTxt.setText(score);
}
for (var t = 0; t < womenToDrop; t++, dropIndex++) {
var newTraveler = new Traveler();
newTraveler.gender = "woman";
// Remove default asset and attach correct one
if (newTraveler.children.length > 0) {
newTraveler.removeChild(newTraveler.children[0]);
}
newTraveler.attachAsset('travelerWoman', {
anchorX: 0.5,
anchorY: 1
});
newTraveler.x = bus.x - station.x;
newTraveler.y = bus.y - station.y - 100;
newTraveler.alpha = 0;
newTraveler.waiting = false;
newTraveler.dropped = true;
newTraveler.station = station;
// Stack vertically at the station
var stackIndex = alreadyDropped + dropIndex;
newTraveler.x = 0;
newTraveler.y = baseY + stackDir * stackIndex * 130;
newTraveler.alpha = 0;
station.addChild(newTraveler);
station.travelers.push(newTraveler);
tween(newTraveler, {
x: 0,
y: baseY + stackDir * stackIndex * 130,
alpha: 1
}, {
duration: 400,
easing: tween.easeOut
});
// Award score for each traveler dropped
score += moneyPerTraveler;
scoreTxt.setText(score);
}
// Clear bus
bus.travelersOnBoard = [];
// Reset lastTravelersOnBoardCount for plus bar
bus.lastTravelersOnBoardCount = 0;
// Mark this station as the last drop
bus.lastDropStationIndex = dropStationIndex;
// Play cheer sound when travelers are dropped off
if (soundEnabled) {
LK.getSound('cheer').play();
}
}
dropBtnPressed = false; // Reset button press
}
};