/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
money: 0,
mowerLevel: 1,
bagCapacity: 10,
cutArea: 1
});
/****
* Classes
****/
var EmptyBagButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44aa44
});
self.buttonText = new Text2("Empty Bag", {
size: 40,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
self.down = function (x, y, obj) {
if (currentBag > 0) {
currentBag = 0;
LK.getSound('emptyBag').play();
updateUI();
}
};
return self;
});
var GrassTile = Container.expand(function () {
var self = Container.call(this);
self.isGrowing = false;
self.isCut = false;
self.growthTime = 0;
self.grassType = null; // Will be set on creation
self.maxGrowthTime = 300;
self.bonus = 0;
self.bagMultiplier = 1;
self.speedMultiplier = 1;
self.visibility = 1;
self.icon = "๐ฑ";
self.tooltip = null;
// Set grass type (must be called after creation)
self.setGrassType = function (typeObj) {
self.grassType = typeObj;
self.maxGrowthTime = typeObj.regrow;
self.bonus = typeObj.bonus || 0;
self.bagMultiplier = typeObj.bagMultiplier || 1;
self.speedMultiplier = typeObj.speedMultiplier || 1;
self.visibility = typeObj.visibility || 1;
self.icon = typeObj.icon || "๐ฑ";
grassGraphics.tint = typeObj.color;
grassGraphics.alpha = 1 * self.visibility;
};
var grassGraphics = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 0.5
});
// Tooltip for grass type
self.showTooltip = function () {
if (self.tooltip) return;
var txt = new Text2(self.icon + " " + (self.grassType ? self.grassType.name : "Grass"), {
size: 32,
fill: "#fff",
stroke: "#222",
strokeThickness: 4
});
txt.anchor.set(0.5, 1);
txt.x = 0;
txt.y = -tileSize / 2 - 8;
self.tooltip = txt;
self.addChild(txt);
};
self.hideTooltip = function () {
if (self.tooltip) {
self.removeChild(self.tooltip);
self.tooltip = null;
}
};
self.cut = function () {
if (!self.isCut) {
self.isCut = true;
self.isGrowing = false;
self.growthTime = 0;
grassGraphics.alpha = 0.3 * self.visibility;
return true;
}
return false;
};
self.startGrowing = function () {
if (self.isCut && !self.isGrowing) {
self.isGrowing = true;
}
};
self.update = function () {
if (self.isGrowing) {
self.growthTime++;
if (self.growthTime >= self.maxGrowthTime) {
self.isGrowing = false;
self.isCut = false;
grassGraphics.alpha = 1 * self.visibility;
}
}
};
// Show tooltip on hover/touch
self.move = function (x, y, obj) {
// Only show tooltip if pointer is close to center
var dx = self.x - x;
var dy = self.y - y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < tileSize / 2) {
self.showTooltip();
} else {
self.hideTooltip();
}
};
self.down = function (x, y, obj) {
if (!self.isCut && currentBag < bagCapacity) {
var success = self.cut();
if (success) {
// Use bagMultiplier for this grass type
currentBag += self.bagMultiplier;
// Bonus money for harder grass
money += moneyPerCut + (self.bonus || 0);
LK.getSound(self.grassType && self.grassType.sound ? self.grassType.sound : 'mow').play();
updateUI();
// Start growing after a short delay
LK.setTimeout(function () {
self.startGrowing();
}, 2000);
}
}
};
return self;
});
// ShopButton: Opens the shop menu
var ShopButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0x3366cc
});
self.buttonText = new Text2("Shop", {
size: 48,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
self.down = function (x, y, obj) {
openShopMenu();
};
return self;
});
// ShopMenu: Displays shop items and handles purchases
var ShopMenu = Container.expand(function () {
var self = Container.call(this);
self.bg = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 14,
// Increased from 10 to 14
scaleY: 14,
// Increased from 10 to 14
x: 0,
y: 0,
tint: 0x222244
});
self.bg.alpha = 0.95;
self.title = new Text2("Mower Shop", {
size: 100,
// Increased from 80 to 100 for better visibility
fill: "#fff"
});
self.title.anchor.set(0.5, 0);
self.title.x = 0;
self.title.y = -700; // Move title up to match larger panel
self.addChild(self.title);
// Shop items will be dynamically created
self.itemButtons = [];
// Close button - move to top right of the shop menu
var closeBtn = self.attachAsset('upgradeButton', {
anchorX: 1,
// right edge
anchorY: 0,
// top edge
scaleX: 0.9,
// Slightly larger close button
scaleY: 0.9,
x: 950,
// Move further right to match larger panel
y: -700,
// Move up to match larger panel
tint: 0x444444
});
var closeText = new Text2("Close", {
size: 56,
//{1r} // Larger text for close button
fill: 0xffffff
});
closeText.anchor.set(1, 0); // right/top
closeText.x = 950;
closeText.y = -700;
self.addChild(closeText);
closeBtn.interactive = true;
closeBtn.down = function () {
self.visible = false;
self.parent.removeChild(self);
};
self.addShopItems = function (items) {
// Remove old buttons
for (var i = 0; i < self.itemButtons.length; i++) {
self.removeChild(self.itemButtons[i]);
}
self.itemButtons = [];
// Layout items vertically
for (var i = 0; i < items.length; i++) {
var item = items[i];
var btn = new Container();
var btnBg = btn.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
y: 0,
tint: item.owned ? 0x888888 : 0x2288ff
});
var txt = new Text2(item.name + (item.owned ? " (Owned)" : " - $" + item.price), {
size: 40,
fill: item.owned ? "#cccccc" : "#ffffff"
});
txt.anchor.set(0.5, 0.5);
btn.addChild(txt);
btn.x = 0;
btn.y = -300 + i * 180;
btn.interactive = true;
(function (item, btn, btnBg, txt) {
btn.down = function () {
if (item.owned) {
// If consumable, allow to buy again
if (item.type === "consumable" && money >= item.price) {
money -= item.price;
item.onBuy();
updateUI();
btnBg.tint = 0x888888;
txt.setText(item.name + " (Owned)");
LK.effects.flashObject(btn, 0x00ff00, 400);
}
return;
}
if (money >= item.price) {
money -= item.price;
item.owned = true;
item.onBuy();
updateUI();
btnBg.tint = 0x888888;
txt.setText(item.name + " (Owned)");
LK.effects.flashObject(btn, 0x00ff00, 400);
LK.getSound('upgrade').play();
} else {
LK.effects.flashObject(btn, 0xff0000, 400);
}
};
})(item, btn, btnBg, txt);
self.addChild(btn);
self.itemButtons.push(btn);
}
};
return self;
});
var UpgradeButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.buttonText = new Text2("Upgrade Mower: $100", {
size: 40,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
self.price = 100;
self.enabled = true;
self.updatePrice = function (price) {
self.price = price;
self.buttonText.setText("Upgrade Mower: $" + price);
};
self.setEnabled = function (enabled) {
self.enabled = enabled;
if (enabled) {
buttonGraphics.alpha = 1;
} else {
buttonGraphics.alpha = 0.5;
}
};
self.down = function (x, y, obj) {
if (self.enabled && money >= self.price) {
money -= self.price;
mowerLevel += 1;
// Increase mower capabilities based on level
bagCapacity = 10 + (mowerLevel - 1) * 5;
cutArea = 1 + Math.floor((mowerLevel - 1) / 2);
moneyPerCut = 1 + Math.floor((mowerLevel - 1) / 3);
// Store progress
storage.money = money;
storage.mowerLevel = mowerLevel;
storage.bagCapacity = bagCapacity;
storage.cutArea = cutArea;
// Increase price for next upgrade
self.price = Math.floor(self.price * 1.5);
self.updatePrice(self.price);
LK.getSound('upgrade').play();
updateUI();
// Update mower appearance
updateMower();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb
});
/****
* Game Code
****/
game.setBackgroundColor(0x87ceeb);
// Game variables
var gridSize = 15; // Increased from 12 to 15 for more plots
var tileSize = 80; // Reduced tile size further to fit more plots
var gridSpacing = 8; // Decreased spacing further to fit more tiles
var grassTiles = [];
var money = storage.money || 0;
var mowerLevel = storage.mowerLevel || 1;
var bagCapacity = storage.bagCapacity || 10;
var currentBag = 0;
var cutArea = storage.cutArea || 1;
var moneyPerCut = 1 + Math.floor((mowerLevel - 1) / 3);
// Grass type definitions and unlock logic
var grassTypes = [{
id: "kentucky",
name: "Kentucky Bluegrass",
color: 0x228811,
image: "grass",
sound: "mow",
regrow: 300,
density: 1,
bagMultiplier: 1,
speedMultiplier: 1,
visibility: 1,
bonus: 0,
unlockLevel: 1,
icon: "๐ฑ"
}, {
id: "bermuda",
name: "Bermuda Grass",
color: 0x3a9d23,
image: "grass",
sound: "mow",
regrow: 320,
density: 1.5,
bagMultiplier: 1,
speedMultiplier: 0.7,
visibility: 1,
bonus: 2,
unlockLevel: 2,
icon: "๐พ"
}, {
id: "zoysia",
name: "Zoysia",
color: 0x7bb661,
image: "grass",
sound: "mow",
regrow: 120,
density: 1,
bagMultiplier: 1,
speedMultiplier: 1,
visibility: 1,
bonus: 3,
unlockLevel: 3,
icon: "๐"
}, {
id: "ryegrass",
name: "Ryegrass",
color: 0x6e8b3d,
image: "grass",
sound: "mow",
regrow: 300,
density: 1,
bagMultiplier: 2,
speedMultiplier: 1,
visibility: 1,
bonus: 4,
unlockLevel: 4,
icon: "๐ฟ"
}, {
id: "fescue",
name: "Fescue",
color: 0x4e5d2e,
image: "grass",
sound: "mow",
regrow: 300,
density: 1,
bagMultiplier: 1,
speedMultiplier: 1,
visibility: 0.5,
bonus: 6,
unlockLevel: 5,
icon: "๐"
}];
// Returns the available grass types for the current mowerLevel
function getUnlockedGrassTypes() {
var unlocked = [];
for (var i = 0; i < grassTypes.length; i++) {
if (mowerLevel >= grassTypes[i].unlockLevel) {
unlocked.push(grassTypes[i]);
}
}
return unlocked;
}
// Set up UI elements
var moneyText = new Text2("$" + money, {
size: 120,
fill: 0xFFFFFF
});
moneyText.anchor.set(0.5, 1);
// Position moneyText above the lawn plots, centered horizontally
// Calculate total width and height of the grid
var totalGridWidth = gridSize * tileSize + (gridSize - 1) * gridSpacing;
var startX = (2048 - totalGridWidth) / 2;
var centerX = 2048 / 2;
var startY = Math.max((2732 - (gridSize * tileSize + (gridSize - 1) * gridSpacing)) / 2, 250);
moneyText.x = centerX;
moneyText.y = startY - 40;
game.addChild(moneyText);
// Add ShopButton to bottom right corner
var shopButton = new ShopButton();
shopButton.x = -180; // Offset from right edge (button is centered on anchor)
shopButton.y = -180; // Offset from bottom edge (button is centered on anchor)
LK.gui.bottomRight.addChild(shopButton);
// --- Shop logic ---
var shopItems = [{
id: "collector",
name: "Grass Collector",
price: 150,
owned: storage.collectorOwned || false,
unlockLevel: 1,
type: "attachment",
onBuy: function onBuy() {
collectorEquipped = true;
storage.collectorOwned = true;
bagCapacity += 10;
updateUI();
LK.effects.flashObject(bagContainer, 0x44ff44, 600);
}
}, {
id: "turbo",
name: "Turbo Booster",
price: 120,
owned: false,
unlockLevel: 2,
type: "consumable",
onBuy: function onBuy() {
turboActive = true;
turboTicks = 300;
LK.effects.flashObject(mower, 0x00ffff, 600);
}
}, {
id: "skin1",
name: "Red Flame Skin",
price: 80,
owned: storage.skin1Owned || false,
unlockLevel: 1,
type: "skin",
onBuy: function onBuy() {
mowerSkin = 1;
storage.skin1Owned = true;
updateMower();
LK.effects.flashObject(mower, 0xff2222, 600);
}
}, {
id: "mulcher",
name: "Mulching Attachment",
price: 200,
owned: storage.mulcherOwned || false,
unlockLevel: 3,
type: "attachment",
onBuy: function onBuy() {
mulcherEquipped = true;
storage.mulcherOwned = true;
LK.effects.flashObject(mower, 0x22ff22, 600);
}
}, {
id: "magnet",
name: "Magnet Power-Up",
price: 100,
owned: false,
unlockLevel: 2,
type: "consumable",
onBuy: function onBuy() {
magnetActive = true;
magnetTicks = 360;
LK.effects.flashObject(mower, 0x8888ff, 600);
}
}, {
id: "pet",
name: "Robotic Pet Helper",
price: 300,
owned: storage.petOwned || false,
unlockLevel: 4,
type: "attachment",
onBuy: function onBuy() {
petEquipped = true;
storage.petOwned = true;
LK.effects.flashObject(mower, 0xffff00, 600);
}
}];
// Track equipped/active powerups
var collectorEquipped = storage.collectorOwned || false;
var turboActive = false;
var turboTicks = 0;
var mowerSkin = 0;
var mulcherEquipped = storage.mulcherOwned || false;
var magnetActive = false;
var magnetTicks = 0;
var petEquipped = storage.petOwned || false;
// Open shop menu
function openShopMenu() {
// Only show items unlocked by mowerLevel
var unlocked = [];
for (var i = 0; i < shopItems.length; i++) {
if (mowerLevel >= shopItems[i].unlockLevel) {
unlocked.push(shopItems[i]);
}
}
var shopMenu = new ShopMenu();
shopMenu.x = 2048 / 2;
shopMenu.y = 2732 / 2;
shopMenu.addShopItems(unlocked);
game.addChild(shopMenu);
}
var bagContainer = new Container();
var bagBackground = bagContainer.attachAsset('bagIndicator', {
anchorX: 0,
anchorY: 0.5
});
var bagFillBar = bagContainer.attachAsset('bagFill', {
anchorX: 0,
anchorY: 0.5,
x: 2,
y: 0
});
var bagText = new Text2("Bag: 0/" + bagCapacity, {
size: 30,
fill: 0x000000
});
bagText.anchor.set(0.5, 0.5);
bagText.x = 100;
bagContainer.addChild(bagText);
bagContainer.x = 20;
bagContainer.y = 100;
LK.gui.top.addChild(bagContainer);
// Mower graphic (shows cursor position)
var mower = game.attachAsset('mower', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 150
});
// Create upgrade button
var upgradeButton = new UpgradeButton();
// Position at bottom center
upgradeButton.x = 2048 / 2;
upgradeButton.y = 2732 - 180;
upgradeButton.updatePrice(Math.floor(100 * Math.pow(1.5, mowerLevel - 1)));
game.addChild(upgradeButton);
// Create empty bag button
var emptyBagButton = new EmptyBagButton();
emptyBagButton.x = 180;
emptyBagButton.y = 2732 - 180;
game.addChild(emptyBagButton);
// Level text
var levelText = new Text2("Mower Level: " + mowerLevel, {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
levelText.x = 20;
levelText.y = 20;
LK.gui.top.addChild(levelText);
// Create grass grid
function createGrassGrid() {
// Calculate total width and height of the grid
var totalGridWidth = gridSize * tileSize + (gridSize - 1) * gridSpacing;
var totalGridHeight = gridSize * tileSize + (gridSize - 1) * gridSpacing;
// Calculate starting coordinates to center the grid on screen
var startX = (2048 - totalGridWidth) / 2;
var startY = (2732 - totalGridHeight) / 2;
// Make sure the grid is vertically centered with some space for UI
// Adjust vertical position to ensure grid is visible and centered between UI elements
startY = Math.max(startY, 250);
// Ensure grid doesn't go too low
startY = Math.min(startY, 2732 - totalGridHeight - 250);
var unlockedTypes = getUnlockedGrassTypes();
var zoneRows = Math.ceil(gridSize / unlockedTypes.length);
for (var row = 0; row < gridSize; row++) {
for (var col = 0; col < gridSize; col++) {
var grassTile = new GrassTile();
grassTile.x = startX + col * (tileSize + gridSpacing) + tileSize / 2;
grassTile.y = startY + row * (tileSize + gridSpacing) + tileSize / 2;
// Assign grass type by zone (horizontal bands)
var typeIdx = Math.floor(row / zoneRows);
if (typeIdx >= unlockedTypes.length) typeIdx = unlockedTypes.length - 1;
grassTile.setGrassType(unlockedTypes[typeIdx]);
game.addChild(grassTile);
grassTiles.push(grassTile);
}
}
}
function updateUI() {
moneyText.setText("$" + money);
// If collector equipped, show bonus capacity
bagText.setText("Bag: " + currentBag + "/" + bagCapacity + (collectorEquipped ? " (Collector)" : ""));
levelText.setText("Mower Level: " + mowerLevel);
// Update bag fill bar
var fillPercent = currentBag / bagCapacity;
bagFillBar.width = Math.max(0, Math.min(196 * fillPercent, 196));
// Update upgrade button state
upgradeButton.setEnabled(money >= upgradeButton.price);
}
function updateMower() {
// Scale mower based on level to indicate its upgraded capabilities
var scale = 1 + (mowerLevel - 1) * 0.1;
tween(mower, {
scaleX: scale,
scaleY: scale
}, {
duration: 500,
easing: tween.easeOut
});
// Change mower color based on level or skin
var level = mowerLevel % 5;
var colors = [0xdd2222, 0xff6600, 0xffcc00, 0x22dd22, 0x2222dd];
if (mowerSkin === 1) {
tween(mower, {
tint: 0xff2222
}, {
duration: 500
});
} else {
tween(mower, {
tint: colors[level]
}, {
duration: 500
});
}
}
// Track mouse/touch position for mower movement
game.move = function (x, y) {
mower.x = x;
mower.y = y;
// Show tooltip for hovered grass tile
for (var i = 0; i < grassTiles.length; i++) {
grassTiles[i].move(x, y, null);
}
};
// Handle multi-cut based on mower level
game.down = function (x, y) {
if (currentBag >= bagCapacity) return;
// Find the closest grass tile to the click position
var closestTile = null;
var closestDist = Number.MAX_VALUE;
for (var i = 0; i < grassTiles.length; i++) {
var tile = grassTiles[i];
var dx = tile.x - x;
var dy = tile.y - y;
var dist = dx * dx + dy * dy;
if (dist < closestDist) {
closestDist = dist;
closestTile = tile;
}
}
// Only proceed if we're close enough to a tile
if (closestTile && closestDist < tileSize * tileSize / 2) {
// Find the index of the closest tile
var tileIndex = grassTiles.indexOf(closestTile);
if (tileIndex !== -1) {
var row = Math.floor(tileIndex / gridSize);
var col = tileIndex % gridSize;
// Cut the tile and adjacent tiles based on cutArea
for (var r = Math.max(0, row - cutArea + 1); r <= Math.min(gridSize - 1, row + cutArea - 1); r++) {
for (var c = Math.max(0, col - cutArea + 1); c <= Math.min(gridSize - 1, col + cutArea - 1); c++) {
var targetIndex = r * gridSize + c;
var targetTile = grassTiles[targetIndex];
if (targetTile && !targetTile.isCut && currentBag < bagCapacity) {
var success = targetTile.cut();
if (success) {
// Use bagMultiplier for this grass type
currentBag += targetTile.bagMultiplier;
// Bonus money for harder grass
money += moneyPerCut + (targetTile.bonus || 0);
// Play sound for grass type
LK.getSound(targetTile.grassType && targetTile.grassType.sound ? targetTile.grassType.sound : 'mow').play();
// Start growing after a short delay
(function (tile) {
LK.setTimeout(function () {
tile.startGrowing();
}, 2000 + Math.random() * 1000);
})(targetTile);
// Flash effect
LK.effects.flashObject(targetTile, 0xffffff, 300);
if (currentBag >= bagCapacity) {
break;
}
}
}
}
if (currentBag >= bagCapacity) {
break;
}
}
updateUI();
// Only play sound once per overall cut action
LK.getSound('mow').play();
}
}
};
game.update = function () {
// Power-up: Turbo Booster
if (turboActive) {
turboTicks--;
if (turboTicks % 10 === 0) {
LK.effects.flashObject(mower, 0x00ffff, 100);
}
if (turboTicks <= 0) {
turboActive = false;
}
}
// Power-up: Magnet
if (magnetActive) {
magnetTicks--;
// Pull in nearby grass clippings (simulate: auto-cut nearby uncut tiles)
for (var i = 0; i < grassTiles.length; i++) {
var tile = grassTiles[i];
if (!tile.isCut && currentBag < bagCapacity) {
var dx = tile.x - mower.x;
var dy = tile.y - mower.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 200) {
var success = tile.cut();
if (success) {
currentBag += tile.bagMultiplier;
money += moneyPerCut + (tile.bonus || 0);
LK.effects.flashObject(tile, 0x8888ff, 200);
LK.getSound(tile.grassType && tile.grassType.sound ? tile.grassType.sound : 'mow').play();
updateUI();
LK.setTimeout(function (t) {
t.startGrowing();
}, 2000, tile);
if (currentBag >= bagCapacity) break;
}
}
}
}
if (magnetTicks <= 0) {
magnetActive = false;
}
}
// Pet Helper: Collects stray clippings (simulate: auto-empty bag if full)
if (petEquipped && currentBag >= bagCapacity) {
currentBag = 0;
LK.effects.flashObject(bagContainer, 0xffff00, 400);
LK.getSound('emptyBag').play();
updateUI();
}
// Mulcher: Bonus money for perfect lawns (simulate: if all cut, bonus)
if (mulcherEquipped) {
var allCut = true;
for (var i = 0; i < grassTiles.length; i++) {
if (!grassTiles[i].isCut) {
allCut = false;
break;
}
}
if (allCut) {
money += 20;
LK.effects.flashObject(mower, 0x22ff22, 600);
updateUI();
// Prevent repeat bonus until regrowth
mulcherEquipped = false;
LK.setTimeout(function () {
mulcherEquipped = storage.mulcherOwned;
}, 3000);
}
}
// Update all grass tiles
for (var i = 0; i < grassTiles.length; i++) {
grassTiles[i].update();
}
};
// Initialize the game
createGrassGrid();
updateUI();
updateMower();
// Play background music
LK.playMusic('bgmusic', {
loop: true
}); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
money: 0,
mowerLevel: 1,
bagCapacity: 10,
cutArea: 1
});
/****
* Classes
****/
var EmptyBagButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x44aa44
});
self.buttonText = new Text2("Empty Bag", {
size: 40,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
self.down = function (x, y, obj) {
if (currentBag > 0) {
currentBag = 0;
LK.getSound('emptyBag').play();
updateUI();
}
};
return self;
});
var GrassTile = Container.expand(function () {
var self = Container.call(this);
self.isGrowing = false;
self.isCut = false;
self.growthTime = 0;
self.grassType = null; // Will be set on creation
self.maxGrowthTime = 300;
self.bonus = 0;
self.bagMultiplier = 1;
self.speedMultiplier = 1;
self.visibility = 1;
self.icon = "๐ฑ";
self.tooltip = null;
// Set grass type (must be called after creation)
self.setGrassType = function (typeObj) {
self.grassType = typeObj;
self.maxGrowthTime = typeObj.regrow;
self.bonus = typeObj.bonus || 0;
self.bagMultiplier = typeObj.bagMultiplier || 1;
self.speedMultiplier = typeObj.speedMultiplier || 1;
self.visibility = typeObj.visibility || 1;
self.icon = typeObj.icon || "๐ฑ";
grassGraphics.tint = typeObj.color;
grassGraphics.alpha = 1 * self.visibility;
};
var grassGraphics = self.attachAsset('grass', {
anchorX: 0.5,
anchorY: 0.5
});
// Tooltip for grass type
self.showTooltip = function () {
if (self.tooltip) return;
var txt = new Text2(self.icon + " " + (self.grassType ? self.grassType.name : "Grass"), {
size: 32,
fill: "#fff",
stroke: "#222",
strokeThickness: 4
});
txt.anchor.set(0.5, 1);
txt.x = 0;
txt.y = -tileSize / 2 - 8;
self.tooltip = txt;
self.addChild(txt);
};
self.hideTooltip = function () {
if (self.tooltip) {
self.removeChild(self.tooltip);
self.tooltip = null;
}
};
self.cut = function () {
if (!self.isCut) {
self.isCut = true;
self.isGrowing = false;
self.growthTime = 0;
grassGraphics.alpha = 0.3 * self.visibility;
return true;
}
return false;
};
self.startGrowing = function () {
if (self.isCut && !self.isGrowing) {
self.isGrowing = true;
}
};
self.update = function () {
if (self.isGrowing) {
self.growthTime++;
if (self.growthTime >= self.maxGrowthTime) {
self.isGrowing = false;
self.isCut = false;
grassGraphics.alpha = 1 * self.visibility;
}
}
};
// Show tooltip on hover/touch
self.move = function (x, y, obj) {
// Only show tooltip if pointer is close to center
var dx = self.x - x;
var dy = self.y - y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < tileSize / 2) {
self.showTooltip();
} else {
self.hideTooltip();
}
};
self.down = function (x, y, obj) {
if (!self.isCut && currentBag < bagCapacity) {
var success = self.cut();
if (success) {
// Use bagMultiplier for this grass type
currentBag += self.bagMultiplier;
// Bonus money for harder grass
money += moneyPerCut + (self.bonus || 0);
LK.getSound(self.grassType && self.grassType.sound ? self.grassType.sound : 'mow').play();
updateUI();
// Start growing after a short delay
LK.setTimeout(function () {
self.startGrowing();
}, 2000);
}
}
};
return self;
});
// ShopButton: Opens the shop menu
var ShopButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
tint: 0x3366cc
});
self.buttonText = new Text2("Shop", {
size: 48,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
self.down = function (x, y, obj) {
openShopMenu();
};
return self;
});
// ShopMenu: Displays shop items and handles purchases
var ShopMenu = Container.expand(function () {
var self = Container.call(this);
self.bg = self.attachAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 14,
// Increased from 10 to 14
scaleY: 14,
// Increased from 10 to 14
x: 0,
y: 0,
tint: 0x222244
});
self.bg.alpha = 0.95;
self.title = new Text2("Mower Shop", {
size: 100,
// Increased from 80 to 100 for better visibility
fill: "#fff"
});
self.title.anchor.set(0.5, 0);
self.title.x = 0;
self.title.y = -700; // Move title up to match larger panel
self.addChild(self.title);
// Shop items will be dynamically created
self.itemButtons = [];
// Close button - move to top right of the shop menu
var closeBtn = self.attachAsset('upgradeButton', {
anchorX: 1,
// right edge
anchorY: 0,
// top edge
scaleX: 0.9,
// Slightly larger close button
scaleY: 0.9,
x: 950,
// Move further right to match larger panel
y: -700,
// Move up to match larger panel
tint: 0x444444
});
var closeText = new Text2("Close", {
size: 56,
//{1r} // Larger text for close button
fill: 0xffffff
});
closeText.anchor.set(1, 0); // right/top
closeText.x = 950;
closeText.y = -700;
self.addChild(closeText);
closeBtn.interactive = true;
closeBtn.down = function () {
self.visible = false;
self.parent.removeChild(self);
};
self.addShopItems = function (items) {
// Remove old buttons
for (var i = 0; i < self.itemButtons.length; i++) {
self.removeChild(self.itemButtons[i]);
}
self.itemButtons = [];
// Layout items vertically
for (var i = 0; i < items.length; i++) {
var item = items[i];
var btn = new Container();
var btnBg = btn.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.1,
scaleY: 1.1,
y: 0,
tint: item.owned ? 0x888888 : 0x2288ff
});
var txt = new Text2(item.name + (item.owned ? " (Owned)" : " - $" + item.price), {
size: 40,
fill: item.owned ? "#cccccc" : "#ffffff"
});
txt.anchor.set(0.5, 0.5);
btn.addChild(txt);
btn.x = 0;
btn.y = -300 + i * 180;
btn.interactive = true;
(function (item, btn, btnBg, txt) {
btn.down = function () {
if (item.owned) {
// If consumable, allow to buy again
if (item.type === "consumable" && money >= item.price) {
money -= item.price;
item.onBuy();
updateUI();
btnBg.tint = 0x888888;
txt.setText(item.name + " (Owned)");
LK.effects.flashObject(btn, 0x00ff00, 400);
}
return;
}
if (money >= item.price) {
money -= item.price;
item.owned = true;
item.onBuy();
updateUI();
btnBg.tint = 0x888888;
txt.setText(item.name + " (Owned)");
LK.effects.flashObject(btn, 0x00ff00, 400);
LK.getSound('upgrade').play();
} else {
LK.effects.flashObject(btn, 0xff0000, 400);
}
};
})(item, btn, btnBg, txt);
self.addChild(btn);
self.itemButtons.push(btn);
}
};
return self;
});
var UpgradeButton = Container.expand(function () {
var self = Container.call(this);
var buttonGraphics = self.attachAsset('upgradeButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.buttonText = new Text2("Upgrade Mower: $100", {
size: 40,
fill: 0xFFFFFF
});
self.buttonText.anchor.set(0.5, 0.5);
self.addChild(self.buttonText);
self.price = 100;
self.enabled = true;
self.updatePrice = function (price) {
self.price = price;
self.buttonText.setText("Upgrade Mower: $" + price);
};
self.setEnabled = function (enabled) {
self.enabled = enabled;
if (enabled) {
buttonGraphics.alpha = 1;
} else {
buttonGraphics.alpha = 0.5;
}
};
self.down = function (x, y, obj) {
if (self.enabled && money >= self.price) {
money -= self.price;
mowerLevel += 1;
// Increase mower capabilities based on level
bagCapacity = 10 + (mowerLevel - 1) * 5;
cutArea = 1 + Math.floor((mowerLevel - 1) / 2);
moneyPerCut = 1 + Math.floor((mowerLevel - 1) / 3);
// Store progress
storage.money = money;
storage.mowerLevel = mowerLevel;
storage.bagCapacity = bagCapacity;
storage.cutArea = cutArea;
// Increase price for next upgrade
self.price = Math.floor(self.price * 1.5);
self.updatePrice(self.price);
LK.getSound('upgrade').play();
updateUI();
// Update mower appearance
updateMower();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87ceeb
});
/****
* Game Code
****/
game.setBackgroundColor(0x87ceeb);
// Game variables
var gridSize = 15; // Increased from 12 to 15 for more plots
var tileSize = 80; // Reduced tile size further to fit more plots
var gridSpacing = 8; // Decreased spacing further to fit more tiles
var grassTiles = [];
var money = storage.money || 0;
var mowerLevel = storage.mowerLevel || 1;
var bagCapacity = storage.bagCapacity || 10;
var currentBag = 0;
var cutArea = storage.cutArea || 1;
var moneyPerCut = 1 + Math.floor((mowerLevel - 1) / 3);
// Grass type definitions and unlock logic
var grassTypes = [{
id: "kentucky",
name: "Kentucky Bluegrass",
color: 0x228811,
image: "grass",
sound: "mow",
regrow: 300,
density: 1,
bagMultiplier: 1,
speedMultiplier: 1,
visibility: 1,
bonus: 0,
unlockLevel: 1,
icon: "๐ฑ"
}, {
id: "bermuda",
name: "Bermuda Grass",
color: 0x3a9d23,
image: "grass",
sound: "mow",
regrow: 320,
density: 1.5,
bagMultiplier: 1,
speedMultiplier: 0.7,
visibility: 1,
bonus: 2,
unlockLevel: 2,
icon: "๐พ"
}, {
id: "zoysia",
name: "Zoysia",
color: 0x7bb661,
image: "grass",
sound: "mow",
regrow: 120,
density: 1,
bagMultiplier: 1,
speedMultiplier: 1,
visibility: 1,
bonus: 3,
unlockLevel: 3,
icon: "๐"
}, {
id: "ryegrass",
name: "Ryegrass",
color: 0x6e8b3d,
image: "grass",
sound: "mow",
regrow: 300,
density: 1,
bagMultiplier: 2,
speedMultiplier: 1,
visibility: 1,
bonus: 4,
unlockLevel: 4,
icon: "๐ฟ"
}, {
id: "fescue",
name: "Fescue",
color: 0x4e5d2e,
image: "grass",
sound: "mow",
regrow: 300,
density: 1,
bagMultiplier: 1,
speedMultiplier: 1,
visibility: 0.5,
bonus: 6,
unlockLevel: 5,
icon: "๐"
}];
// Returns the available grass types for the current mowerLevel
function getUnlockedGrassTypes() {
var unlocked = [];
for (var i = 0; i < grassTypes.length; i++) {
if (mowerLevel >= grassTypes[i].unlockLevel) {
unlocked.push(grassTypes[i]);
}
}
return unlocked;
}
// Set up UI elements
var moneyText = new Text2("$" + money, {
size: 120,
fill: 0xFFFFFF
});
moneyText.anchor.set(0.5, 1);
// Position moneyText above the lawn plots, centered horizontally
// Calculate total width and height of the grid
var totalGridWidth = gridSize * tileSize + (gridSize - 1) * gridSpacing;
var startX = (2048 - totalGridWidth) / 2;
var centerX = 2048 / 2;
var startY = Math.max((2732 - (gridSize * tileSize + (gridSize - 1) * gridSpacing)) / 2, 250);
moneyText.x = centerX;
moneyText.y = startY - 40;
game.addChild(moneyText);
// Add ShopButton to bottom right corner
var shopButton = new ShopButton();
shopButton.x = -180; // Offset from right edge (button is centered on anchor)
shopButton.y = -180; // Offset from bottom edge (button is centered on anchor)
LK.gui.bottomRight.addChild(shopButton);
// --- Shop logic ---
var shopItems = [{
id: "collector",
name: "Grass Collector",
price: 150,
owned: storage.collectorOwned || false,
unlockLevel: 1,
type: "attachment",
onBuy: function onBuy() {
collectorEquipped = true;
storage.collectorOwned = true;
bagCapacity += 10;
updateUI();
LK.effects.flashObject(bagContainer, 0x44ff44, 600);
}
}, {
id: "turbo",
name: "Turbo Booster",
price: 120,
owned: false,
unlockLevel: 2,
type: "consumable",
onBuy: function onBuy() {
turboActive = true;
turboTicks = 300;
LK.effects.flashObject(mower, 0x00ffff, 600);
}
}, {
id: "skin1",
name: "Red Flame Skin",
price: 80,
owned: storage.skin1Owned || false,
unlockLevel: 1,
type: "skin",
onBuy: function onBuy() {
mowerSkin = 1;
storage.skin1Owned = true;
updateMower();
LK.effects.flashObject(mower, 0xff2222, 600);
}
}, {
id: "mulcher",
name: "Mulching Attachment",
price: 200,
owned: storage.mulcherOwned || false,
unlockLevel: 3,
type: "attachment",
onBuy: function onBuy() {
mulcherEquipped = true;
storage.mulcherOwned = true;
LK.effects.flashObject(mower, 0x22ff22, 600);
}
}, {
id: "magnet",
name: "Magnet Power-Up",
price: 100,
owned: false,
unlockLevel: 2,
type: "consumable",
onBuy: function onBuy() {
magnetActive = true;
magnetTicks = 360;
LK.effects.flashObject(mower, 0x8888ff, 600);
}
}, {
id: "pet",
name: "Robotic Pet Helper",
price: 300,
owned: storage.petOwned || false,
unlockLevel: 4,
type: "attachment",
onBuy: function onBuy() {
petEquipped = true;
storage.petOwned = true;
LK.effects.flashObject(mower, 0xffff00, 600);
}
}];
// Track equipped/active powerups
var collectorEquipped = storage.collectorOwned || false;
var turboActive = false;
var turboTicks = 0;
var mowerSkin = 0;
var mulcherEquipped = storage.mulcherOwned || false;
var magnetActive = false;
var magnetTicks = 0;
var petEquipped = storage.petOwned || false;
// Open shop menu
function openShopMenu() {
// Only show items unlocked by mowerLevel
var unlocked = [];
for (var i = 0; i < shopItems.length; i++) {
if (mowerLevel >= shopItems[i].unlockLevel) {
unlocked.push(shopItems[i]);
}
}
var shopMenu = new ShopMenu();
shopMenu.x = 2048 / 2;
shopMenu.y = 2732 / 2;
shopMenu.addShopItems(unlocked);
game.addChild(shopMenu);
}
var bagContainer = new Container();
var bagBackground = bagContainer.attachAsset('bagIndicator', {
anchorX: 0,
anchorY: 0.5
});
var bagFillBar = bagContainer.attachAsset('bagFill', {
anchorX: 0,
anchorY: 0.5,
x: 2,
y: 0
});
var bagText = new Text2("Bag: 0/" + bagCapacity, {
size: 30,
fill: 0x000000
});
bagText.anchor.set(0.5, 0.5);
bagText.x = 100;
bagContainer.addChild(bagText);
bagContainer.x = 20;
bagContainer.y = 100;
LK.gui.top.addChild(bagContainer);
// Mower graphic (shows cursor position)
var mower = game.attachAsset('mower', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 150
});
// Create upgrade button
var upgradeButton = new UpgradeButton();
// Position at bottom center
upgradeButton.x = 2048 / 2;
upgradeButton.y = 2732 - 180;
upgradeButton.updatePrice(Math.floor(100 * Math.pow(1.5, mowerLevel - 1)));
game.addChild(upgradeButton);
// Create empty bag button
var emptyBagButton = new EmptyBagButton();
emptyBagButton.x = 180;
emptyBagButton.y = 2732 - 180;
game.addChild(emptyBagButton);
// Level text
var levelText = new Text2("Mower Level: " + mowerLevel, {
size: 40,
fill: 0xFFFFFF
});
levelText.anchor.set(0, 0);
levelText.x = 20;
levelText.y = 20;
LK.gui.top.addChild(levelText);
// Create grass grid
function createGrassGrid() {
// Calculate total width and height of the grid
var totalGridWidth = gridSize * tileSize + (gridSize - 1) * gridSpacing;
var totalGridHeight = gridSize * tileSize + (gridSize - 1) * gridSpacing;
// Calculate starting coordinates to center the grid on screen
var startX = (2048 - totalGridWidth) / 2;
var startY = (2732 - totalGridHeight) / 2;
// Make sure the grid is vertically centered with some space for UI
// Adjust vertical position to ensure grid is visible and centered between UI elements
startY = Math.max(startY, 250);
// Ensure grid doesn't go too low
startY = Math.min(startY, 2732 - totalGridHeight - 250);
var unlockedTypes = getUnlockedGrassTypes();
var zoneRows = Math.ceil(gridSize / unlockedTypes.length);
for (var row = 0; row < gridSize; row++) {
for (var col = 0; col < gridSize; col++) {
var grassTile = new GrassTile();
grassTile.x = startX + col * (tileSize + gridSpacing) + tileSize / 2;
grassTile.y = startY + row * (tileSize + gridSpacing) + tileSize / 2;
// Assign grass type by zone (horizontal bands)
var typeIdx = Math.floor(row / zoneRows);
if (typeIdx >= unlockedTypes.length) typeIdx = unlockedTypes.length - 1;
grassTile.setGrassType(unlockedTypes[typeIdx]);
game.addChild(grassTile);
grassTiles.push(grassTile);
}
}
}
function updateUI() {
moneyText.setText("$" + money);
// If collector equipped, show bonus capacity
bagText.setText("Bag: " + currentBag + "/" + bagCapacity + (collectorEquipped ? " (Collector)" : ""));
levelText.setText("Mower Level: " + mowerLevel);
// Update bag fill bar
var fillPercent = currentBag / bagCapacity;
bagFillBar.width = Math.max(0, Math.min(196 * fillPercent, 196));
// Update upgrade button state
upgradeButton.setEnabled(money >= upgradeButton.price);
}
function updateMower() {
// Scale mower based on level to indicate its upgraded capabilities
var scale = 1 + (mowerLevel - 1) * 0.1;
tween(mower, {
scaleX: scale,
scaleY: scale
}, {
duration: 500,
easing: tween.easeOut
});
// Change mower color based on level or skin
var level = mowerLevel % 5;
var colors = [0xdd2222, 0xff6600, 0xffcc00, 0x22dd22, 0x2222dd];
if (mowerSkin === 1) {
tween(mower, {
tint: 0xff2222
}, {
duration: 500
});
} else {
tween(mower, {
tint: colors[level]
}, {
duration: 500
});
}
}
// Track mouse/touch position for mower movement
game.move = function (x, y) {
mower.x = x;
mower.y = y;
// Show tooltip for hovered grass tile
for (var i = 0; i < grassTiles.length; i++) {
grassTiles[i].move(x, y, null);
}
};
// Handle multi-cut based on mower level
game.down = function (x, y) {
if (currentBag >= bagCapacity) return;
// Find the closest grass tile to the click position
var closestTile = null;
var closestDist = Number.MAX_VALUE;
for (var i = 0; i < grassTiles.length; i++) {
var tile = grassTiles[i];
var dx = tile.x - x;
var dy = tile.y - y;
var dist = dx * dx + dy * dy;
if (dist < closestDist) {
closestDist = dist;
closestTile = tile;
}
}
// Only proceed if we're close enough to a tile
if (closestTile && closestDist < tileSize * tileSize / 2) {
// Find the index of the closest tile
var tileIndex = grassTiles.indexOf(closestTile);
if (tileIndex !== -1) {
var row = Math.floor(tileIndex / gridSize);
var col = tileIndex % gridSize;
// Cut the tile and adjacent tiles based on cutArea
for (var r = Math.max(0, row - cutArea + 1); r <= Math.min(gridSize - 1, row + cutArea - 1); r++) {
for (var c = Math.max(0, col - cutArea + 1); c <= Math.min(gridSize - 1, col + cutArea - 1); c++) {
var targetIndex = r * gridSize + c;
var targetTile = grassTiles[targetIndex];
if (targetTile && !targetTile.isCut && currentBag < bagCapacity) {
var success = targetTile.cut();
if (success) {
// Use bagMultiplier for this grass type
currentBag += targetTile.bagMultiplier;
// Bonus money for harder grass
money += moneyPerCut + (targetTile.bonus || 0);
// Play sound for grass type
LK.getSound(targetTile.grassType && targetTile.grassType.sound ? targetTile.grassType.sound : 'mow').play();
// Start growing after a short delay
(function (tile) {
LK.setTimeout(function () {
tile.startGrowing();
}, 2000 + Math.random() * 1000);
})(targetTile);
// Flash effect
LK.effects.flashObject(targetTile, 0xffffff, 300);
if (currentBag >= bagCapacity) {
break;
}
}
}
}
if (currentBag >= bagCapacity) {
break;
}
}
updateUI();
// Only play sound once per overall cut action
LK.getSound('mow').play();
}
}
};
game.update = function () {
// Power-up: Turbo Booster
if (turboActive) {
turboTicks--;
if (turboTicks % 10 === 0) {
LK.effects.flashObject(mower, 0x00ffff, 100);
}
if (turboTicks <= 0) {
turboActive = false;
}
}
// Power-up: Magnet
if (magnetActive) {
magnetTicks--;
// Pull in nearby grass clippings (simulate: auto-cut nearby uncut tiles)
for (var i = 0; i < grassTiles.length; i++) {
var tile = grassTiles[i];
if (!tile.isCut && currentBag < bagCapacity) {
var dx = tile.x - mower.x;
var dy = tile.y - mower.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 200) {
var success = tile.cut();
if (success) {
currentBag += tile.bagMultiplier;
money += moneyPerCut + (tile.bonus || 0);
LK.effects.flashObject(tile, 0x8888ff, 200);
LK.getSound(tile.grassType && tile.grassType.sound ? tile.grassType.sound : 'mow').play();
updateUI();
LK.setTimeout(function (t) {
t.startGrowing();
}, 2000, tile);
if (currentBag >= bagCapacity) break;
}
}
}
}
if (magnetTicks <= 0) {
magnetActive = false;
}
}
// Pet Helper: Collects stray clippings (simulate: auto-empty bag if full)
if (petEquipped && currentBag >= bagCapacity) {
currentBag = 0;
LK.effects.flashObject(bagContainer, 0xffff00, 400);
LK.getSound('emptyBag').play();
updateUI();
}
// Mulcher: Bonus money for perfect lawns (simulate: if all cut, bonus)
if (mulcherEquipped) {
var allCut = true;
for (var i = 0; i < grassTiles.length; i++) {
if (!grassTiles[i].isCut) {
allCut = false;
break;
}
}
if (allCut) {
money += 20;
LK.effects.flashObject(mower, 0x22ff22, 600);
updateUI();
// Prevent repeat bonus until regrowth
mulcherEquipped = false;
LK.setTimeout(function () {
mulcherEquipped = storage.mulcherOwned;
}, 3000);
}
}
// Update all grass tiles
for (var i = 0; i < grassTiles.length; i++) {
grassTiles[i].update();
}
};
// Initialize the game
createGrassGrid();
updateUI();
updateMower();
// Play background music
LK.playMusic('bgmusic', {
loop: true
});
Fullscreen modern App Store landscape banner, 16:9, high definition, for a game titled "Infinite Lawn Master" and with the description "A satisfying grass-cutting simulator where players click to mow lawns, empty collection bags when full, and earn money to upgrade their equipment. Watch as cut grass regrows, creating an endless cycle of lawn maintenance and mower upgrades that increase efficiency and earning potential.". No text on banner!
top down view of long grass. In-Game asset. 2d. High contrast. No shadows
Simple yet pleasing rectangle that can be used for a UI. In-Game asset. 2d. High contrast. No shadows
long grass bar. In-Game asset. 2d. High contrast. No shadows