/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Customer = Container.expand(function () {
var self = Container.call(this);
var customerSprite = self.attachAsset('customer', {
anchorX: 0.5,
anchorY: 1.0
});
self.walkSpeed = 2;
self.targetX = 0;
self.targetY = 0;
self.hasReachedStand = false;
self.hasPurchased = false;
self.isInQueue = false;
self.queueIndex = -1; // Index in the customerQueue array
self.orderStartTime = 0;
self.orderDuration = 5000; // 5 seconds base order time
// Create loading circle for order progress
self.loadingCircle = new Container();
var loadingBg = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
loadingBg.tint = 0x333333;
self.loadingCircle.addChild(loadingBg);
var loadingFill = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
loadingFill.tint = 0x00FF00;
loadingFill.alpha = 0;
self.loadingCircle.addChild(loadingFill);
self.loadingCircle.x = 0;
self.loadingCircle.y = -160;
self.loadingCircle.alpha = 0;
self.addChild(self.loadingCircle);
self.loadingFill = loadingFill;
// Initialize idle animation properties
self.idleTimer = 0;
self.baseY = 0;
self.isIdle = false;
self.update = function () {
// Only move if we have valid target coordinates
var distanceX = Math.abs(self.x - self.targetX);
var distanceY = Math.abs(self.y - self.targetY);
if (distanceX > 5 || distanceY > 5) {
// Moving - stop idle animation
self.isIdle = false;
// Move towards target
if (self.x < self.targetX) {
self.x += self.walkSpeed * gameSpeedMultiplier;
} else if (self.x > self.targetX) {
self.x -= self.walkSpeed * gameSpeedMultiplier;
}
if (self.y < self.targetY) {
self.y += self.walkSpeed * gameSpeedMultiplier;
} else if (self.y > self.targetY) {
self.y -= self.walkSpeed * gameSpeedMultiplier;
}
} else {
// Reached target position
if (!self.isInQueue && !self.hasPurchased) {
// Join the queue
joinQueue(self);
} else if (self.isInQueue && self.queueIndex === 0 && !self.hasReachedStand && !self.hasPurchased) {
// First in queue, ready for manual sale
self.hasReachedStand = true;
}
// Start idle animation when stationary
if (!self.isIdle) {
self.isIdle = true;
self.baseY = self.y;
self.idleTimer = 0;
}
// Gentle idle bobbing animation
self.idleTimer += 0.05;
self.y = self.baseY + Math.sin(self.idleTimer) * 3;
}
};
self.leaveStand = function () {
var exitX = self.x < 1024 ? -100 : 2148;
tween(self, {
x: exitX
}, {
duration: 2000,
onFinish: function onFinish() {
self.destroy();
var index = customers.indexOf(self);
if (index > -1) {
customers.splice(index, 1);
}
}
});
};
return self;
});
var LemonadeGlass = Container.expand(function () {
var self = Container.call(this);
var glass = self.attachAsset('lemonadeGlass', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveToCustomer = function (targetX, targetY, callback) {
tween(self, {
x: targetX,
y: targetY
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: callback
});
};
return self;
});
var LemonadeStand = Container.expand(function () {
var self = Container.call(this);
// Always start with normal lemonade stand initially
var standBase = self.attachAsset('lemonadeStand', {
anchorX: 0.5,
anchorY: 1.0
});
self.standBase = standBase; // Store reference for upgrades
// Add menu table
var menuTable = self.attachAsset('menuTable', {
anchorX: 0.5,
anchorY: 0.5,
x: 150,
y: -50
});
// Add price sign on menu table with bubble font style
var signText = new Text2('$1.00', {
size: 28,
fill: 0x00FF00,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowDistance: 2
});
signText.anchor.set(0.5, 0.5);
signText.x = 150;
signText.y = -50;
self.addChild(signText);
self.updateSign = function (price) {
signText.setText('$' + price.toFixed(2));
};
self.updateStandAsset = function () {
// Remove old stand asset
if (self.standBase) {
self.removeChild(self.standBase);
}
// Add new upgraded stand asset
var assetName = standLevel > 1 ? 'upgradedstand' : 'lemonadeStand';
self.standBase = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0,
x: standLevel > 1 ? -20 : 0,
y: standLevel > 1 ? -20 : 0
});
// Hide/show lemonade glass on stand based on upgrade level
for (var l = 0; l < lemonadeGlasses.length; l++) {
if (lemonadeGlasses[l] && lemonadeGlasses[l].parent === game) {
// Check if this is the stand lemonade glass (positioned on stand)
if (Math.abs(lemonadeGlasses[l].x - lemonadeStand.x) < 50 && Math.abs(lemonadeGlasses[l].y - (lemonadeStand.y - 160)) < 50) {
lemonadeGlasses[l].alpha = standLevel > 1 ? 0 : 1;
}
}
}
// Ensure menu table and sign stay on top of upgraded stand
if (standLevel > 1) {
self.addChild(menuTable);
self.addChild(signText); // Bring price sign to front
}
};
return self;
});
var UpgradeButton = Container.expand(function (upgradeData) {
var self = Container.call(this);
var button = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.4,
scaleY: 1.4
});
var titleText = new Text2(upgradeData.title, {
size: 32,
fill: 0xFFFFFF,
font: "'Comic Sans MS', 'Chalkboard SE', cursive"
});
titleText.anchor.set(0.5, 0.3);
titleText.x = 0;
titleText.y = -15;
self.addChild(titleText);
var costText = new Text2('$' + upgradeData.cost, {
size: 72,
fill: 0xFF8C00,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 3,
dropShadowDistance: 2
});
costText.anchor.set(0.5, 0.0);
costText.x = 0;
costText.y = 60;
self.addChild(costText);
self.upgradeData = upgradeData;
self.updateCost = function () {
costText.setText('$' + self.upgradeData.cost.toFixed(0));
// Update price color based on affordability
if (money >= self.upgradeData.cost) {
costText.tint = 0x00FF00; // Green when affordable
} else {
costText.tint = 0xFF0000; // Red when unaffordable
}
// Remove tinting to show original button image colors
button.tint = 0xFFFFFF; // Reset to original colors
};
self.down = function (x, y, obj) {
if (money >= self.upgradeData.cost) {
money -= self.upgradeData.cost;
self.upgradeData.apply();
self.upgradeData.cost *= 1.5;
self.updateCost();
if (!isSoundMuted) {
LK.getSound('upgrade').play();
}
// No animation - keep same size
} else {
// Play button click sound even when can't afford
if (!isSoundMuted) {
LK.getSound('buttonClick').play();
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var money = 0;
var lemonadeCount = 0; // No longer stackable
var salesCount = 0;
var productionRate = storage.productionRate || 2000; // milliseconds per lemonade, starting at 2 seconds
var currentPrice = 1.00;
var customerSpawnRate = storage.customerSpawnRate || 4000;
var standLevel = storage.standLevel || 1;
var customerCapacity = storage.customerCapacity || 3; // Starting customer capacity is 3
// Mute state variables
var isMusicMuted = storage.isMusicMuted || false;
var isSoundMuted = storage.isSoundMuted || false;
// Game arrays
var customers = [];
var lemonadeGlasses = [];
// Speed boost variables
var gameSpeedMultiplier = 1.0;
var boostEndTime = 0;
// Queue system variables
var queuePositions = [{
x: 924,
y: 1710
},
// Front of queue (closest to stand)
{
x: 824,
y: 1710
},
// Second position
{
x: 724,
y: 1710
},
// Third position
{
x: 624,
y: 1710
},
// Fourth position
{
x: 524,
y: 1710
} // Fifth position
];
var customerQueue = []; // Ordered array of customers in queue
var maxQueueSize = customerCapacity; // Maximum customers allowed in queue based on capacity
// Production tracking for customers per minute
var lastSaleTime = Date.now();
var salesHistory = [];
var currentProductionRate = 0;
// Create background environment
// Sky background
game.setBackgroundColor(0x87CEEB);
// Add clouds with random positions
var clouds = [];
for (var c = 0; c < 6; c++) {
var cloud = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloud.x = Math.random() * 2500 - 200;
cloud.y = 250 + Math.random() * 300;
clouds.push(cloud);
game.addChild(cloud);
}
// Add sun to sky
var sun = LK.getAsset('sun', {
anchorX: 0.5,
anchorY: 0.5
});
sun.x = 1700;
sun.y = 300;
game.addChild(sun);
// Start cloud animations with 5x slower speed
function animateCloud(cloud) {
var randomSpeed = (15000 + Math.random() * 10000) * 5; // 5x slower
tween(cloud, {
x: cloud.x + 2500
}, {
duration: randomSpeed,
easing: tween.linear,
onFinish: function onFinish() {
cloud.x = -250;
cloud.y = 250 + Math.random() * 300;
animateCloud(cloud);
}
});
}
for (var c = 0; c < clouds.length; c++) {
animateCloud(clouds[c]);
}
// Add houses in background
var house1 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house1.x = 200;
house1.y = 1450;
game.addChild(house1);
var house2 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house2.x = 600;
house2.y = 1450;
game.addChild(house2);
var house3 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house3.x = 1400;
house3.y = 1450;
game.addChild(house3);
var house4 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house4.x = 1800;
house4.y = 1450;
game.addChild(house4);
// Add grass pattern
var grassPattern = [];
for (var g = 0; g < 5; g++) {
var garden = LK.getAsset('garden', {
anchorX: 0.0,
anchorY: 1.0
});
garden.x = g * 512;
garden.y = 1600;
grassPattern.push(garden);
game.addChild(garden);
}
// Add fences for each house (beginning and end with gaps)
// House 1 fences (x: 200)
var fence1a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence1a.x = 50;
fence1a.y = 1580;
game.addChild(fence1a);
var fence1b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence1b.x = 350;
fence1b.y = 1580;
game.addChild(fence1b);
// House 2 fences (x: 600)
var fence2a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence2a.x = 450;
fence2a.y = 1580;
game.addChild(fence2a);
var fence2b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence2b.x = 750;
fence2b.y = 1580;
game.addChild(fence2b);
// House 3 fences (x: 1400)
var fence3a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence3a.x = 1250;
fence3a.y = 1580;
game.addChild(fence3a);
var fence3b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence3b.x = 1550;
fence3b.y = 1580;
game.addChild(fence3b);
// House 4 fences (x: 1800)
var fence4a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence4a.x = 1650;
fence4a.y = 1580;
game.addChild(fence4a);
var fence4b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence4b.x = 1950;
fence4b.y = 1580;
game.addChild(fence4b);
// Add beige background section covering entire bottom area
var brownBackground = LK.getAsset('bottompart', {
anchorX: 0.5,
anchorY: 0.0
});
brownBackground.tint = 0xF5F5DC; // Beige color
brownBackground.x = 1024;
brownBackground.y = 2000; // Start below the asphalt road to keep it visible
// Add asphalt road
var asphalt = LK.getAsset('asphalt', {
anchorX: 0.5,
anchorY: 1.0
});
asphalt.x = 1024;
asphalt.y = 2000;
game.addChild(asphalt);
// Add sidewalk pattern
var sidewalkPattern = [];
for (var s = 0; s < 5; s++) {
var sidewalk = LK.getAsset('sidewalk', {
anchorX: 0.0,
anchorY: 1.0
});
sidewalk.x = s * 555;
sidewalk.y = 1710;
sidewalkPattern.push(sidewalk);
game.addChild(sidewalk);
}
// Create lemonade stand
var lemonadeStand = game.addChild(new LemonadeStand());
lemonadeStand.x = 1024;
lemonadeStand.y = 1700;
// Add manual selling variables
var isProcessingSale = false;
var saleProgressCircle = null;
// Add click handler for manual selling
lemonadeStand.down = function (x, y, obj) {
// Only process if there's a customer at front of queue and we're not already processing
if (customerQueue.length > 0 && !isProcessingSale && lemonadeCount > 0) {
var frontCustomer = customerQueue[0];
if (frontCustomer && frontCustomer.hasReachedStand && !frontCustomer.hasPurchased) {
startManualSale(frontCustomer);
} else {
// Play button click sound when clicking but can't sell
if (!isSoundMuted) {
LK.getSound('buttonClick').play();
}
}
} else {
// Play button click sound when clicking but can't sell
if (!isSoundMuted) {
LK.getSound('buttonClick').play();
}
}
};
// Create HUD elements
// Create modern HUD container with background
var hudContainer = new Container();
hudContainer.x = 120;
hudContainer.y = 40;
LK.gui.topLeft.addChild(hudContainer);
// HUD background panel removed to clean up top corner appearance
var moneyText = new Text2('💰 $' + money.toFixed(2), {
size: 52,
fill: 0xFFD700,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
stroke: 0x000000,
strokeThickness: 4
});
moneyText.anchor.set(0, 0);
moneyText.x = 20;
moneyText.y = 15;
hudContainer.addChild(moneyText);
var lemonadeText = new Text2('🏆 ' + salesCount + ' Sales', {
size: 52,
fill: 0x00FF88,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
stroke: 0x000000,
strokeThickness: 4
});
lemonadeText.anchor.set(0, 0);
lemonadeText.x = 20;
lemonadeText.y = 80;
hudContainer.addChild(lemonadeText);
var customerRateText = new Text2('⚡ 0/min served', {
size: 42,
fill: 0xFF6B35,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
stroke: 0x000000,
strokeThickness: 3
});
customerRateText.anchor.set(0, 0);
customerRateText.x = 20;
customerRateText.y = 145;
hudContainer.addChild(customerRateText);
// Create upgrade menu
var upgradeMenu = new Container();
upgradeMenu.x = 1024;
upgradeMenu.y = 2400; // Move up 100 pixels from 2500 to 2400
game.addChild(upgradeMenu);
// Upgrade definitions
var upgrades = [{
title: "Faster Production",
cost: 5,
apply: function apply() {
productionRate *= 0.8; // Make production 20% faster each upgrade
updateHUD();
}
}, {
title: "Better Recipe",
cost: 15,
apply: function apply() {
currentPrice += 0.3;
lemonadeStand.updateSign(currentPrice);
}
}, {
title: "Marketing",
cost: 30,
apply: function apply() {
customerCapacity += 1; // Increase customer capacity by 1
maxQueueSize = customerCapacity; // Update max queue size
customerSpawnRate *= 0.75;
}
}, {
title: "Stand Upgrade",
cost: 1000,
apply: function apply() {
standLevel++;
// Double the profit (price) and production speed
currentPrice *= 2;
productionRate *= 0.5; // Half the time = double the speed
// Update stand visual asset
lemonadeStand.updateStandAsset();
lemonadeStand.updateSign(currentPrice);
// Ensure customers stay on top layer after stand upgrade
for (var c = 0; c < customers.length; c++) {
if (customers[c]) {
game.addChild(customers[c]); // Brings customers to front
}
}
}
}];
// Create upgrade buttons
var upgradeButtons = [];
for (var i = 0; i < upgrades.length; i++) {
var upgradeButton = new UpgradeButton(upgrades[i]);
upgradeButton.x = (i - 1.5) * 425; // Button width (300 * 1.4 = 420) + 5 pixel padding = 425
upgradeButton.y = 0; // Keep buttons aligned horizontally
upgradeMenu.addChild(upgradeButton);
upgradeButtons.push(upgradeButton);
}
// Timers
var lastProductionTime = Date.now();
var lastCustomerTime = Date.now();
// We'll handle timing manually in update loop for speed boost
// Auto-save timer
var saveTimer = LK.setInterval(function () {
saveGame();
}, 5000);
function updateHUD() {
moneyText.setText('💰 $' + money.toFixed(2));
lemonadeText.setText('🏆 ' + salesCount + ' Sales');
customerRateText.setText('⚡ ' + calculateCustomerRate() + '/min served');
// Update upgrade buttons
for (var i = 0; i < upgradeMenu.children.length; i++) {
upgradeMenu.children[i].updateCost();
}
}
function joinQueue(customer) {
// Check if customer is already in queue to prevent duplicates
if (customerQueue.indexOf(customer) !== -1) {
return;
}
// Only add if there's an available position and under max limit
if (customerQueue.length < maxQueueSize && customerQueue.length < queuePositions.length) {
customerQueue.push(customer);
customer.isInQueue = true;
customer.queueIndex = customerQueue.length - 1;
// Assign specific position immediately
var positionIndex = customerQueue.length - 1;
customer.targetX = queuePositions[positionIndex].x;
customer.targetY = queuePositions[positionIndex].y;
}
}
function leaveQueue(customer) {
var index = customerQueue.indexOf(customer);
if (index !== -1) {
customerQueue.splice(index, 1);
customer.isInQueue = false;
customer.queueIndex = -1;
// Update queue positions for remaining customers
updateQueuePositions();
}
}
function updateQueuePositions() {
// Reassign positions based on current queue order
for (var i = 0; i < customerQueue.length; i++) {
var customer = customerQueue[i];
customer.queueIndex = i;
if (i < queuePositions.length) {
customer.targetX = queuePositions[i].x;
customer.targetY = queuePositions[i].y;
}
}
}
function calculateCustomerRate() {
if (salesHistory.length < 2) {
return 0;
}
var timeSpan = salesHistory[salesHistory.length - 1] - salesHistory[0];
var salesInSpan = salesHistory.length;
if (timeSpan > 0) {
return (salesInSpan / (timeSpan / 60000)).toFixed(1);
}
return 0;
}
function spawnCustomer() {
// Don't spawn if we already have the maximum number of customers on screen
if (customers.length >= customerCapacity) {
return;
}
// Only spawn one customer at a time
var customer = new Customer();
var startSide = Math.random() < 0.5 ? 0 : 1;
if (startSide === 0) {
customer.x = -100;
customer.targetX = queuePositions[0].x - 100;
} else {
customer.x = 2148;
customer.targetX = queuePositions[0].x + 100;
}
customer.y = 1710;
customer.targetY = 1710;
customers.push(customer);
game.addChild(customer);
}
function startManualSale(customer) {
isProcessingSale = true;
// Create progress circle
saleProgressCircle = new Container();
// Background circle
var progressBg = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
progressBg.tint = 0x333333;
progressBg.alpha = 0.8;
saleProgressCircle.addChild(progressBg);
// Progress fill circle
var progressFill = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
progressFill.tint = 0x00FF00;
saleProgressCircle.addChild(progressFill);
// Position above the stand
saleProgressCircle.x = lemonadeStand.x;
saleProgressCircle.y = lemonadeStand.y - 300;
game.addChild(saleProgressCircle);
// Animate the progress fill over 2 seconds
tween(progressFill, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
completeManualSale(customer);
}
});
}
function completeManualSale(customer) {
// Complete the customer's purchase
if (customer && !customer.hasPurchased && customer.queueIndex === 0) {
customer.hasPurchased = true;
customer.hasReachedStand = false;
lemonadeCount = 0; // Used up the ready lemonade
salesCount++;
money += currentPrice;
// Track sale time for production rate calculation
salesHistory.push(Date.now());
if (salesHistory.length > 10) {
salesHistory.shift();
}
if (!isSoundMuted) {
LK.getSound('cashRegister').play();
}
// Create lemonade glass for customer
var lemonadeGlass = new LemonadeGlass();
lemonadeGlass.x = lemonadeStand.x;
lemonadeGlass.y = lemonadeStand.y - 50;
game.addChild(lemonadeGlass);
lemonadeGlasses.push(lemonadeGlass);
// Move lemonade to customer's hand position
lemonadeGlass.moveToCustomer(customer.x, customer.y - 80, function () {
// Keep lemonade with customer while they leave
customer.addChild(lemonadeGlass);
lemonadeGlass.x = 20; // Offset from customer center
lemonadeGlass.y = -80; // Above customer
});
// Show coin animation
var coin = new Container();
var coinSprite = coin.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
coin.x = customer.x;
coin.y = customer.y - 100;
game.addChild(coin);
tween(coin, {
y: coin.y - 100,
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
coin.destroy();
}
});
// Remove from queue and advance other customers
leaveQueue(customer);
// Customer leaves after purchase
LK.setTimeout(function () {
customer.leaveStand();
}, 1000);
}
// Clean up progress circle
if (saleProgressCircle) {
saleProgressCircle.destroy();
saleProgressCircle = null;
}
isProcessingSale = false;
}
function saveGame() {
storage.money = money;
storage.salesCount = salesCount;
storage.productionRate = productionRate;
storage.currentPrice = currentPrice;
storage.customerSpawnRate = customerSpawnRate;
storage.standLevel = standLevel;
storage.customerCapacity = customerCapacity;
storage.isMusicMuted = isMusicMuted;
storage.isSoundMuted = isSoundMuted;
}
// Game update loop
game.update = function () {
// Check if speed boost should end
if (Date.now() > boostEndTime) {
gameSpeedMultiplier = 1.0;
}
var currentTime = Date.now();
// Handle production timer with speed boost - always have lemonade ready
if (currentTime - lastProductionTime >= productionRate / gameSpeedMultiplier) {
lemonadeCount = 1; // Always have one ready
lastProductionTime = currentTime;
}
// Handle customer spawn timer with speed boost
if (currentTime - lastCustomerTime >= customerSpawnRate / gameSpeedMultiplier) {
spawnCustomer();
lastCustomerTime = currentTime;
}
// Update customers with speed boost
for (var i = customers.length - 1; i >= 0; i--) {
if (customers[i] && customers[i].update) {
// Apply speed boost to customer walk speed
customers[i].walkSpeed = 2 * gameSpeedMultiplier;
customers[i].update();
}
}
// Update HUD every 30 frames (0.5 seconds)
if (LK.ticks % 30 === 0) {
updateHUD();
}
// Ensure lemonade glasses are always on top layer for visibility
for (var l = 0; l < lemonadeGlasses.length; l++) {
if (lemonadeGlasses[l] && lemonadeGlasses[l].parent === game) {
game.addChild(lemonadeGlasses[l]); // Brings to front
}
}
// Ensure customers are always on top layer for visibility
for (var c = 0; c < customers.length; c++) {
if (customers[c] && customers[c].parent === game) {
game.addChild(customers[c]); // Brings customers to front
}
}
// Ensure lemonade stand price sign stays on absolute top layer
if (lemonadeStand && lemonadeStand.children) {
for (var s = 0; s < lemonadeStand.children.length; s++) {
if (lemonadeStand.children[s] instanceof Text2) {
lemonadeStand.addChild(lemonadeStand.children[s]); // Bring price text to front
break;
}
}
}
};
// Add brown background to front layer to ensure it covers all blue areas
game.addChild(brownBackground);
// Fences are already added earlier and should stay behind upgradedstand
// No need to bring them to front layer
// Bring upgrade menu to front layer
game.addChild(upgradeMenu);
// Add one lemonade glass on lemonade stand on top layer
var standLemonadeGlass = new LemonadeGlass();
standLemonadeGlass.x = lemonadeStand.x;
standLemonadeGlass.y = lemonadeStand.y - 160; // Position on top of stand, moved 10 pixels up
game.addChild(standLemonadeGlass);
lemonadeGlasses.push(standLemonadeGlass);
// Initial setup
lemonadeStand.updateSign(currentPrice);
updateHUD();
// Create music mute button
var musicMuteButton = LK.getAsset('musicMuteButton', {
anchorX: 0.5,
anchorY: 0.5
});
musicMuteButton.x = -100;
musicMuteButton.y = 70;
LK.gui.topRight.addChild(musicMuteButton);
// Add music note symbol
var musicNoteText = new Text2('♪', {
size: 36,
fill: 0xFFFFFF,
font: "'Arial', sans-serif"
});
musicNoteText.anchor.set(0.5, 0.5);
musicNoteText.x = -100;
musicNoteText.y = 70;
LK.gui.topRight.addChild(musicNoteText);
// Update music button appearance based on mute state
function updateMusicButton() {
if (isMusicMuted) {
musicMuteButton.alpha = 0.5;
musicNoteText.alpha = 0.5;
} else {
musicMuteButton.alpha = 1.0;
musicNoteText.alpha = 1.0;
}
}
// Music mute button click handler
musicMuteButton.down = function (x, y, obj) {
isMusicMuted = !isMusicMuted;
updateMusicButton();
if (isMusicMuted) {
LK.stopMusic();
} else {
LK.playMusic('backgroundMusic', {
volume: 0.3
});
}
// Save mute state
storage.isMusicMuted = isMusicMuted;
};
// Create sound mute button
var soundMuteButton = LK.getAsset('soundMuteButton', {
anchorX: 0.5,
anchorY: 0.5
});
soundMuteButton.x = -100;
soundMuteButton.y = 140;
LK.gui.topRight.addChild(soundMuteButton);
// Add sound symbol
var soundText = new Text2('♫', {
size: 36,
fill: 0xFFFFFF,
font: "'Arial', sans-serif"
});
soundText.anchor.set(0.5, 0.5);
soundText.x = -100;
soundText.y = 140;
LK.gui.topRight.addChild(soundText);
// Update sound button appearance based on mute state
function updateSoundButton() {
if (isSoundMuted) {
soundMuteButton.alpha = 0.5;
soundText.alpha = 0.5;
} else {
soundMuteButton.alpha = 1.0;
soundText.alpha = 1.0;
}
}
// Sound mute button click handler
soundMuteButton.down = function (x, y, obj) {
isSoundMuted = !isSoundMuted;
updateSoundButton();
// Save mute state
storage.isSoundMuted = isSoundMuted;
};
// Initialize button states
updateMusicButton();
updateSoundButton();
// Start background music
if (!isMusicMuted) {
LK.playMusic('backgroundMusic', {
volume: 0.3
});
} /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Customer = Container.expand(function () {
var self = Container.call(this);
var customerSprite = self.attachAsset('customer', {
anchorX: 0.5,
anchorY: 1.0
});
self.walkSpeed = 2;
self.targetX = 0;
self.targetY = 0;
self.hasReachedStand = false;
self.hasPurchased = false;
self.isInQueue = false;
self.queueIndex = -1; // Index in the customerQueue array
self.orderStartTime = 0;
self.orderDuration = 5000; // 5 seconds base order time
// Create loading circle for order progress
self.loadingCircle = new Container();
var loadingBg = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
loadingBg.tint = 0x333333;
self.loadingCircle.addChild(loadingBg);
var loadingFill = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
loadingFill.tint = 0x00FF00;
loadingFill.alpha = 0;
self.loadingCircle.addChild(loadingFill);
self.loadingCircle.x = 0;
self.loadingCircle.y = -160;
self.loadingCircle.alpha = 0;
self.addChild(self.loadingCircle);
self.loadingFill = loadingFill;
// Initialize idle animation properties
self.idleTimer = 0;
self.baseY = 0;
self.isIdle = false;
self.update = function () {
// Only move if we have valid target coordinates
var distanceX = Math.abs(self.x - self.targetX);
var distanceY = Math.abs(self.y - self.targetY);
if (distanceX > 5 || distanceY > 5) {
// Moving - stop idle animation
self.isIdle = false;
// Move towards target
if (self.x < self.targetX) {
self.x += self.walkSpeed * gameSpeedMultiplier;
} else if (self.x > self.targetX) {
self.x -= self.walkSpeed * gameSpeedMultiplier;
}
if (self.y < self.targetY) {
self.y += self.walkSpeed * gameSpeedMultiplier;
} else if (self.y > self.targetY) {
self.y -= self.walkSpeed * gameSpeedMultiplier;
}
} else {
// Reached target position
if (!self.isInQueue && !self.hasPurchased) {
// Join the queue
joinQueue(self);
} else if (self.isInQueue && self.queueIndex === 0 && !self.hasReachedStand && !self.hasPurchased) {
// First in queue, ready for manual sale
self.hasReachedStand = true;
}
// Start idle animation when stationary
if (!self.isIdle) {
self.isIdle = true;
self.baseY = self.y;
self.idleTimer = 0;
}
// Gentle idle bobbing animation
self.idleTimer += 0.05;
self.y = self.baseY + Math.sin(self.idleTimer) * 3;
}
};
self.leaveStand = function () {
var exitX = self.x < 1024 ? -100 : 2148;
tween(self, {
x: exitX
}, {
duration: 2000,
onFinish: function onFinish() {
self.destroy();
var index = customers.indexOf(self);
if (index > -1) {
customers.splice(index, 1);
}
}
});
};
return self;
});
var LemonadeGlass = Container.expand(function () {
var self = Container.call(this);
var glass = self.attachAsset('lemonadeGlass', {
anchorX: 0.5,
anchorY: 0.5
});
self.moveToCustomer = function (targetX, targetY, callback) {
tween(self, {
x: targetX,
y: targetY
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: callback
});
};
return self;
});
var LemonadeStand = Container.expand(function () {
var self = Container.call(this);
// Always start with normal lemonade stand initially
var standBase = self.attachAsset('lemonadeStand', {
anchorX: 0.5,
anchorY: 1.0
});
self.standBase = standBase; // Store reference for upgrades
// Add menu table
var menuTable = self.attachAsset('menuTable', {
anchorX: 0.5,
anchorY: 0.5,
x: 150,
y: -50
});
// Add price sign on menu table with bubble font style
var signText = new Text2('$1.00', {
size: 28,
fill: 0x00FF00,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 4,
dropShadowDistance: 2
});
signText.anchor.set(0.5, 0.5);
signText.x = 150;
signText.y = -50;
self.addChild(signText);
self.updateSign = function (price) {
signText.setText('$' + price.toFixed(2));
};
self.updateStandAsset = function () {
// Remove old stand asset
if (self.standBase) {
self.removeChild(self.standBase);
}
// Add new upgraded stand asset
var assetName = standLevel > 1 ? 'upgradedstand' : 'lemonadeStand';
self.standBase = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 1.0,
x: standLevel > 1 ? -20 : 0,
y: standLevel > 1 ? -20 : 0
});
// Hide/show lemonade glass on stand based on upgrade level
for (var l = 0; l < lemonadeGlasses.length; l++) {
if (lemonadeGlasses[l] && lemonadeGlasses[l].parent === game) {
// Check if this is the stand lemonade glass (positioned on stand)
if (Math.abs(lemonadeGlasses[l].x - lemonadeStand.x) < 50 && Math.abs(lemonadeGlasses[l].y - (lemonadeStand.y - 160)) < 50) {
lemonadeGlasses[l].alpha = standLevel > 1 ? 0 : 1;
}
}
}
// Ensure menu table and sign stay on top of upgraded stand
if (standLevel > 1) {
self.addChild(menuTable);
self.addChild(signText); // Bring price sign to front
}
};
return self;
});
var UpgradeButton = Container.expand(function (upgradeData) {
var self = Container.call(this);
var button = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.4,
scaleY: 1.4
});
var titleText = new Text2(upgradeData.title, {
size: 32,
fill: 0xFFFFFF,
font: "'Comic Sans MS', 'Chalkboard SE', cursive"
});
titleText.anchor.set(0.5, 0.3);
titleText.x = 0;
titleText.y = -15;
self.addChild(titleText);
var costText = new Text2('$' + upgradeData.cost, {
size: 72,
fill: 0xFF8C00,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
dropShadow: true,
dropShadowColor: 0x000000,
dropShadowBlur: 3,
dropShadowDistance: 2
});
costText.anchor.set(0.5, 0.0);
costText.x = 0;
costText.y = 60;
self.addChild(costText);
self.upgradeData = upgradeData;
self.updateCost = function () {
costText.setText('$' + self.upgradeData.cost.toFixed(0));
// Update price color based on affordability
if (money >= self.upgradeData.cost) {
costText.tint = 0x00FF00; // Green when affordable
} else {
costText.tint = 0xFF0000; // Red when unaffordable
}
// Remove tinting to show original button image colors
button.tint = 0xFFFFFF; // Reset to original colors
};
self.down = function (x, y, obj) {
if (money >= self.upgradeData.cost) {
money -= self.upgradeData.cost;
self.upgradeData.apply();
self.upgradeData.cost *= 1.5;
self.updateCost();
if (!isSoundMuted) {
LK.getSound('upgrade').play();
}
// No animation - keep same size
} else {
// Play button click sound even when can't afford
if (!isSoundMuted) {
LK.getSound('buttonClick').play();
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
// Game variables
var money = 0;
var lemonadeCount = 0; // No longer stackable
var salesCount = 0;
var productionRate = storage.productionRate || 2000; // milliseconds per lemonade, starting at 2 seconds
var currentPrice = 1.00;
var customerSpawnRate = storage.customerSpawnRate || 4000;
var standLevel = storage.standLevel || 1;
var customerCapacity = storage.customerCapacity || 3; // Starting customer capacity is 3
// Mute state variables
var isMusicMuted = storage.isMusicMuted || false;
var isSoundMuted = storage.isSoundMuted || false;
// Game arrays
var customers = [];
var lemonadeGlasses = [];
// Speed boost variables
var gameSpeedMultiplier = 1.0;
var boostEndTime = 0;
// Queue system variables
var queuePositions = [{
x: 924,
y: 1710
},
// Front of queue (closest to stand)
{
x: 824,
y: 1710
},
// Second position
{
x: 724,
y: 1710
},
// Third position
{
x: 624,
y: 1710
},
// Fourth position
{
x: 524,
y: 1710
} // Fifth position
];
var customerQueue = []; // Ordered array of customers in queue
var maxQueueSize = customerCapacity; // Maximum customers allowed in queue based on capacity
// Production tracking for customers per minute
var lastSaleTime = Date.now();
var salesHistory = [];
var currentProductionRate = 0;
// Create background environment
// Sky background
game.setBackgroundColor(0x87CEEB);
// Add clouds with random positions
var clouds = [];
for (var c = 0; c < 6; c++) {
var cloud = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5
});
cloud.x = Math.random() * 2500 - 200;
cloud.y = 250 + Math.random() * 300;
clouds.push(cloud);
game.addChild(cloud);
}
// Add sun to sky
var sun = LK.getAsset('sun', {
anchorX: 0.5,
anchorY: 0.5
});
sun.x = 1700;
sun.y = 300;
game.addChild(sun);
// Start cloud animations with 5x slower speed
function animateCloud(cloud) {
var randomSpeed = (15000 + Math.random() * 10000) * 5; // 5x slower
tween(cloud, {
x: cloud.x + 2500
}, {
duration: randomSpeed,
easing: tween.linear,
onFinish: function onFinish() {
cloud.x = -250;
cloud.y = 250 + Math.random() * 300;
animateCloud(cloud);
}
});
}
for (var c = 0; c < clouds.length; c++) {
animateCloud(clouds[c]);
}
// Add houses in background
var house1 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house1.x = 200;
house1.y = 1450;
game.addChild(house1);
var house2 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house2.x = 600;
house2.y = 1450;
game.addChild(house2);
var house3 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house3.x = 1400;
house3.y = 1450;
game.addChild(house3);
var house4 = LK.getAsset('house', {
anchorX: 0.5,
anchorY: 1.0
});
house4.x = 1800;
house4.y = 1450;
game.addChild(house4);
// Add grass pattern
var grassPattern = [];
for (var g = 0; g < 5; g++) {
var garden = LK.getAsset('garden', {
anchorX: 0.0,
anchorY: 1.0
});
garden.x = g * 512;
garden.y = 1600;
grassPattern.push(garden);
game.addChild(garden);
}
// Add fences for each house (beginning and end with gaps)
// House 1 fences (x: 200)
var fence1a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence1a.x = 50;
fence1a.y = 1580;
game.addChild(fence1a);
var fence1b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence1b.x = 350;
fence1b.y = 1580;
game.addChild(fence1b);
// House 2 fences (x: 600)
var fence2a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence2a.x = 450;
fence2a.y = 1580;
game.addChild(fence2a);
var fence2b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence2b.x = 750;
fence2b.y = 1580;
game.addChild(fence2b);
// House 3 fences (x: 1400)
var fence3a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence3a.x = 1250;
fence3a.y = 1580;
game.addChild(fence3a);
var fence3b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence3b.x = 1550;
fence3b.y = 1580;
game.addChild(fence3b);
// House 4 fences (x: 1800)
var fence4a = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence4a.x = 1650;
fence4a.y = 1580;
game.addChild(fence4a);
var fence4b = LK.getAsset('fence', {
anchorX: 0.5,
anchorY: 1.0
});
fence4b.x = 1950;
fence4b.y = 1580;
game.addChild(fence4b);
// Add beige background section covering entire bottom area
var brownBackground = LK.getAsset('bottompart', {
anchorX: 0.5,
anchorY: 0.0
});
brownBackground.tint = 0xF5F5DC; // Beige color
brownBackground.x = 1024;
brownBackground.y = 2000; // Start below the asphalt road to keep it visible
// Add asphalt road
var asphalt = LK.getAsset('asphalt', {
anchorX: 0.5,
anchorY: 1.0
});
asphalt.x = 1024;
asphalt.y = 2000;
game.addChild(asphalt);
// Add sidewalk pattern
var sidewalkPattern = [];
for (var s = 0; s < 5; s++) {
var sidewalk = LK.getAsset('sidewalk', {
anchorX: 0.0,
anchorY: 1.0
});
sidewalk.x = s * 555;
sidewalk.y = 1710;
sidewalkPattern.push(sidewalk);
game.addChild(sidewalk);
}
// Create lemonade stand
var lemonadeStand = game.addChild(new LemonadeStand());
lemonadeStand.x = 1024;
lemonadeStand.y = 1700;
// Add manual selling variables
var isProcessingSale = false;
var saleProgressCircle = null;
// Add click handler for manual selling
lemonadeStand.down = function (x, y, obj) {
// Only process if there's a customer at front of queue and we're not already processing
if (customerQueue.length > 0 && !isProcessingSale && lemonadeCount > 0) {
var frontCustomer = customerQueue[0];
if (frontCustomer && frontCustomer.hasReachedStand && !frontCustomer.hasPurchased) {
startManualSale(frontCustomer);
} else {
// Play button click sound when clicking but can't sell
if (!isSoundMuted) {
LK.getSound('buttonClick').play();
}
}
} else {
// Play button click sound when clicking but can't sell
if (!isSoundMuted) {
LK.getSound('buttonClick').play();
}
}
};
// Create HUD elements
// Create modern HUD container with background
var hudContainer = new Container();
hudContainer.x = 120;
hudContainer.y = 40;
LK.gui.topLeft.addChild(hudContainer);
// HUD background panel removed to clean up top corner appearance
var moneyText = new Text2('💰 $' + money.toFixed(2), {
size: 52,
fill: 0xFFD700,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
stroke: 0x000000,
strokeThickness: 4
});
moneyText.anchor.set(0, 0);
moneyText.x = 20;
moneyText.y = 15;
hudContainer.addChild(moneyText);
var lemonadeText = new Text2('🏆 ' + salesCount + ' Sales', {
size: 52,
fill: 0x00FF88,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
stroke: 0x000000,
strokeThickness: 4
});
lemonadeText.anchor.set(0, 0);
lemonadeText.x = 20;
lemonadeText.y = 80;
hudContainer.addChild(lemonadeText);
var customerRateText = new Text2('⚡ 0/min served', {
size: 42,
fill: 0xFF6B35,
font: "'Comic Sans MS', 'Chalkboard SE', cursive",
stroke: 0x000000,
strokeThickness: 3
});
customerRateText.anchor.set(0, 0);
customerRateText.x = 20;
customerRateText.y = 145;
hudContainer.addChild(customerRateText);
// Create upgrade menu
var upgradeMenu = new Container();
upgradeMenu.x = 1024;
upgradeMenu.y = 2400; // Move up 100 pixels from 2500 to 2400
game.addChild(upgradeMenu);
// Upgrade definitions
var upgrades = [{
title: "Faster Production",
cost: 5,
apply: function apply() {
productionRate *= 0.8; // Make production 20% faster each upgrade
updateHUD();
}
}, {
title: "Better Recipe",
cost: 15,
apply: function apply() {
currentPrice += 0.3;
lemonadeStand.updateSign(currentPrice);
}
}, {
title: "Marketing",
cost: 30,
apply: function apply() {
customerCapacity += 1; // Increase customer capacity by 1
maxQueueSize = customerCapacity; // Update max queue size
customerSpawnRate *= 0.75;
}
}, {
title: "Stand Upgrade",
cost: 1000,
apply: function apply() {
standLevel++;
// Double the profit (price) and production speed
currentPrice *= 2;
productionRate *= 0.5; // Half the time = double the speed
// Update stand visual asset
lemonadeStand.updateStandAsset();
lemonadeStand.updateSign(currentPrice);
// Ensure customers stay on top layer after stand upgrade
for (var c = 0; c < customers.length; c++) {
if (customers[c]) {
game.addChild(customers[c]); // Brings customers to front
}
}
}
}];
// Create upgrade buttons
var upgradeButtons = [];
for (var i = 0; i < upgrades.length; i++) {
var upgradeButton = new UpgradeButton(upgrades[i]);
upgradeButton.x = (i - 1.5) * 425; // Button width (300 * 1.4 = 420) + 5 pixel padding = 425
upgradeButton.y = 0; // Keep buttons aligned horizontally
upgradeMenu.addChild(upgradeButton);
upgradeButtons.push(upgradeButton);
}
// Timers
var lastProductionTime = Date.now();
var lastCustomerTime = Date.now();
// We'll handle timing manually in update loop for speed boost
// Auto-save timer
var saveTimer = LK.setInterval(function () {
saveGame();
}, 5000);
function updateHUD() {
moneyText.setText('💰 $' + money.toFixed(2));
lemonadeText.setText('🏆 ' + salesCount + ' Sales');
customerRateText.setText('⚡ ' + calculateCustomerRate() + '/min served');
// Update upgrade buttons
for (var i = 0; i < upgradeMenu.children.length; i++) {
upgradeMenu.children[i].updateCost();
}
}
function joinQueue(customer) {
// Check if customer is already in queue to prevent duplicates
if (customerQueue.indexOf(customer) !== -1) {
return;
}
// Only add if there's an available position and under max limit
if (customerQueue.length < maxQueueSize && customerQueue.length < queuePositions.length) {
customerQueue.push(customer);
customer.isInQueue = true;
customer.queueIndex = customerQueue.length - 1;
// Assign specific position immediately
var positionIndex = customerQueue.length - 1;
customer.targetX = queuePositions[positionIndex].x;
customer.targetY = queuePositions[positionIndex].y;
}
}
function leaveQueue(customer) {
var index = customerQueue.indexOf(customer);
if (index !== -1) {
customerQueue.splice(index, 1);
customer.isInQueue = false;
customer.queueIndex = -1;
// Update queue positions for remaining customers
updateQueuePositions();
}
}
function updateQueuePositions() {
// Reassign positions based on current queue order
for (var i = 0; i < customerQueue.length; i++) {
var customer = customerQueue[i];
customer.queueIndex = i;
if (i < queuePositions.length) {
customer.targetX = queuePositions[i].x;
customer.targetY = queuePositions[i].y;
}
}
}
function calculateCustomerRate() {
if (salesHistory.length < 2) {
return 0;
}
var timeSpan = salesHistory[salesHistory.length - 1] - salesHistory[0];
var salesInSpan = salesHistory.length;
if (timeSpan > 0) {
return (salesInSpan / (timeSpan / 60000)).toFixed(1);
}
return 0;
}
function spawnCustomer() {
// Don't spawn if we already have the maximum number of customers on screen
if (customers.length >= customerCapacity) {
return;
}
// Only spawn one customer at a time
var customer = new Customer();
var startSide = Math.random() < 0.5 ? 0 : 1;
if (startSide === 0) {
customer.x = -100;
customer.targetX = queuePositions[0].x - 100;
} else {
customer.x = 2148;
customer.targetX = queuePositions[0].x + 100;
}
customer.y = 1710;
customer.targetY = 1710;
customers.push(customer);
game.addChild(customer);
}
function startManualSale(customer) {
isProcessingSale = true;
// Create progress circle
saleProgressCircle = new Container();
// Background circle
var progressBg = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.0,
scaleY: 2.0
});
progressBg.tint = 0x333333;
progressBg.alpha = 0.8;
saleProgressCircle.addChild(progressBg);
// Progress fill circle
var progressFill = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1
});
progressFill.tint = 0x00FF00;
saleProgressCircle.addChild(progressFill);
// Position above the stand
saleProgressCircle.x = lemonadeStand.x;
saleProgressCircle.y = lemonadeStand.y - 300;
game.addChild(saleProgressCircle);
// Animate the progress fill over 2 seconds
tween(progressFill, {
scaleX: 1.8,
scaleY: 1.8
}, {
duration: 2000,
easing: tween.linear,
onFinish: function onFinish() {
completeManualSale(customer);
}
});
}
function completeManualSale(customer) {
// Complete the customer's purchase
if (customer && !customer.hasPurchased && customer.queueIndex === 0) {
customer.hasPurchased = true;
customer.hasReachedStand = false;
lemonadeCount = 0; // Used up the ready lemonade
salesCount++;
money += currentPrice;
// Track sale time for production rate calculation
salesHistory.push(Date.now());
if (salesHistory.length > 10) {
salesHistory.shift();
}
if (!isSoundMuted) {
LK.getSound('cashRegister').play();
}
// Create lemonade glass for customer
var lemonadeGlass = new LemonadeGlass();
lemonadeGlass.x = lemonadeStand.x;
lemonadeGlass.y = lemonadeStand.y - 50;
game.addChild(lemonadeGlass);
lemonadeGlasses.push(lemonadeGlass);
// Move lemonade to customer's hand position
lemonadeGlass.moveToCustomer(customer.x, customer.y - 80, function () {
// Keep lemonade with customer while they leave
customer.addChild(lemonadeGlass);
lemonadeGlass.x = 20; // Offset from customer center
lemonadeGlass.y = -80; // Above customer
});
// Show coin animation
var coin = new Container();
var coinSprite = coin.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
coin.x = customer.x;
coin.y = customer.y - 100;
game.addChild(coin);
tween(coin, {
y: coin.y - 100,
alpha: 0
}, {
duration: 1500,
onFinish: function onFinish() {
coin.destroy();
}
});
// Remove from queue and advance other customers
leaveQueue(customer);
// Customer leaves after purchase
LK.setTimeout(function () {
customer.leaveStand();
}, 1000);
}
// Clean up progress circle
if (saleProgressCircle) {
saleProgressCircle.destroy();
saleProgressCircle = null;
}
isProcessingSale = false;
}
function saveGame() {
storage.money = money;
storage.salesCount = salesCount;
storage.productionRate = productionRate;
storage.currentPrice = currentPrice;
storage.customerSpawnRate = customerSpawnRate;
storage.standLevel = standLevel;
storage.customerCapacity = customerCapacity;
storage.isMusicMuted = isMusicMuted;
storage.isSoundMuted = isSoundMuted;
}
// Game update loop
game.update = function () {
// Check if speed boost should end
if (Date.now() > boostEndTime) {
gameSpeedMultiplier = 1.0;
}
var currentTime = Date.now();
// Handle production timer with speed boost - always have lemonade ready
if (currentTime - lastProductionTime >= productionRate / gameSpeedMultiplier) {
lemonadeCount = 1; // Always have one ready
lastProductionTime = currentTime;
}
// Handle customer spawn timer with speed boost
if (currentTime - lastCustomerTime >= customerSpawnRate / gameSpeedMultiplier) {
spawnCustomer();
lastCustomerTime = currentTime;
}
// Update customers with speed boost
for (var i = customers.length - 1; i >= 0; i--) {
if (customers[i] && customers[i].update) {
// Apply speed boost to customer walk speed
customers[i].walkSpeed = 2 * gameSpeedMultiplier;
customers[i].update();
}
}
// Update HUD every 30 frames (0.5 seconds)
if (LK.ticks % 30 === 0) {
updateHUD();
}
// Ensure lemonade glasses are always on top layer for visibility
for (var l = 0; l < lemonadeGlasses.length; l++) {
if (lemonadeGlasses[l] && lemonadeGlasses[l].parent === game) {
game.addChild(lemonadeGlasses[l]); // Brings to front
}
}
// Ensure customers are always on top layer for visibility
for (var c = 0; c < customers.length; c++) {
if (customers[c] && customers[c].parent === game) {
game.addChild(customers[c]); // Brings customers to front
}
}
// Ensure lemonade stand price sign stays on absolute top layer
if (lemonadeStand && lemonadeStand.children) {
for (var s = 0; s < lemonadeStand.children.length; s++) {
if (lemonadeStand.children[s] instanceof Text2) {
lemonadeStand.addChild(lemonadeStand.children[s]); // Bring price text to front
break;
}
}
}
};
// Add brown background to front layer to ensure it covers all blue areas
game.addChild(brownBackground);
// Fences are already added earlier and should stay behind upgradedstand
// No need to bring them to front layer
// Bring upgrade menu to front layer
game.addChild(upgradeMenu);
// Add one lemonade glass on lemonade stand on top layer
var standLemonadeGlass = new LemonadeGlass();
standLemonadeGlass.x = lemonadeStand.x;
standLemonadeGlass.y = lemonadeStand.y - 160; // Position on top of stand, moved 10 pixels up
game.addChild(standLemonadeGlass);
lemonadeGlasses.push(standLemonadeGlass);
// Initial setup
lemonadeStand.updateSign(currentPrice);
updateHUD();
// Create music mute button
var musicMuteButton = LK.getAsset('musicMuteButton', {
anchorX: 0.5,
anchorY: 0.5
});
musicMuteButton.x = -100;
musicMuteButton.y = 70;
LK.gui.topRight.addChild(musicMuteButton);
// Add music note symbol
var musicNoteText = new Text2('♪', {
size: 36,
fill: 0xFFFFFF,
font: "'Arial', sans-serif"
});
musicNoteText.anchor.set(0.5, 0.5);
musicNoteText.x = -100;
musicNoteText.y = 70;
LK.gui.topRight.addChild(musicNoteText);
// Update music button appearance based on mute state
function updateMusicButton() {
if (isMusicMuted) {
musicMuteButton.alpha = 0.5;
musicNoteText.alpha = 0.5;
} else {
musicMuteButton.alpha = 1.0;
musicNoteText.alpha = 1.0;
}
}
// Music mute button click handler
musicMuteButton.down = function (x, y, obj) {
isMusicMuted = !isMusicMuted;
updateMusicButton();
if (isMusicMuted) {
LK.stopMusic();
} else {
LK.playMusic('backgroundMusic', {
volume: 0.3
});
}
// Save mute state
storage.isMusicMuted = isMusicMuted;
};
// Create sound mute button
var soundMuteButton = LK.getAsset('soundMuteButton', {
anchorX: 0.5,
anchorY: 0.5
});
soundMuteButton.x = -100;
soundMuteButton.y = 140;
LK.gui.topRight.addChild(soundMuteButton);
// Add sound symbol
var soundText = new Text2('♫', {
size: 36,
fill: 0xFFFFFF,
font: "'Arial', sans-serif"
});
soundText.anchor.set(0.5, 0.5);
soundText.x = -100;
soundText.y = 140;
LK.gui.topRight.addChild(soundText);
// Update sound button appearance based on mute state
function updateSoundButton() {
if (isSoundMuted) {
soundMuteButton.alpha = 0.5;
soundText.alpha = 0.5;
} else {
soundMuteButton.alpha = 1.0;
soundText.alpha = 1.0;
}
}
// Sound mute button click handler
soundMuteButton.down = function (x, y, obj) {
isSoundMuted = !isSoundMuted;
updateSoundButton();
// Save mute state
storage.isSoundMuted = isSoundMuted;
};
// Initialize button states
updateMusicButton();
updateSoundButton();
// Start background music
if (!isMusicMuted) {
LK.playMusic('backgroundMusic', {
volume: 0.3
});
}
Standart coin. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
clear
Lemonade glass. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Lemonade stand. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Cloud. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Grassy garden. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Asphalt. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Tiny house. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
rounded fund button, no text. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Wooden table menu. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Sidewalk stones. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
White garden fence. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Mobile game style bottom menu background with borders vector. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
White fill Lemonade truck with tires, with lemonade stand space on behind, door, windows, white color fill. . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
Sun. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat