User prompt
"Display the player’s current money on screen. The automatic collection button is initially locked and shows 'Unlock'. When the player’s money reaches 10,000 or more, the button becomes clickable. If the player clicks the 'Unlock' button, deduct 10,000 from their money, activate automatic collection mode, and change the button text to 'Automatic Collection'."
User prompt
Automatic Collection Unlock Condition The Automatic Collection feature will unlock when the player reaches 10,000 money. Before the player has 10,000, the button should be disabled or hidden. When the player’s money is 10,000 or more, the button becomes enabled and clickable. Clicking the button will turn ON the Automatic Collection mode.
User prompt
When the game is finished, reset the player back to the beginning.
User prompt
Button Name The button text should be: "Automatic Collection" ✅ Behavior When the player clicks the button once, Automatic Collection mode turns ON. No need to hold the button—once it’s ON, it stays ON. The player’s character automatically collects nearby crops continuously while the mode is ON. 🔄 Toggle (Optional) You can decide if clicking the button again turns the mode OFF or if it stays ON permanently until the player leaves or resets. 📍 Button Position & Size Position: Bottom center of the screen AnchorPoint = Vector2.new(0.5, 1) Position = UDim2.new(0.5, 0, 1, -30) Size: Size = UDim2.new(0, 280, 0, 110) 🟩 Text Style Text: "Automatic Collection" Text color: bright green (TextColor3 = Color3.fromRGB(50, 205, 50)) 🧠 Extra Idea Change the button’s background color to show it’s active (e.g., green when ON, gray when OFF).
User prompt
Button Position (Bottom Center) Place the button at the bottom center of the screen. Recommended position: AnchorPoint = Vector2.new(0.5, 1) Position = UDim2.new(0.5, 0, 1, -30) (This means the button’s bottom center is 30 pixels above the bottom edge of the screen.) 📏 Button Size Keep it big and clear for easy clicking/tapping: Size = UDim2.new(0, 280, 0, 110) 🔘 Button Text Text: "Automatic Harvest" Text color: bright green (TextColor3 = Color3.fromRGB(50, 205, 50)) 🔄 Toggle Behavior Summary Click once → mode ON (button changes color to indicate active) Click again → mode OFF (button reverts to normal color) When ON, player automatically harvests nearby crops without holding or clicking
User prompt
Button Name & Label Change the button text to: "Automatic Harvest" 🔄 Toggle Behavior (On/Off by Clicking) When the player clicks the button once, the Automatic Harvest mode turns ON. When the player clicks the button again, the mode turns OFF. No need to hold the mouse button — once it’s on, harvesting happens automatically as the player moves near crops. 📏 Button Size & Position Make the button bigger for easier interaction. Recommended size: Size = UDim2.new(0, 280, 0, 110) (Width: 280 px, Height: 110 px) Position on the right side of the screen, centered vertically: AnchorPoint = Vector2.new(1, 0.5) Position = UDim2.new(1, -30, 0.5, 0) 📝 Text Style Text color: Bright green (TextColor3 = Color3.fromRGB(50, 205, 50)) Text example: "Automatic Harvest" 🧠 Additional Tips Change the button’s background color or add a glow effect to visually indicate if Automatic Harvest is active. While the mode is active, the player’s character automatically harvests crops nearby without clicking or holding. If the player toggles the mode off, harvesting stops until toggled on again.
User prompt
🔄 Toggle Behavior for Swipe Harvest Button Goal: When the player clicks the button once, Swipe Harvest Mode turns ON. When the player clicks the button again, Swipe Harvest Mode turns OFF. How It Works Step-by-Step: Initial State Set a boolean variable, e.g., swipeModeActive = false at the start. On Button Click If swipeModeActive == false (currently off): Set swipeModeActive = true (turn it on) Change button appearance (e.g., color to green) Show message: "Swipe Harvest Mode Activated" Else if swipeModeActive == true (currently on): Set swipeModeActive = false (turn it off) Reset button appearance (e.g., color to default) Show message: "Swipe Harvest Mode Deactivated" Harvest Logic Only allow harvesting while swipeModeActive == true. Visual Feedback Suggestion: Button color toggles between two states: Inactive: Blue or gray Active: Bright green Optional: Add a small label/text near the button that shows "ON" or "OFF".
User prompt
. Button Position The button should be placed on the right side of the screen. Recommended position: AnchorPoint = (1, 0.5) and Position = UDim2.new(1, -30, 0.5, 0) (This means it will be centered vertically near the right edge, with a 30-pixel margin.) 📏 2. Button Size Make the button a bit bigger than usual for easy clicking/tapping. Recommended size: Size = UDim2.new(0, 200, 0, 80) (Width: 200 pixels, Height: 80 pixels — large enough for clear text and easy mobile use.) 📝 3. Button Text (Function Description) The button should display this text clearly: "Swipe Harvest Mode: Hold and Drag to Harvest" Or a shorter alternative: "Swipe Mode 🌾 (Hold & Drag)" This text clearly explains what the button does to the player. 🧠 Extra Tip: Change the button color when it’s active to show it’s turned on: Normal color: Gray or Blue (Color3.fromRGB(100, 100, 255)) Active color: Green (Color3.fromRGB(50, 200, 100))
User prompt
Swipe Mode Activation Button (with Hold-to-Harvest Mechanic) 🎯 Concept: A button appears on the screen (UI). When the player clicks/taps this button, their character enters "Swipe Harvest Mode". In this mode, holding and dragging across crops will harvest them instantly. ✅ Step-by-Step Breakdown 1. Create the UI Button Design a ScreenGui + TextButton or ImageButton named "SwipeModeButton". Place it on the corner or center of the screen. Label: "Swipe Mode" or an icon like a sickle 🌾. 2. Activate Swipe Mode When the player clicks the button: Set a variable like swipeModeActive = true. Show a message like "Swipe Mode Activated!". Optionally change the button’s color to show it’s active. 3. Track Holding + Movement While swipeModeActive == true: Use UserInputService.InputBegan to detect when the mouse or finger is held down. While held: Use RunService.RenderStepped or Heartbeat to constantly raycast from the screen. Check what object (crop) is under the cursor/finger. If the object is a ripe crop and hasn't already been harvested: Harvest it. Mark it as harvested for that session. 4. Stop Swipe Mode After some time (optional), or if the player clicks the button again: Set swipeModeActive = false. Reset the UI button color/state. 📱 Mobile Compatibility Use TouchStarted, TouchMoved, and TouchEnded to support phones and tablets. The button works the same way on mobile and PC. 💡 Extra Suggestions Add a short cooldown or energy bar to balance the swipe mode. Visually highlight crops that will be harvested when swiping. Optional: Only unlock this mode after reaching a certain level or money amount (e.g., 10,000 coins).
User prompt
Please fix the bug: 'Timeout.tick error: zombies is not defined' in or related to this line: 'if (zombies.length > 0) {' Line Number: 1699
User prompt
Swipe Harvesting System – Detailed Explanation 🧠 Concept: Players can hold down the mouse button (or finger on mobile) and swipe across crops to harvest them quickly. As they drag across ready crops, each one gets harvested without needing individual clicks. ✅ How It Works (Step by Step) Input Detection When the player holds down the mouse button (PC) or touches and holds (mobile), start tracking the mouse/touch movement. Raycasting or Region Detection As the player moves their cursor or finger across the screen, cast a ray (or use Region3) from the camera to detect what object is being hovered. If the hovered object is a ripe crop, trigger the harvest function. Continuous Check While Holding Use a loop (e.g., RunService.RenderStepped) to continuously check while the player is holding. Harvest any new crops the swipe touches. Prevent Repeat Harvesting Keep track of already harvested crops during the swipe to avoid double harvesting the same one. Harvest Action When a ripe crop is detected: Play a particle or sound effect. Add the item to the player's inventory. Disable or remove the crop. Mobile Support Use UserInputService.TouchStarted, TouchMoved, and TouchEnded for swipe support on phones/tablets. Works just like dragging with a finger. Visual Feedback (Optional) Show a light effect or trail behind the cursor/finger to make swiping feel responsive. Add a glow or animation on crops that are being harvested.
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'var progress = Math.max(0, Math.min(1, money / 100000));' Line Number: 1175
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'var progress = Math.max(0, Math.min(1, money / 100000));' Line Number: 1175
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'money')' in or related to this line: 'moneyTxt.setText('$' + player.money);' Line Number: 1169
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'if (money >= 100000 && !game._missionWinShown) {' Line Number: 1179
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'var progress = Math.max(0, Math.min(1, money / 100000));' Line Number: 1175
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'money')' in or related to this line: 'moneyTxt.setText('$' + player.money);' Line Number: 1169
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'if (money >= 100000 && !game._missionWinShown) {' Line Number: 1179
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'var progress = Math.max(0, Math.min(1, money / 100000));' Line Number: 1175
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'if (money >= 100000 && !game._missionWinShown) {' Line Number: 1179
User prompt
Please fix the bug: 'money is not defined' in or related to this line: 'var progress = Math.max(0, Math.min(1, money / 100000));' Line Number: 1175
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'money')' in or related to this line: 'moneyTxt.setText('$' + player.money);' Line Number: 1169
User prompt
Auto-Harvest System (With Unlock Requirement + Click to Trigger) 🧠 Goal: Players can only use the auto-harvest feature if: They have at least 25,000 money. They click on the crop while being close enough to it. ✅ How to Set It Up (Step by Step) Money Requirement (25,000 Coins) Create a value like Money under the player's data. Only allow auto-harvest if player.Money >= 25000. Proximity Check When the player clicks a crop, check the distance between the player and the crop. If they are within, say, 10 studs, allow harvesting. Click to Harvest Even though it's “automatic,” require the player to click (left mouse) when near the crop. This keeps it interactive but still easier than before. Harvest Action If both conditions are true (money + proximity + click), then: Give the crop item (e.g., Wheat). Play a visual/sound effect. Remove or reset the crop. 💬 Example Logic in Words: The player walks up to a ripe crop. They click on the crop. The script checks: Is the player’s money 25,000 or more? Is the player close enough to the crop? If yes, the crop is harvested automatically and added to their inventory. 🎨 Extra Tip: You can add a UI pop-up that says: “Auto-Harvest Unlocked at 25,000 Coins!” to encourage players to reach that goal.
User prompt
"There is lag in the game."
User prompt
"There is lag in the game."
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Removed plugin import to fix 'Unable to load plugins' error var Animal = Container.expand(function () { var self = Container.call(this); self.animalAsset = null; self.setAnimalImage = function (imageId) { if (self.animalAsset) { self.animalAsset.destroy(); } self.animalAsset = self.attachAsset(imageId, { anchorX: 0.5, anchorY: 0.5 }); }; return self; }); // Bird class var Bird = Container.expand(function () { var self = Container.call(this); // Create the bird asset var birdAsset = self.attachAsset('Bird', { anchorX: 0.5, anchorY: 0.5, alpha: 1, scaleX: 0.8, scaleY: 0.8 }); // Variables for movement self.speed = 1.0; // Faster than clouds self.lifeTime = 3000; // 50 seconds at 60fps self.lifeCounter = 0; // Track how long bird has been visible self.flyHeight = 0; // Vertical position offset for flying pattern // Initialize bird self.init = function () { self.lifeCounter = 0; self.alpha = 1; // Start movement animation using tween self.startMoving(); }; // Start the left to right movement with slight up/down wave pattern self.startMoving = function () { // Move bird to the right side of screen var targetX = 2048 + birdAsset.width / 2; // Move past right edge // Create the movement tween tween(self, { x: targetX }, { duration: 20000, // Faster than clouds - 20 seconds to cross screen easing: tween.linear }); }; // Update function called every frame - optimized self.update = function () { // Early return if invisible or not visible - prevents unnecessary calculations if (self.alpha <= 0 || !self.visible) return; // Increment life counter first for potential early returns self.lifeCounter++; // Only check edge crossing if we're near the edge - optimization var edgeX = 2048 + birdAsset.width / 2; if (self.x >= edgeX) { // Stop any existing tweens on this bird tween.stop(self); // Fade out animation tween(self, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Remove bird from game self.visible = false; // Hide instead of destroy birds.push(self); // Return to pool } }); return; // Early return after starting fade out animation } // Create a small wavy flight pattern - only for visible, active birds self.y = self.y + Math.sin(self.lifeCounter * 0.03) * 1.5; }; return self; }); // CarrotSlot class for area 2 var CarrotSlot = Container.expand(function () { var self = Container.call(this); self.slotAsset = self.attachAsset('carrot', { anchorX: 0.5, anchorY: 1 }); var sproutAsset = self.attachAsset('sprout', { anchorX: 0.5, anchorY: 1, alpha: 0, scaleX: 0.5, scaleY: 0.5 }); self.harvested = false; self.regrowTime = 1800; // 30 seconds at 60fps self.regrowCounter = 0; self.fieldIndex = 1; // Area 2 self.regrowing = false; // Show/hide carrot and handle regrow animation self.setHarvested = function (harvested) { self.harvested = harvested; if (harvested) { // Hide carrot with animation tween(self.slotAsset, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); // Show sprout with animation sproutAsset.alpha = 1; sproutAsset.scaleX = 0.1; sproutAsset.scaleY = 0.1; // Animate sprout appearing tween(sproutAsset, { scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); self.regrowCounter = self.regrowTime; self.regrowing = true; } else { // Show carrot with animation tween(self.slotAsset, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeIn }); // Hide sprout with animation tween(sproutAsset, { alpha: 0 }, { duration: 300, easing: tween.easeIn }); self.regrowing = false; } }; // Regrow logic self.update = function () { if (self.harvested && self.regrowing) { if (self.regrowCounter > 0) { // Animate sprout growing to carrot size var t = 1 - self.regrowCounter / self.regrowTime; sproutAsset.scaleX = 0.5 + 0.5 * t; sproutAsset.scaleY = 0.5 + 0.5 * t; self.regrowCounter--; } if (self.regrowCounter <= 0) { // Sprout is now full size, switch back to carrot and allow harvest again sproutAsset.scaleX = 1; sproutAsset.scaleY = 1; self.setHarvested(false); } } }; // For hit detection self.isHarvestable = function () { return !self.harvested; }; return self; }); // Cloud class var Cloud = Container.expand(function () { var self = Container.call(this); // Create the cloud asset var cloudAsset = self.attachAsset('cloud', { anchorX: 0.5, anchorY: 0.5, alpha: 0.8 // Semi-transparent clouds }); // Variables for movement self.speed = 0.2; // Very slow speed for smooth movement self.lifeTime = 6000; // 100 seconds at 60fps - longer lifetime self.lifeCounter = 0; // Track how long cloud has been visible self.layer = 0; // Layer for visual depth (0 = front, 1 = back) // Initialize cloud self.init = function () { self.lifeCounter = 0; self.alpha = 1; // Apply layer-specific settings if (self.layer === 1) { // Back layer cloud (slightly smaller and more transparent) self.scale.set(0.8, 0.8); self.alpha = 0.7; } // Start movement animation using tween self.startMoving(); }; // Start the left to right movement only self.startMoving = function () { // Move cloud to the right side of screen var targetX = 2048 + cloudAsset.width / 2; // Move past right edge // Create the movement tween tween(self, { x: targetX }, { duration: 60000, // Slower movement - 60 seconds to cross screen easing: tween.linear }); }; // Update function called every frame - optimized self.update = function () { // Early return if invisible or not visible - saves processing if (self.alpha <= 0 || !self.visible) return; // Track lastX for edge detection - initialize once if undefined if (typeof self.lastX === "undefined") self.lastX = self.x; // Only check edge crossing if we're near the edge - optimization var edgeX = 2048 + cloudAsset.width / 2; if (self.x >= edgeX && self.lastX < edgeX) { // Stop any existing tweens on this cloud to prevent conflicts tween.stop(self); // Fade out animation tween(self, { alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Remove cloud from game self.visible = false; // Hide instead of destroy clouds.push(self); // Return to pool } }); } // Update lastX for next frame self.lastX = self.x; }; return self; }); // EggplantSlot class for area 4 var EggplantSlot = Container.expand(function () { var self = Container.call(this); self.slotAsset = self.attachAsset('eggplant', { anchorX: 0.5, anchorY: 1 }); var sproutAsset = self.attachAsset('sprout', { anchorX: 0.5, anchorY: 1, alpha: 0, scaleX: 0.5, scaleY: 0.5 }); self.harvested = false; self.regrowTime = 1800; // 30 seconds at 60fps self.regrowCounter = 0; self.fieldIndex = 3; // Area 4 self.regrowing = false; // Show/hide eggplant and handle regrow animation self.setHarvested = function (harvested) { self.harvested = harvested; if (harvested) { // Hide eggplant with animation tween(self.slotAsset, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); // Show sprout with animation sproutAsset.alpha = 1; sproutAsset.scaleX = 0.1; sproutAsset.scaleY = 0.1; // Animate sprout appearing tween(sproutAsset, { scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); self.regrowCounter = self.regrowTime; self.regrowing = true; } else { // Show eggplant with animation tween(self.slotAsset, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeIn }); // Hide sprout with animation tween(sproutAsset, { alpha: 0 }, { duration: 300, easing: tween.easeIn }); self.regrowing = false; } }; // Regrow logic self.update = function () { if (self.harvested && self.regrowing) { if (self.regrowCounter > 0) { // Animate sprout growing to eggplant size var t = 1 - self.regrowCounter / self.regrowTime; sproutAsset.scaleX = 0.5 + 0.5 * t; sproutAsset.scaleY = 0.5 + 0.5 * t; self.regrowCounter--; } if (self.regrowCounter <= 0) { // Sprout is now full size, switch back to eggplant and allow harvest again sproutAsset.scaleX = 1; sproutAsset.scaleY = 1; self.setHarvested(false); } } }; // For hit detection self.isHarvestable = function () { return !self.harvested; }; return self; }); // Farmer class var Farmer = Container.expand(function () { var self = Container.call(this); // Create animation frames var idleFrame = self.attachAsset('farmer', { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); // Frame 2 (running) var runningFrame = self.attachAsset('farmer_walk', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); // Frame 3 (right step) var rightStepFrame = self.attachAsset('farmer_walk', { anchorX: 0.5, anchorY: 0.5, alpha: 0, rotation: 0.1 }); self.speed = 18; // px per move self.targetX = self.x; self.targetY = self.y; self.moving = false; self.lastMoving = false; self.animationTimer = 0; self.animationFrame = 0; self.frameTime = 18; // 0.3 seconds at 60fps // Animation frame switching self.switchToFrame = function (frameNum) { idleFrame.alpha = 0; runningFrame.alpha = 0; rightStepFrame.alpha = 0; if (frameNum === 0) { // Idle idleFrame.alpha = 1; } else if (frameNum === 1) { // Running runningFrame.alpha = 1; } else if (frameNum === 2) { // Right step rightStepFrame.alpha = 1; } }; // Move farmer towards target self.update = function () { // Track movement direction and update image if (self.moving) { var dx = self.targetX - self.x; // Check if moving right or left if (dx > 0) { // Moving right - use player_right asset idleFrame.scaleX = 1; runningFrame.scaleX = 1; rightStepFrame.scaleX = 1; // Switch to player_right image if (self.currentAsset !== idleFrame || idleFrame.texture !== LK.getAsset('player_right', {}).texture) { if (self.currentAsset) { self.currentAsset.alpha = 0; } var rightAsset = LK.getAsset('player_right', { anchorX: 0.5, anchorY: 0.5 }); rightAsset.alpha = 1; self.addChild(rightAsset); self.currentAsset = rightAsset; } } else if (dx < 0) { // Moving left - use player_left asset idleFrame.scaleX = -1; runningFrame.scaleX = -1; rightStepFrame.scaleX = -1; // Switch to player_left image if (self.currentAsset !== idleFrame || idleFrame.texture !== LK.getAsset('player_left', {}).texture) { if (self.currentAsset) { self.currentAsset.alpha = 0; } var leftAsset = LK.getAsset('player_left', { anchorX: 0.5, anchorY: 0.5 }); leftAsset.alpha = 1; self.addChild(leftAsset); self.currentAsset = leftAsset; } } // Animation handling self.animationTimer++; if (self.animationTimer >= self.frameTime) { self.animationTimer = 0; self.animationFrame = self.animationFrame === 1 ? 2 : 1; // Toggle between frames 1 and 2 self.switchToFrame(self.animationFrame); } } else if (!self.moving && self.lastMoving) { // Just stopped moving - switch to idle self.switchToFrame(0); } self.lastMoving = self.moving; if (!self.moving) return; var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.speed) { self.x = self.targetX; self.y = self.targetY; self.moving = false; // Switch to idle frame when stopping self.switchToFrame(0); } else { self.x += self.speed * dx / dist; self.y += self.speed * dy / dist; } }; // Set movement target self.moveTo = function (x, y) { self.targetX = x; self.targetY = y; // Start movement if (!self.moving) { self.moving = true; self.animationFrame = 1; self.animationTimer = 0; self.switchToFrame(self.animationFrame); } }; // Attach sickle to farmer's hand var sickle = self.attachAsset('sickle', { anchorX: 0.2, // hand position, left side of ellipse anchorY: 0.7, // slightly below center x: 0, y: 30 }); sickle.alpha = 1; // Add attack animation for sickle self.attack = function () { tween(sickle, { rotation: Math.PI * 2 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { sickle.rotation = 0; } }); }; return self; }); // Field class var Field = Container.expand(function () { var self = Container.call(this); self.index = 0; // 0-3 self.locked = false; self.lockCost = 0; self.lockNode = null; self.fenceNodes = []; self.wheats = []; self.unlockBtn = null; // Draw fences - Empty implementation to eliminate fences self.drawFences = function (x, y, w, h) { self.fenceNodes = []; // No fences or gates are created }; // Place wheat self.placeWheats = function (x, y, w, h, count) { self.wheats = []; // Define grid size var rows = 5; var cols = 10; // Choose vegetable asset per area var vegAssetId = 'wheat'; if (self.index === 1) vegAssetId = 'carrot'; if (self.index === 2) vegAssetId = 'tomato'; if (self.index === 3) vegAssetId = 'eggplant'; // Fallback if asset not found, use wheat if (!LK.assets || !LK.assets[vegAssetId]) vegAssetId = 'wheat'; // Calculate asset size for spacing (use wheat size as reference for all) var vegAsset = LK.getAsset('wheat', { anchorX: 0.5, anchorY: 1 }); var wheatW = vegAsset.width; var wheatH = vegAsset.height; // Padding from fences var padX = 60; var padY = 60; // Compute available area for wheat grid var gridW = w - 2 * padX; var gridH = h - 2 * padY; // Compute spacing between wheat plants var spacingX = (gridW - wheatW) / (cols - 1); var spacingY = (gridH - wheatH) / (rows - 1); // Place appropriate vegetable in grid based on field index for (var row = 0; row < rows; ++row) { for (var col = 0; col < cols; ++col) { var wx = x + padX + col * spacingX + wheatW / 2; var wy = y + padY + row * spacingY + wheatH; var veg; // Create appropriate vegetable based on area/field index if (self.index === 1) { veg = new CarrotSlot(); } else if (self.index === 2) { veg = new TomatoSlot(); } else if (self.index === 3) { veg = new EggplantSlot(); } else { veg = new Wheat(); veg.fieldIndex = self.index; veg.vegAssetId = vegAssetId; veg.setVegAsset && veg.setVegAsset(vegAssetId); } veg.x = wx; veg.y = wy; self.addChild(veg); self.wheats.push(veg); } } }; // Lock overlay self.showLock = function (centerX, centerY) { if (self.lockNode) return; self.lockNode = LK.getAsset('lock', { anchorX: 0.5, anchorY: 0.5, x: centerX, y: centerY }); self.addChild(self.lockNode); // Unlock text var unlockTxt = new Text2('Unlock\n$' + self.lockCost, { size: 60, fill: "#fff" }); unlockTxt.anchor.set(0.5, 0.5); unlockTxt.x = centerX; unlockTxt.y = centerY + 70; self.unlockBtn = unlockTxt; self.addChild(unlockTxt); }; self.hideLock = function () { if (self.lockNode) { self.lockNode.destroy(); self.lockNode = null; } if (self.unlockBtn) { self.unlockBtn.destroy(); self.unlockBtn = null; } }; return self; }); // Rooster class with 4-frame animation cycling smoothly var Rooster = Container.expand(function () { var self = Container.call(this); // Animation frame asset ids var frameIds = ['rooster_1', 'rooster_2', 'rooster_3', 'rooster_4']; self.roosterFrames = []; self.currentFrame = 0; self.frameTimer = 0; self.frameInterval = 10; // ~0.16s per frame at 60fps (smooth) // Attach all frames, only show the first for (var i = 0; i < frameIds.length; ++i) { var frame = self.attachAsset(frameIds[i], { anchorX: 0.5, anchorY: 0.5, alpha: i === 0 ? 1 : 0 }); self.roosterFrames.push(frame); } // Animation update self.update = function () { self.frameTimer++; if (self.frameTimer >= self.frameInterval) { // Hide current frame self.roosterFrames[self.currentFrame].alpha = 0; // Advance to next frame self.currentFrame = (self.currentFrame + 1) % self.roosterFrames.length; // Show new frame self.roosterFrames[self.currentFrame].alpha = 1; self.frameTimer = 0; } }; return self; }); // TomatoSlot class for area 3 var TomatoSlot = Container.expand(function () { var self = Container.call(this); self.slotAsset = self.attachAsset('tomato', { anchorX: 0.5, anchorY: 1 }); var sproutAsset = self.attachAsset('sprout', { anchorX: 0.5, anchorY: 1, alpha: 0, scaleX: 0.5, scaleY: 0.5 }); self.harvested = false; self.regrowTime = 1800; // 30 seconds at 60fps self.regrowCounter = 0; self.fieldIndex = 2; // Area 3 self.regrowing = false; // Show/hide tomato and handle regrow animation self.setHarvested = function (harvested) { self.harvested = harvested; if (harvested) { // Hide tomato with animation tween(self.slotAsset, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); // Show sprout with animation sproutAsset.alpha = 1; sproutAsset.scaleX = 0.1; sproutAsset.scaleY = 0.1; // Animate sprout appearing tween(sproutAsset, { scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); self.regrowCounter = self.regrowTime; self.regrowing = true; } else { // Show tomato with animation tween(self.slotAsset, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeIn }); // Hide sprout with animation tween(sproutAsset, { alpha: 0 }, { duration: 300, easing: tween.easeIn }); self.regrowing = false; } }; // Regrow logic self.update = function () { if (self.harvested && self.regrowing) { if (self.regrowCounter > 0) { // Animate sprout growing to tomato size var t = 1 - self.regrowCounter / self.regrowTime; sproutAsset.scaleX = 0.5 + 0.5 * t; sproutAsset.scaleY = 0.5 + 0.5 * t; self.regrowCounter--; } if (self.regrowCounter <= 0) { // Sprout is now full size, switch back to tomato and allow harvest again sproutAsset.scaleX = 1; sproutAsset.scaleY = 1; self.setHarvested(false); } } }; // For hit detection self.isHarvestable = function () { return !self.harvested; }; return self; }); // Wheat class var Wheat = Container.expand(function () { var self = Container.call(this); self.vegAssetId = 'wheat'; self.wheatAsset = self.attachAsset('wheat', { anchorX: 0.5, anchorY: 1 }); var sproutAsset = self.attachAsset('sprout', { anchorX: 0.5, anchorY: 1, alpha: 0, scaleX: 0.5, scaleY: 0.5 }); self.harvested = false; self.regrowTime = 1800; // 30 seconds at 60fps self.regrowCounter = 0; self.fieldIndex = 0; // which field this wheat belongs to self.regrowing = false; self.removeAfter = 0; // Change vegetable asset if needed self.setVegAsset = function (assetId) { if (self.wheatAsset) { self.wheatAsset.destroy(); } self.vegAssetId = assetId || 'wheat'; // Only allow wheat, carrot, tomato, eggplant var validAssets = { wheat: 1, carrot: 1, tomato: 1, eggplant: 1 }; if (!validAssets[self.vegAssetId]) self.vegAssetId = 'wheat'; self.wheatAsset = self.attachAsset(self.vegAssetId, { anchorX: 0.5, anchorY: 1 }); // Make sure sprout is on top self.removeChild(sproutAsset); self.addChild(sproutAsset); }; // Show/hide wheat and handle regrow animation self.setHarvested = function (harvested) { self.harvested = harvested; if (harvested) { // Hide wheat, show sprout if (self.wheatAsset) { // Animate wheat harvesting with tween tween(self.wheatAsset, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); } // Show and animate sprout growing from nothing sproutAsset.alpha = 1; sproutAsset.scaleX = 0.1; sproutAsset.scaleY = 0.1; // Animate sprout appearing tween(sproutAsset, { scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeOut }); self.regrowCounter = self.regrowTime; self.regrowing = true; } else { // Show wheat/veg, hide sprout with animation if (self.wheatAsset) { tween(self.wheatAsset, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeIn }); } // Fade out sprout tween(sproutAsset, { alpha: 0 }, { duration: 300, easing: tween.easeIn }); self.regrowing = false; } }; // Regrow logic and removal after 5 seconds - highly optimized self.update = function () { // Early return if not harvested or not regrowing - prevents unnecessary checks if (!self.harvested || !self.regrowing) return; if (self.regrowCounter > 0) { // Optimize calculations by using cached values and only calculating once var t = 1 - self.regrowCounter / self.regrowTime; var newScale = 0.5 + 0.5 * t; sproutAsset.scaleX = newScale; sproutAsset.scaleY = newScale; self.regrowCounter--; } else { // Sprout is now full size, switch back to wheat/veg and allow harvest again sproutAsset.scaleX = 1; sproutAsset.scaleY = 1; self.setHarvested(false); } }; // For hit detection self.isHarvestable = function () { return !self.harvested; }; return self; }); var Zombie = Container.expand(function () { var self = Container.call(this); // Create the zombie frames (two different assets) var zombieFrame1 = self.attachAsset('zombie_1', { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); var zombieFrame2 = self.attachAsset('zombie_2', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); // Animation variables self.currentFrame = 0; self.frameTimer = 0; self.frameInterval = 15; // Frame switch interval self.speed = 2; // Movement speed self.lastX = 0; // Track previous X for collision detection self.lastY = 0; // Track previous Y for collision detection self.lastWasIntersecting = false; // Track previous intersection state self.isDead = false; // Track if zombie is dead // Handle click/tap on zombie self.down = function (x, y, obj) { // Mark zombie as clicked and initiate death sequence if (!self.isDead) { self.isDead = true; // Flash zombie yellow to indicate hit LK.effects.flashObject(self, 0xFFFF00, 300); // Death animation - fade out and scale down tween(self, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Remove zombie from game self.destroy(); // Create new zombie after a 40s delay LK.setTimeout(createZombie, 40000); } }); // Prevent event from bubbling to game (stop farmer movement) return true; } }; // Zombie animation and movement self.update = function () { // Don't update if dead or not visible if (self.isDead || !self.visible) return; // Save previous position for collision detection self.lastX = self.x; self.lastY = self.y; // Frame animation self.frameTimer++; if (self.frameTimer >= self.frameInterval) { // Toggle between frames self.currentFrame = self.currentFrame === 0 ? 1 : 0; // Show current frame zombieFrame1.alpha = self.currentFrame === 0 ? 1 : 0; zombieFrame2.alpha = self.currentFrame === 1 ? 1 : 0; self.frameTimer = 0; } // Move zombie toward the farmer if (farmer) { var dx = farmer.x - self.x; var dy = farmer.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > self.speed) { self.x += self.speed * dx / dist; self.y += self.speed * dy / dist; } } // Check if zombie is off-screen and return to pool if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) { self.visible = false; // Hide instead of destroy zombies.push(self); // Return to pool } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x4caf50 // Grass green }); /**** * Game Code ****/ // Area 4 // Area 3 // Area 2 // Lock icon (ellipse, gray) // Sickle (ellipse, yellow) // House (box) // Farmer (box) // Wheat (ellipse) // Field fence (rectangle) // --- Game constants --- // Rooster animation frames (replace id values with your own image ids) var FIELD_W = 900; var FIELD_H = 900; var FIELD_GAP = 120; // Increased gap to move fences further from center var FIELD_COUNT = 4; var WHEAT_PER_FIELD = 10; // Area unlock prices and reward multipliers var FIELD_LOCK_COSTS = [0, 20000, 40000, 60000]; // Area 1 open, others locked var FIELD_REWARD_MULT = [1, 2, 3, 4]; // Area 1=1x, 2=2x, 3=3x, 4=4x // --- Area constants --- // Define the distance from center for all areas (equal distance) var AREA_DISTANCE = 600; // Create an array to hold the areas var areas = []; var animals = []; // --- Game state --- var fields = []; var farmer = null; var money = storage.money || 0; var moneyTxt = null; var sickle = null; var dragging = false; var dragOffsetX = 0; var dragOffsetY = 0; var unlockFieldIndex = -1; var playerMovementBlockedByUIClick = false; // Flag to indicate UI handled the click // --- Layout calculation --- // Fields: 2x2 grid, with house in center // [0][1] // [2][3] var fieldPositions = [{ x: 0, y: 0 }, // top-left { x: FIELD_W + FIELD_GAP, y: 0 }, // top-right { x: 0, y: FIELD_H + FIELD_GAP }, // bottom-left { x: FIELD_W + FIELD_GAP, y: FIELD_H + FIELD_GAP } // bottom-right ]; // Center everything in game area var totalW = FIELD_W * 2 + FIELD_GAP; var totalH = FIELD_H * 2 + FIELD_GAP; var offsetX = Math.floor((2048 - totalW) / 2); var offsetY = Math.floor((2732 - totalH) / 2); // --- Create fields --- // Load unlocked fields from storage, default to [true, false, false, false] (only first field unlocked) // Defensive: ensure unlockedFields is an array of length 4 var unlockedFields = storage.unlockedFields; if (!Array.isArray(unlockedFields) || unlockedFields.length !== 4) { unlockedFields = [true, false, false, false]; storage.unlockedFields = unlockedFields; } for (var i = 0; i < FIELD_COUNT; ++i) { var f = new Field(); f.index = i; // Use unlockedFields to determine locked state f.locked = !unlockedFields[i]; f.lockCost = FIELD_LOCK_COSTS[i]; var pos = fieldPositions[i]; var fx = offsetX + pos.x; var fy = offsetY + pos.y; // Skip drawing fences f.placeWheats(fx, fy, FIELD_W, FIELD_H, WHEAT_PER_FIELD); if (f.locked) { // Center of field f.showLock(fx + FIELD_W / 2, fy + FIELD_H / 2); } game.addChild(f); fields.push(f); } // Center reference point for positions var centerX = offsetX + FIELD_W; var centerY = offsetY + FIELD_H - 250; // --- Create farmer --- farmer = new Farmer(); farmer.x = storage.farmerX || centerX; farmer.y = storage.farmerY || centerY - 120; // Start at center area game.addChild(farmer); // --- Create sickle (attached to farmer's hand) --- sickle = LK.getAsset('sickle', { anchorX: 0.2, // hand position, left side of ellipse anchorY: 0.7, // slightly below center x: 0, y: 30 }); sickle.alpha = 1; // --- Money display --- moneyTxt = new Text2('$0', { size: 100, fill: "#fff" }); moneyTxt.anchor.set(0.5, 0); LK.gui.top.addChild(moneyTxt); // --- Information message on right side --- // Create the info message text var infoMsgTxt = new Text2('Game controls: Use mouse to move.\nFor mobile, tap to move.', { size: 80, fill: "#fff" }); infoMsgTxt.anchor.set(1, 0.5); // Right align, vertically centered // Position at right side, vertically centered (avoid top 100px for menu) infoMsgTxt.x = 2048 - 40; // 40px padding from right edge infoMsgTxt.y = 2732 / 2; game.addChild(infoMsgTxt); // Hide after 30 seconds (1800 frames at 60fps) LK.setTimeout(function () { if (infoMsgTxt && infoMsgTxt.parent) { infoMsgTxt.destroy(); } }, 30000); // --- Missions UI --- // "Missions" title var missionsTitle = new Text2('Missions', { size: 90, fill: "#fff" }); missionsTitle.anchor.set(0, 0); // Left align, top // Place on the left side of the center part of the screen var missionAreaX = 180; // 180px from left edge, safe from menu var missionAreaY = 120; // Below top menu area missionsTitle.x = missionAreaX; missionsTitle.y = missionAreaY; game.addChild(missionsTitle); // Mission bar background (smaller, left side) var missionBarBg = LK.getAsset('centerCircle', { anchorX: 0, anchorY: 0, x: missionAreaX, y: missionsTitle.y + missionsTitle.height + 18, scaleX: 2.2, scaleY: 0.5, alpha: 0.25 }); game.addChild(missionBarBg); // Mission bar fill (progress) var missionBarFill = LK.getAsset('centerCircle', { anchorX: 0, anchorY: 0, x: missionAreaX, y: missionsTitle.y + missionsTitle.height + 18, scaleX: 0, // Will be set dynamically scaleY: 0.5, tint: 0xFFD700, // Gold color alpha: 0.85 }); game.addChild(missionBarFill); // Mission text var missionText = new Text2('Reach $100,000', { size: 60, fill: "#fff" }); missionText.anchor.set(0, 0); missionText.x = missionAreaX + 10; missionText.y = missionBarBg.y + missionBarBg.height + 8; game.addChild(missionText); // Store for update var missionBarMaxWidth = 200; // px, visually (not used, but kept for reference) var missionBarFillMaxScale = 2.2; // --- Helper: update money display --- function updateMoneyDisplay() { moneyTxt.setText('$' + money); // --- Missions progress update --- if (typeof missionBarFill !== "undefined") { // Clamp progress between 0 and 1 var progress = Math.max(0, Math.min(1, money / 100000)); missionBarFill.scaleX = missionBarFillMaxScale * progress; } // --- Mission complete: You Win! --- if (money >= 100000 && !game._missionWinShown) { game._missionWinShown = true; LK.showYouWin(); } } // --- Helper: unlock field if enough money --- function tryUnlockField(fieldIdx) { var f = fields[fieldIdx]; if (!f.locked) return; if (money >= f.lockCost) { money -= f.lockCost; updateMoneyDisplay(); f.locked = false; f.hideLock(); // Update unlockedFields in storage only if changed var unlockedFields = storage.unlockedFields || [true, false, false, false]; if (!unlockedFields[fieldIdx]) { unlockedFields[fieldIdx] = true; storage.unlockedFields = unlockedFields; } // Flash field green for (var i = 0; i < f.fenceNodes.length; ++i) { LK.effects.flashObject(f.fenceNodes[i], 0x00ff00, 600); } } else { // Not enough money, flash red LK.effects.flashScreen(0xff0000, 400); } } // --- Touch/mouse handling --- var lastDownX = 0, lastDownY = 0; var harvestMode = false; var harvestWheat = null; // Helper: find harvestable vegetable under (x, y) in unlocked fields function findHarvestableWheat(x, y) { for (var i = 0; i < fields.length; ++i) { var f = fields[i]; if (f.locked) continue; for (var j = 0; j < f.wheats.length; ++j) { var w = f.wheats[j]; if (w.isHarvestable()) { // Use bounding box for hit var wx = w.x, wy = w.y; var ww = 60, wh = 80; if (x >= wx - ww / 2 && x <= wx + ww / 2 && y >= wy - wh && y <= wy) { return w; } } } } return null; } // Helper: find locked field under (x, y) function findLockedField(x, y) { for (var i = 0; i < fields.length; ++i) { var f = fields[i]; if (!f.locked) continue; // Use field area var pos = fieldPositions[i]; var fx = offsetX + pos.x; var fy = offsetY + pos.y; if (x >= fx && x <= fx + FIELD_W && y >= fy && y <= fy + FIELD_H) { return i; } } return -1; } // --- Game event handlers --- // Move farmer or harvest wheat game.down = function (x, y, obj) { // If UI handled this click, do nothing for game movement. if (playerMovementBlockedByUIClick) { playerMovementBlockedByUIClick = false; // Reset flag for next input return; } // Shop system completely removed // No shopButton references needed anymore // Continue with normal game control flow // No shop button checks needed // lastDownX = x; lastDownY = y; // Check if tapping on locked field unlock button var lockedIdx = findLockedField(x, y); if (lockedIdx >= 0) { var f = fields[lockedIdx]; // If tap is near lock icon or unlock text var centerX = offsetX + fieldPositions[lockedIdx].x + FIELD_W / 2; var centerY = offsetY + fieldPositions[lockedIdx].y + FIELD_H / 2; var dist = Math.sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)); if (dist < 120) { tryUnlockField(lockedIdx); return; } } // Check for wheat var w = findHarvestableWheat(x, y); if (w) { harvestMode = true; harvestWheat = w; // Change player to face forward during harvest // Switch to player_down for harvesting if (farmer.currentAsset) { farmer.currentAsset.alpha = 0; } var playerDown = LK.getAsset('player_down', { anchorX: 0.5, anchorY: 0.5 }); playerDown.alpha = 1; farmer.addChild(playerDown); farmer.currentAsset = playerDown; // Trigger sickle attack animation farmer.attack(); // Show harvest animation effect var harvestEffect = LK.getAsset('sprout', { anchorX: 0.5, anchorY: 0.5, x: w.x, y: w.y - 50, alpha: 0.8, scaleX: 2, scaleY: 2 }); game.addChild(harvestEffect); // Animate the harvest effect tween(harvestEffect, { alpha: 0, y: harvestEffect.y - 100 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { harvestEffect.destroy(); } }); // Harvest w.setHarvested(true); var reward = 100 * (FIELD_REWARD_MULT[w.fieldIndex] || 1); money += reward; updateMoneyDisplay(); // Flash wheat LK.effects.flashObject(w, 0xffff00, 300); // No setHarvested(false) here; regrow/removal handled in Wheat.update harvestMode = false; harvestWheat = null; return; } // Move farmer with animation farmer.moveTo(x, y); dragging = true; dragOffsetX = x - farmer.x; dragOffsetY = y - farmer.y; }; // Drag farmer game.move = function (x, y, obj) { // If UI interaction blocked the start of a drag, this helps. if (playerMovementBlockedByUIClick) { // Flag will be reset by the next 'down' event, or could be reset here if necessary, // but typically 'move' follows a 'down' that wasn't blocked. return; } // Shop system completely removed // if (dragging) { farmer.moveTo(x - dragOffsetX, y - dragOffsetY); } if (harvestMode && harvestWheat) { sickle.x = x; sickle.y = y; } }; // End drag/harvest game.up = function (x, y, obj) { // If UI interaction blocked the 'down' event, 'up' should also be controlled. if (playerMovementBlockedByUIClick) { playerMovementBlockedByUIClick = false; // Reset flag here as well return; } // Shop system completely removed // // dragging = false; harvestMode = false; harvestWheat = null; }; // --- Main update loop --- // Defensive: If game is reset, clear win timer game.update = function () { // Update farmer farmer.update(); // Update wheat regrow for (var i = 0; i < fields.length; ++i) { var f = fields[i]; f.wheats.forEach(function (wheat) { wheat.update(); }); } // Update clouds for (var i = 0; i < clouds.length; i++) { clouds[i].update(); } // Update birds for (var i = 0; i < birds.length; i++) { birds[i].update(); } }; // --- Create areas and place animals --- // Calculate center of game screen var gameWidth = 2048; var gameHeight = 2732; var gameCenterX = gameWidth / 2; var gameCenterY = gameHeight / 2; // Create the four areas at equal distances from center // Area_Left var areaLeft = new Container(); areaLeft.x = gameCenterX - AREA_DISTANCE; areaLeft.y = gameCenterY; areaLeft.name = "Area_Left"; game.addChild(areaLeft); areas.push(areaLeft); // Area_Right var areaRight = new Container(); areaRight.x = gameCenterX + AREA_DISTANCE; areaRight.y = gameCenterY; areaRight.name = "Area_Right"; game.addChild(areaRight); areas.push(areaRight); // Area_Top var areaTop = new Container(); areaTop.x = gameCenterX; areaTop.y = gameCenterY - AREA_DISTANCE; areaTop.name = "Area_Top"; game.addChild(areaTop); areas.push(areaTop); // Area_Bottom var areaBottom = new Container(); areaBottom.x = gameCenterX; areaBottom.y = gameCenterY + AREA_DISTANCE; areaBottom.name = "Area_Bottom"; game.addChild(areaBottom); areas.push(areaBottom); // Place animals in the areas // Animal_Right at bottom right of screen var animalRight = new Animal(); animalRight.setAnimalImage('animal_right'); animalRight.name = "Animal_Right"; // Place at bottom right, accounting for anchor (0.5, 0.5) and asset size var animalRightAsset = LK.getAsset('animal_right', { anchorX: 0.5, anchorY: 0.5 }); animalRight.x = gameWidth - animalRightAsset.width / 2 - 10; // 10px padding from right animalRight.y = gameHeight - animalRightAsset.height / 2 - 10; // 10px padding from bottom game.addChild(animalRight); animals.push(animalRight); // Area_Bottom has no animal // --- Create clouds --- var clouds = []; // Create clouds at different positions with increased delay function createClouds() { // Reuse existing clouds or create new ones if pool is empty var cloud; if (clouds.length > 0) { cloud = clouds.pop(); cloud.alpha = 1; // Reset alpha for visibility cloud.visible = true; // Ensure cloud is visible cloud.init(); // Reinitialize cloud properties } else { cloud = new Cloud(); game.addChild(cloud); cloud.init(); // Initialize cloud properties } // Cloud width for calculations - cached outside for performance var cloudWidth = 500; cloud.x = -cloudWidth / 2; // Start from off-screen left cloud.y = 180; // Higher position near the top cloud.layer = 0; // Front layer - this will be in front cloud.init(); // Initialize movement and timing clouds.push(cloud); // Schedule creation of new clouds after these disappear - extended time LK.setTimeout(createClouds, 180000); // 180 seconds to spread out cloud creation more } // Start creating clouds createClouds(); // Shop system completely removed // --- Create zombie --- var zombie = null; var zombieDamageTimer = 0; // Timer to prevent continuous damage var zombieDamageAmount = 500; // Initial zombie damage/cost var zombieAppearCount = 0; // How many times zombie has appeared // --- Create birds --- var birds = []; // Create a bird that starts at the top, flies across, disappears, and restarts in a loop function createBird() { // Avoid overlapping timers - clear any existing if (window._birdCreationTimer) { LK.clearTimeout(window._birdCreationTimer); window._birdCreationTimer = null; } // Reuse existing birds or create new ones if pool is empty var bird; if (birds.length > 0) { bird = birds.pop(); bird.alpha = 1; // Reset alpha for visibility bird.visible = true; // Ensure bird is visible bird.init(); // Reinitialize bird properties } else { bird = new Bird(); game.addChild(bird); bird.init(); // Initialize bird properties } // Cache bird dimensions for better performance var birdWidth = 350; // Use known width instead of creating temporary asset var birdHeight = 350; // Use known height bird.x = -birdWidth / 2; bird.y = birdHeight / 2 + 20; // 20px padding from the very top bird.init(); birds.push(bird); // Simple flag to prevent duplicate scheduling bird._nextBirdScheduled = false; // Optimized check function that runs less frequently bird._birdLoopCheck = function () { // Only schedule new bird once if this one is gone if ((!bird.parent || bird.alpha <= 0) && !bird._nextBirdScheduled) { bird._nextBirdScheduled = true; // Create new bird after a longer delay to reduce processing frequency window._birdCreationTimer = LK.setTimeout(createBird, 240000); // 240 seconds (4 min) delay for less frequent processing } else if (bird.parent && bird.alpha > 0) { // Check much less frequently to reduce timer overhead LK.setTimeout(bird._birdLoopCheck, 30000); // Check every 30 seconds instead of 15 } }; // Start the check after a longer delay LK.setTimeout(bird._birdLoopCheck, 30000); } // Start creating birds after 1 minute (60000 ms) LK.setTimeout(createBird, 60000); // Play background music in a loop LK.playMusic('backgroundMusic', { loop: true }); // --- Initial money display --- updateMoneyDisplay(); // --- Place trees at corners --- // Tree size var treeWidth = 400; var treeHeight = 532; // Top left corner tree var topLeftTree = LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: offsetX / 2, y: offsetY / 2 }); game.addChild(topLeftTree); // Top right corner tree var topRightTree = LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: 2048 - offsetX / 2, y: offsetY / 2 }); game.addChild(topRightTree); // Bottom left corner tree var bottomLeftTree = LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: offsetX / 2, y: 2732 - offsetY / 2 }); game.addChild(bottomLeftTree); // Bottom right corner tree var bottomRightTree = LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: 2048 - offsetX / 2, y: 2732 - offsetY / 2 }); game.addChild(bottomRightTree); // --- Place trees at middle sides aligned with corner trees --- // Tree to the left of center var leftCenterTree = LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: offsetX / 2, y: topRightTree.y }); game.addChild(leftCenterTree); // Tree to the right of center var rightCenterTree = LK.getAsset('tree', { anchorX: 0.5, anchorY: 0.5, x: 2048 - offsetX / 2, y: topRightTree.y }); game.addChild(rightCenterTree); // --- Add Rooster that moves left-to-right, disappears at right edge, reappears after 40s and repeats --- var rooster = null; var roosterTimeout = null; function spawnRooster() { // Remove any existing rooster - with robust null check if (rooster) { // Only try to destroy if rooster exists and has a parent if (rooster && rooster.parent) { try { rooster.destroy(); } catch (e) { console.log("Error destroying rooster:", e); } } rooster = null; } rooster = new Rooster(); // Start at the left center of the screen rooster.y = 2732 / 2; rooster.x = rooster.roosterFrames[0].width / 2 + 10; game.addChild(rooster); // Rooster path: left-to-right, horizontally centered var roosterStartX = rooster.roosterFrames[0].width / 2 + 10; var roosterEndX = 2048 - rooster.roosterFrames[0].width / 2 - 10; var roosterSpeed = 1; // px per frame, very slow movement rooster.targetX = roosterEndX; rooster.direction = 1; // 1: moving right rooster.updateRoosterMovement = function () { // Track lastX for edge detection if (typeof rooster.lastX === "undefined") rooster.lastX = rooster.x; // Move only horizontally in the center var dx = rooster.targetX - rooster.x; var dist = Math.abs(dx); if (dist < roosterSpeed) { rooster.x = rooster.targetX; } else { rooster.x += roosterSpeed * (dx > 0 ? 1 : -1); } // Call animation update if (typeof rooster.update === "function") { rooster.update(); } // Check if rooster just reached the right edge (disappear trigger) if (rooster.lastX < roosterEndX && rooster.x >= roosterEndX) { // Fade out and destroy, then respawn after 40s tween(rooster, { alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { if (rooster && rooster.parent) { rooster.destroy(); } roosterTimeout = LK.setTimeout(spawnRooster, 40000); // 40 seconds } }); } // Update lastX for next frame rooster.lastX = rooster.x; }; } // Add rooster update to main game loop var oldGameUpdate = game.update; game.update = function () { if (typeof oldGameUpdate === "function") oldGameUpdate(); if (rooster && rooster.parent && typeof rooster.updateRoosterMovement === "function") { rooster.updateRoosterMovement(); } // Update zombie if (zombie && zombie.parent) { zombie.update(); // Collision detection with player (only triggers on state change) var isIntersecting = zombie.intersects(farmer); // Check if we just started intersecting if (!zombie.lastWasIntersecting && isIntersecting && zombieDamageTimer <= 0) { // Zombie just touched the player, deduct gold money = Math.max(0, money - zombieDamageAmount); updateMoneyDisplay(); // Flash both zombie and farmer red LK.effects.flashObject(zombie, 0xff0000, 500); LK.effects.flashObject(farmer, 0xff0000, 500); // Set cooldown timer to prevent continuous damage (3 second cooldown) zombieDamageTimer = 180; } // Update intersection state zombie.lastWasIntersecting = isIntersecting; // Update damage cooldown timer if (zombieDamageTimer > 0) { zombieDamageTimer--; } } storage.farmerX = farmer.x; storage.farmerY = farmer.y; storage.money = money; }; // Create the zombie function createZombie() { // Reuse existing zombie or create new one if pool is empty if (!zombie) { zombie = new Zombie(); game.addChild(zombie); } else { zombie.alpha = 1; zombie.visible = true; zombie.isDead = false; zombie.init(); // Reinitialize zombie properties } // Reset zombie properties for reuse zombie.alpha = 1; zombie.isDead = false; // Position zombie off-screen to the left zombie.x = -100; zombie.y = 2732 / 2; // Initialize tracking variables zombie.lastX = zombie.x; zombie.lastY = zombie.y; zombie.lastWasIntersecting = false; // Increase zombie damage each time it spawns zombieAppearCount++; if (zombieAppearCount === 1) { zombieDamageAmount = 500; } else { zombieDamageAmount = zombieDamageAmount * 2; } // Schedule next zombie creation with increased delay LK.setTimeout(createZombie, 120000); // 120 seconds delay for next zombie } // Start the first rooster and create zombie spawnRooster(); // Delay the first zombie spawn by 40 seconds after game start LK.setTimeout(createZombie, 40000); // Zombie respawn is now handled after being killed by the player; // --- Time and Season System --- // --- Day and Season Variables --- var currentDay = storage.currentDay || 1; var currentSeason = storage.currentSeason || "Summer"; var SEASONS = ["Summer", "Autumn"]; var seasonIndex = SEASONS.indexOf(currentSeason); if (seasonIndex === -1) seasonIndex = 0; var autumnColors = [0xC97A3A, 0xB94E2A, 0xA65C1B, 0xE07B39, 0xD2691E, 0xB22222, 0x8B4513]; var autumnLeafAssets = []; // Will hold leaf particles var autumnLeavesActive = false; var autumnCropSlowdown = 2; // Crops grow 2x slower in autumn var autumnNightStartHour = 18; // Night starts earlier (not implemented, placeholder) var autumnGrassColor = 0xB97A56; // Dry grass color // --- UI Elements for Day and Season --- var dayTxt = new Text2('Day ' + currentDay, { size: 80, fill: "#fff" }); dayTxt.anchor.set(1, 0); dayTxt.x = 2048 - 40; // Right side, 40px padding dayTxt.y = 40; // Top, 40px padding game.addChild(dayTxt); var seasonTxt = new Text2(SEASONS[seasonIndex], { size: 80, fill: "#fff" }); seasonTxt.anchor.set(1, 0); seasonTxt.x = 2048 - 40; // Right side, 40px padding seasonTxt.y = 120; // Below day text game.addChild(seasonTxt); // --- Autumn Effects: Falling Leaves --- function spawnAutumnLeaves() { if (autumnLeavesActive) return; autumnLeavesActive = true; // Remove any old leaves for (var i = 0; i < autumnLeafAssets.length; ++i) { if (autumnLeafAssets[i] && autumnLeafAssets[i].parent) { try { autumnLeafAssets[i].destroy(); } catch (e) {} } } autumnLeafAssets = []; // Spawn 20 leaves at random positions at the top for (var i = 0; i < 20; ++i) { var color = autumnColors[Math.floor(Math.random() * autumnColors.length)]; var leaf = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.25 + Math.random() * 0.2, scaleY: 0.12 + Math.random() * 0.1, tint: color, alpha: 0.7 + Math.random() * 0.3, x: Math.random() * 2048, y: -30 - Math.random() * 100 }); leaf._fallSpeed = 1.5 + Math.random() * 1.5; leaf._drift = (Math.random() - 0.5) * 2; leaf._spin = (Math.random() - 0.5) * 0.1; game.addChild(leaf); autumnLeafAssets.push(leaf); } } // --- Remove Autumn Leaves --- function removeAutumnLeaves() { autumnLeavesActive = false; for (var i = 0; i < autumnLeafAssets.length; ++i) { if (autumnLeafAssets[i] && autumnLeafAssets[i].parent) { try { autumnLeafAssets[i].destroy(); } catch (e) {} } } autumnLeafAssets = []; } // --- Update Autumn Leaves --- function updateAutumnLeaves() { if (!autumnLeavesActive) return; for (var i = 0; i < autumnLeafAssets.length; ++i) { var leaf = autumnLeafAssets[i]; if (!leaf) continue; leaf.y += leaf._fallSpeed; leaf.x += leaf._drift; leaf.rotation += leaf._spin; // Respawn leaf at top if it falls below screen if (leaf.y > 2732 + 30) { leaf.y = -30 - Math.random() * 100; leaf.x = Math.random() * 2048; leaf._fallSpeed = 1.5 + Math.random() * 1.5; leaf._drift = (Math.random() - 0.5) * 2; leaf._spin = (Math.random() - 0.5) * 0.1; } } } // --- Crop Growth Slowdown Helper --- function getCropGrowthModifier() { return SEASONS[seasonIndex] === "Autumn" ? autumnCropSlowdown : 1; } // --- Update Day and Season --- function updateDayAndSeason() { currentDay++; // Change season every 3 days if (currentDay > 3) { currentDay = 1; seasonIndex = (seasonIndex + 1) % SEASONS.length; currentSeason = SEASONS[seasonIndex]; // Visual changes for each season if (currentSeason === "Autumn") { game.setBackgroundColor(autumnGrassColor); spawnAutumnLeaves(); } else { // Summer: restore green grass, remove leaves game.setBackgroundColor(0x4caf50); removeAutumnLeaves(); } } dayTxt.setText('Day ' + currentDay); seasonTxt.setText(currentSeason); storage.currentDay = currentDay; storage.currentSeason = currentSeason; } // --- Apply Crop Growth Slowdown in Autumn --- // Patch all crop regrowTime on season change function applySeasonalCropGrowth() { for (var i = 0; i < fields.length; ++i) { var f = fields[i]; for (var j = 0; j < f.wheats.length; ++j) { var crop = f.wheats[j]; if (typeof crop._baseRegrowTime === "undefined") { crop._baseRegrowTime = crop.regrowTime; } crop.regrowTime = crop._baseRegrowTime * getCropGrowthModifier(); } } } // --- Patch updateDayAndSeason to also update crop growth --- var _oldUpdateDayAndSeason = updateDayAndSeason; updateDayAndSeason = function updateDayAndSeason() { _oldUpdateDayAndSeason(); applySeasonalCropGrowth(); }; // --- Initial Season Visuals and Crop Growth --- if (currentSeason === "Autumn") { game.setBackgroundColor(autumnGrassColor); spawnAutumnLeaves(); } else { game.setBackgroundColor(0x4caf50); removeAutumnLeaves(); } applySeasonalCropGrowth(); // --- Set interval to update day and season every minute (for testing) --- LK.setInterval(updateDayAndSeason, 60000); // --- Patch main game update to animate autumn leaves --- var _oldGameUpdateForSeason = game.update; game.update = function () { if (typeof _oldGameUpdateForSeason === "function") _oldGameUpdateForSeason(); updateAutumnLeaves(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Removed plugin import to fix 'Unable to load plugins' error
var Animal = Container.expand(function () {
var self = Container.call(this);
self.animalAsset = null;
self.setAnimalImage = function (imageId) {
if (self.animalAsset) {
self.animalAsset.destroy();
}
self.animalAsset = self.attachAsset(imageId, {
anchorX: 0.5,
anchorY: 0.5
});
};
return self;
});
// Bird class
var Bird = Container.expand(function () {
var self = Container.call(this);
// Create the bird asset
var birdAsset = self.attachAsset('Bird', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1,
scaleX: 0.8,
scaleY: 0.8
});
// Variables for movement
self.speed = 1.0; // Faster than clouds
self.lifeTime = 3000; // 50 seconds at 60fps
self.lifeCounter = 0; // Track how long bird has been visible
self.flyHeight = 0; // Vertical position offset for flying pattern
// Initialize bird
self.init = function () {
self.lifeCounter = 0;
self.alpha = 1;
// Start movement animation using tween
self.startMoving();
};
// Start the left to right movement with slight up/down wave pattern
self.startMoving = function () {
// Move bird to the right side of screen
var targetX = 2048 + birdAsset.width / 2; // Move past right edge
// Create the movement tween
tween(self, {
x: targetX
}, {
duration: 20000,
// Faster than clouds - 20 seconds to cross screen
easing: tween.linear
});
};
// Update function called every frame - optimized
self.update = function () {
// Early return if invisible or not visible - prevents unnecessary calculations
if (self.alpha <= 0 || !self.visible) return;
// Increment life counter first for potential early returns
self.lifeCounter++;
// Only check edge crossing if we're near the edge - optimization
var edgeX = 2048 + birdAsset.width / 2;
if (self.x >= edgeX) {
// Stop any existing tweens on this bird
tween.stop(self);
// Fade out animation
tween(self, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove bird from game
self.visible = false; // Hide instead of destroy
birds.push(self); // Return to pool
}
});
return; // Early return after starting fade out animation
}
// Create a small wavy flight pattern - only for visible, active birds
self.y = self.y + Math.sin(self.lifeCounter * 0.03) * 1.5;
};
return self;
});
// CarrotSlot class for area 2
var CarrotSlot = Container.expand(function () {
var self = Container.call(this);
self.slotAsset = self.attachAsset('carrot', {
anchorX: 0.5,
anchorY: 1
});
var sproutAsset = self.attachAsset('sprout', {
anchorX: 0.5,
anchorY: 1,
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
});
self.harvested = false;
self.regrowTime = 1800; // 30 seconds at 60fps
self.regrowCounter = 0;
self.fieldIndex = 1; // Area 2
self.regrowing = false;
// Show/hide carrot and handle regrow animation
self.setHarvested = function (harvested) {
self.harvested = harvested;
if (harvested) {
// Hide carrot with animation
tween(self.slotAsset, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
// Show sprout with animation
sproutAsset.alpha = 1;
sproutAsset.scaleX = 0.1;
sproutAsset.scaleY = 0.1;
// Animate sprout appearing
tween(sproutAsset, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
self.regrowCounter = self.regrowTime;
self.regrowing = true;
} else {
// Show carrot with animation
tween(self.slotAsset, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
// Hide sprout with animation
tween(sproutAsset, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
self.regrowing = false;
}
};
// Regrow logic
self.update = function () {
if (self.harvested && self.regrowing) {
if (self.regrowCounter > 0) {
// Animate sprout growing to carrot size
var t = 1 - self.regrowCounter / self.regrowTime;
sproutAsset.scaleX = 0.5 + 0.5 * t;
sproutAsset.scaleY = 0.5 + 0.5 * t;
self.regrowCounter--;
}
if (self.regrowCounter <= 0) {
// Sprout is now full size, switch back to carrot and allow harvest again
sproutAsset.scaleX = 1;
sproutAsset.scaleY = 1;
self.setHarvested(false);
}
}
};
// For hit detection
self.isHarvestable = function () {
return !self.harvested;
};
return self;
});
// Cloud class
var Cloud = Container.expand(function () {
var self = Container.call(this);
// Create the cloud asset
var cloudAsset = self.attachAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.8 // Semi-transparent clouds
});
// Variables for movement
self.speed = 0.2; // Very slow speed for smooth movement
self.lifeTime = 6000; // 100 seconds at 60fps - longer lifetime
self.lifeCounter = 0; // Track how long cloud has been visible
self.layer = 0; // Layer for visual depth (0 = front, 1 = back)
// Initialize cloud
self.init = function () {
self.lifeCounter = 0;
self.alpha = 1;
// Apply layer-specific settings
if (self.layer === 1) {
// Back layer cloud (slightly smaller and more transparent)
self.scale.set(0.8, 0.8);
self.alpha = 0.7;
}
// Start movement animation using tween
self.startMoving();
};
// Start the left to right movement only
self.startMoving = function () {
// Move cloud to the right side of screen
var targetX = 2048 + cloudAsset.width / 2; // Move past right edge
// Create the movement tween
tween(self, {
x: targetX
}, {
duration: 60000,
// Slower movement - 60 seconds to cross screen
easing: tween.linear
});
};
// Update function called every frame - optimized
self.update = function () {
// Early return if invisible or not visible - saves processing
if (self.alpha <= 0 || !self.visible) return;
// Track lastX for edge detection - initialize once if undefined
if (typeof self.lastX === "undefined") self.lastX = self.x;
// Only check edge crossing if we're near the edge - optimization
var edgeX = 2048 + cloudAsset.width / 2;
if (self.x >= edgeX && self.lastX < edgeX) {
// Stop any existing tweens on this cloud to prevent conflicts
tween.stop(self);
// Fade out animation
tween(self, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove cloud from game
self.visible = false; // Hide instead of destroy
clouds.push(self); // Return to pool
}
});
}
// Update lastX for next frame
self.lastX = self.x;
};
return self;
});
// EggplantSlot class for area 4
var EggplantSlot = Container.expand(function () {
var self = Container.call(this);
self.slotAsset = self.attachAsset('eggplant', {
anchorX: 0.5,
anchorY: 1
});
var sproutAsset = self.attachAsset('sprout', {
anchorX: 0.5,
anchorY: 1,
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
});
self.harvested = false;
self.regrowTime = 1800; // 30 seconds at 60fps
self.regrowCounter = 0;
self.fieldIndex = 3; // Area 4
self.regrowing = false;
// Show/hide eggplant and handle regrow animation
self.setHarvested = function (harvested) {
self.harvested = harvested;
if (harvested) {
// Hide eggplant with animation
tween(self.slotAsset, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
// Show sprout with animation
sproutAsset.alpha = 1;
sproutAsset.scaleX = 0.1;
sproutAsset.scaleY = 0.1;
// Animate sprout appearing
tween(sproutAsset, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
self.regrowCounter = self.regrowTime;
self.regrowing = true;
} else {
// Show eggplant with animation
tween(self.slotAsset, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
// Hide sprout with animation
tween(sproutAsset, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
self.regrowing = false;
}
};
// Regrow logic
self.update = function () {
if (self.harvested && self.regrowing) {
if (self.regrowCounter > 0) {
// Animate sprout growing to eggplant size
var t = 1 - self.regrowCounter / self.regrowTime;
sproutAsset.scaleX = 0.5 + 0.5 * t;
sproutAsset.scaleY = 0.5 + 0.5 * t;
self.regrowCounter--;
}
if (self.regrowCounter <= 0) {
// Sprout is now full size, switch back to eggplant and allow harvest again
sproutAsset.scaleX = 1;
sproutAsset.scaleY = 1;
self.setHarvested(false);
}
}
};
// For hit detection
self.isHarvestable = function () {
return !self.harvested;
};
return self;
});
// Farmer class
var Farmer = Container.expand(function () {
var self = Container.call(this);
// Create animation frames
var idleFrame = self.attachAsset('farmer', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
// Frame 2 (running)
var runningFrame = self.attachAsset('farmer_walk', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Frame 3 (right step)
var rightStepFrame = self.attachAsset('farmer_walk', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
rotation: 0.1
});
self.speed = 18; // px per move
self.targetX = self.x;
self.targetY = self.y;
self.moving = false;
self.lastMoving = false;
self.animationTimer = 0;
self.animationFrame = 0;
self.frameTime = 18; // 0.3 seconds at 60fps
// Animation frame switching
self.switchToFrame = function (frameNum) {
idleFrame.alpha = 0;
runningFrame.alpha = 0;
rightStepFrame.alpha = 0;
if (frameNum === 0) {
// Idle
idleFrame.alpha = 1;
} else if (frameNum === 1) {
// Running
runningFrame.alpha = 1;
} else if (frameNum === 2) {
// Right step
rightStepFrame.alpha = 1;
}
};
// Move farmer towards target
self.update = function () {
// Track movement direction and update image
if (self.moving) {
var dx = self.targetX - self.x;
// Check if moving right or left
if (dx > 0) {
// Moving right - use player_right asset
idleFrame.scaleX = 1;
runningFrame.scaleX = 1;
rightStepFrame.scaleX = 1;
// Switch to player_right image
if (self.currentAsset !== idleFrame || idleFrame.texture !== LK.getAsset('player_right', {}).texture) {
if (self.currentAsset) {
self.currentAsset.alpha = 0;
}
var rightAsset = LK.getAsset('player_right', {
anchorX: 0.5,
anchorY: 0.5
});
rightAsset.alpha = 1;
self.addChild(rightAsset);
self.currentAsset = rightAsset;
}
} else if (dx < 0) {
// Moving left - use player_left asset
idleFrame.scaleX = -1;
runningFrame.scaleX = -1;
rightStepFrame.scaleX = -1;
// Switch to player_left image
if (self.currentAsset !== idleFrame || idleFrame.texture !== LK.getAsset('player_left', {}).texture) {
if (self.currentAsset) {
self.currentAsset.alpha = 0;
}
var leftAsset = LK.getAsset('player_left', {
anchorX: 0.5,
anchorY: 0.5
});
leftAsset.alpha = 1;
self.addChild(leftAsset);
self.currentAsset = leftAsset;
}
}
// Animation handling
self.animationTimer++;
if (self.animationTimer >= self.frameTime) {
self.animationTimer = 0;
self.animationFrame = self.animationFrame === 1 ? 2 : 1; // Toggle between frames 1 and 2
self.switchToFrame(self.animationFrame);
}
} else if (!self.moving && self.lastMoving) {
// Just stopped moving - switch to idle
self.switchToFrame(0);
}
self.lastMoving = self.moving;
if (!self.moving) return;
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.speed) {
self.x = self.targetX;
self.y = self.targetY;
self.moving = false;
// Switch to idle frame when stopping
self.switchToFrame(0);
} else {
self.x += self.speed * dx / dist;
self.y += self.speed * dy / dist;
}
};
// Set movement target
self.moveTo = function (x, y) {
self.targetX = x;
self.targetY = y;
// Start movement
if (!self.moving) {
self.moving = true;
self.animationFrame = 1;
self.animationTimer = 0;
self.switchToFrame(self.animationFrame);
}
};
// Attach sickle to farmer's hand
var sickle = self.attachAsset('sickle', {
anchorX: 0.2,
// hand position, left side of ellipse
anchorY: 0.7,
// slightly below center
x: 0,
y: 30
});
sickle.alpha = 1;
// Add attack animation for sickle
self.attack = function () {
tween(sickle, {
rotation: Math.PI * 2
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
sickle.rotation = 0;
}
});
};
return self;
});
// Field class
var Field = Container.expand(function () {
var self = Container.call(this);
self.index = 0; // 0-3
self.locked = false;
self.lockCost = 0;
self.lockNode = null;
self.fenceNodes = [];
self.wheats = [];
self.unlockBtn = null;
// Draw fences - Empty implementation to eliminate fences
self.drawFences = function (x, y, w, h) {
self.fenceNodes = [];
// No fences or gates are created
};
// Place wheat
self.placeWheats = function (x, y, w, h, count) {
self.wheats = [];
// Define grid size
var rows = 5;
var cols = 10;
// Choose vegetable asset per area
var vegAssetId = 'wheat';
if (self.index === 1) vegAssetId = 'carrot';
if (self.index === 2) vegAssetId = 'tomato';
if (self.index === 3) vegAssetId = 'eggplant';
// Fallback if asset not found, use wheat
if (!LK.assets || !LK.assets[vegAssetId]) vegAssetId = 'wheat';
// Calculate asset size for spacing (use wheat size as reference for all)
var vegAsset = LK.getAsset('wheat', {
anchorX: 0.5,
anchorY: 1
});
var wheatW = vegAsset.width;
var wheatH = vegAsset.height;
// Padding from fences
var padX = 60;
var padY = 60;
// Compute available area for wheat grid
var gridW = w - 2 * padX;
var gridH = h - 2 * padY;
// Compute spacing between wheat plants
var spacingX = (gridW - wheatW) / (cols - 1);
var spacingY = (gridH - wheatH) / (rows - 1);
// Place appropriate vegetable in grid based on field index
for (var row = 0; row < rows; ++row) {
for (var col = 0; col < cols; ++col) {
var wx = x + padX + col * spacingX + wheatW / 2;
var wy = y + padY + row * spacingY + wheatH;
var veg;
// Create appropriate vegetable based on area/field index
if (self.index === 1) {
veg = new CarrotSlot();
} else if (self.index === 2) {
veg = new TomatoSlot();
} else if (self.index === 3) {
veg = new EggplantSlot();
} else {
veg = new Wheat();
veg.fieldIndex = self.index;
veg.vegAssetId = vegAssetId;
veg.setVegAsset && veg.setVegAsset(vegAssetId);
}
veg.x = wx;
veg.y = wy;
self.addChild(veg);
self.wheats.push(veg);
}
}
};
// Lock overlay
self.showLock = function (centerX, centerY) {
if (self.lockNode) return;
self.lockNode = LK.getAsset('lock', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY
});
self.addChild(self.lockNode);
// Unlock text
var unlockTxt = new Text2('Unlock\n$' + self.lockCost, {
size: 60,
fill: "#fff"
});
unlockTxt.anchor.set(0.5, 0.5);
unlockTxt.x = centerX;
unlockTxt.y = centerY + 70;
self.unlockBtn = unlockTxt;
self.addChild(unlockTxt);
};
self.hideLock = function () {
if (self.lockNode) {
self.lockNode.destroy();
self.lockNode = null;
}
if (self.unlockBtn) {
self.unlockBtn.destroy();
self.unlockBtn = null;
}
};
return self;
});
// Rooster class with 4-frame animation cycling smoothly
var Rooster = Container.expand(function () {
var self = Container.call(this);
// Animation frame asset ids
var frameIds = ['rooster_1', 'rooster_2', 'rooster_3', 'rooster_4'];
self.roosterFrames = [];
self.currentFrame = 0;
self.frameTimer = 0;
self.frameInterval = 10; // ~0.16s per frame at 60fps (smooth)
// Attach all frames, only show the first
for (var i = 0; i < frameIds.length; ++i) {
var frame = self.attachAsset(frameIds[i], {
anchorX: 0.5,
anchorY: 0.5,
alpha: i === 0 ? 1 : 0
});
self.roosterFrames.push(frame);
}
// Animation update
self.update = function () {
self.frameTimer++;
if (self.frameTimer >= self.frameInterval) {
// Hide current frame
self.roosterFrames[self.currentFrame].alpha = 0;
// Advance to next frame
self.currentFrame = (self.currentFrame + 1) % self.roosterFrames.length;
// Show new frame
self.roosterFrames[self.currentFrame].alpha = 1;
self.frameTimer = 0;
}
};
return self;
});
// TomatoSlot class for area 3
var TomatoSlot = Container.expand(function () {
var self = Container.call(this);
self.slotAsset = self.attachAsset('tomato', {
anchorX: 0.5,
anchorY: 1
});
var sproutAsset = self.attachAsset('sprout', {
anchorX: 0.5,
anchorY: 1,
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
});
self.harvested = false;
self.regrowTime = 1800; // 30 seconds at 60fps
self.regrowCounter = 0;
self.fieldIndex = 2; // Area 3
self.regrowing = false;
// Show/hide tomato and handle regrow animation
self.setHarvested = function (harvested) {
self.harvested = harvested;
if (harvested) {
// Hide tomato with animation
tween(self.slotAsset, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
// Show sprout with animation
sproutAsset.alpha = 1;
sproutAsset.scaleX = 0.1;
sproutAsset.scaleY = 0.1;
// Animate sprout appearing
tween(sproutAsset, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
self.regrowCounter = self.regrowTime;
self.regrowing = true;
} else {
// Show tomato with animation
tween(self.slotAsset, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
// Hide sprout with animation
tween(sproutAsset, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
self.regrowing = false;
}
};
// Regrow logic
self.update = function () {
if (self.harvested && self.regrowing) {
if (self.regrowCounter > 0) {
// Animate sprout growing to tomato size
var t = 1 - self.regrowCounter / self.regrowTime;
sproutAsset.scaleX = 0.5 + 0.5 * t;
sproutAsset.scaleY = 0.5 + 0.5 * t;
self.regrowCounter--;
}
if (self.regrowCounter <= 0) {
// Sprout is now full size, switch back to tomato and allow harvest again
sproutAsset.scaleX = 1;
sproutAsset.scaleY = 1;
self.setHarvested(false);
}
}
};
// For hit detection
self.isHarvestable = function () {
return !self.harvested;
};
return self;
});
// Wheat class
var Wheat = Container.expand(function () {
var self = Container.call(this);
self.vegAssetId = 'wheat';
self.wheatAsset = self.attachAsset('wheat', {
anchorX: 0.5,
anchorY: 1
});
var sproutAsset = self.attachAsset('sprout', {
anchorX: 0.5,
anchorY: 1,
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
});
self.harvested = false;
self.regrowTime = 1800; // 30 seconds at 60fps
self.regrowCounter = 0;
self.fieldIndex = 0; // which field this wheat belongs to
self.regrowing = false;
self.removeAfter = 0;
// Change vegetable asset if needed
self.setVegAsset = function (assetId) {
if (self.wheatAsset) {
self.wheatAsset.destroy();
}
self.vegAssetId = assetId || 'wheat';
// Only allow wheat, carrot, tomato, eggplant
var validAssets = {
wheat: 1,
carrot: 1,
tomato: 1,
eggplant: 1
};
if (!validAssets[self.vegAssetId]) self.vegAssetId = 'wheat';
self.wheatAsset = self.attachAsset(self.vegAssetId, {
anchorX: 0.5,
anchorY: 1
});
// Make sure sprout is on top
self.removeChild(sproutAsset);
self.addChild(sproutAsset);
};
// Show/hide wheat and handle regrow animation
self.setHarvested = function (harvested) {
self.harvested = harvested;
if (harvested) {
// Hide wheat, show sprout
if (self.wheatAsset) {
// Animate wheat harvesting with tween
tween(self.wheatAsset, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
}
// Show and animate sprout growing from nothing
sproutAsset.alpha = 1;
sproutAsset.scaleX = 0.1;
sproutAsset.scaleY = 0.1;
// Animate sprout appearing
tween(sproutAsset, {
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeOut
});
self.regrowCounter = self.regrowTime;
self.regrowing = true;
} else {
// Show wheat/veg, hide sprout with animation
if (self.wheatAsset) {
tween(self.wheatAsset, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeIn
});
}
// Fade out sprout
tween(sproutAsset, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
self.regrowing = false;
}
};
// Regrow logic and removal after 5 seconds - highly optimized
self.update = function () {
// Early return if not harvested or not regrowing - prevents unnecessary checks
if (!self.harvested || !self.regrowing) return;
if (self.regrowCounter > 0) {
// Optimize calculations by using cached values and only calculating once
var t = 1 - self.regrowCounter / self.regrowTime;
var newScale = 0.5 + 0.5 * t;
sproutAsset.scaleX = newScale;
sproutAsset.scaleY = newScale;
self.regrowCounter--;
} else {
// Sprout is now full size, switch back to wheat/veg and allow harvest again
sproutAsset.scaleX = 1;
sproutAsset.scaleY = 1;
self.setHarvested(false);
}
};
// For hit detection
self.isHarvestable = function () {
return !self.harvested;
};
return self;
});
var Zombie = Container.expand(function () {
var self = Container.call(this);
// Create the zombie frames (two different assets)
var zombieFrame1 = self.attachAsset('zombie_1', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1
});
var zombieFrame2 = self.attachAsset('zombie_2', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
// Animation variables
self.currentFrame = 0;
self.frameTimer = 0;
self.frameInterval = 15; // Frame switch interval
self.speed = 2; // Movement speed
self.lastX = 0; // Track previous X for collision detection
self.lastY = 0; // Track previous Y for collision detection
self.lastWasIntersecting = false; // Track previous intersection state
self.isDead = false; // Track if zombie is dead
// Handle click/tap on zombie
self.down = function (x, y, obj) {
// Mark zombie as clicked and initiate death sequence
if (!self.isDead) {
self.isDead = true;
// Flash zombie yellow to indicate hit
LK.effects.flashObject(self, 0xFFFF00, 300);
// Death animation - fade out and scale down
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove zombie from game
self.destroy();
// Create new zombie after a 40s delay
LK.setTimeout(createZombie, 40000);
}
});
// Prevent event from bubbling to game (stop farmer movement)
return true;
}
};
// Zombie animation and movement
self.update = function () {
// Don't update if dead or not visible
if (self.isDead || !self.visible) return;
// Save previous position for collision detection
self.lastX = self.x;
self.lastY = self.y;
// Frame animation
self.frameTimer++;
if (self.frameTimer >= self.frameInterval) {
// Toggle between frames
self.currentFrame = self.currentFrame === 0 ? 1 : 0;
// Show current frame
zombieFrame1.alpha = self.currentFrame === 0 ? 1 : 0;
zombieFrame2.alpha = self.currentFrame === 1 ? 1 : 0;
self.frameTimer = 0;
}
// Move zombie toward the farmer
if (farmer) {
var dx = farmer.x - self.x;
var dy = farmer.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > self.speed) {
self.x += self.speed * dx / dist;
self.y += self.speed * dy / dist;
}
}
// Check if zombie is off-screen and return to pool
if (self.x < -100 || self.x > 2048 + 100 || self.y < -100 || self.y > 2732 + 100) {
self.visible = false; // Hide instead of destroy
zombies.push(self); // Return to pool
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x4caf50 // Grass green
});
/****
* Game Code
****/
// Area 4
// Area 3
// Area 2
// Lock icon (ellipse, gray)
// Sickle (ellipse, yellow)
// House (box)
// Farmer (box)
// Wheat (ellipse)
// Field fence (rectangle)
// --- Game constants ---
// Rooster animation frames (replace id values with your own image ids)
var FIELD_W = 900;
var FIELD_H = 900;
var FIELD_GAP = 120; // Increased gap to move fences further from center
var FIELD_COUNT = 4;
var WHEAT_PER_FIELD = 10;
// Area unlock prices and reward multipliers
var FIELD_LOCK_COSTS = [0, 20000, 40000, 60000]; // Area 1 open, others locked
var FIELD_REWARD_MULT = [1, 2, 3, 4]; // Area 1=1x, 2=2x, 3=3x, 4=4x
// --- Area constants ---
// Define the distance from center for all areas (equal distance)
var AREA_DISTANCE = 600;
// Create an array to hold the areas
var areas = [];
var animals = [];
// --- Game state ---
var fields = [];
var farmer = null;
var money = storage.money || 0;
var moneyTxt = null;
var sickle = null;
var dragging = false;
var dragOffsetX = 0;
var dragOffsetY = 0;
var unlockFieldIndex = -1;
var playerMovementBlockedByUIClick = false; // Flag to indicate UI handled the click
// --- Layout calculation ---
// Fields: 2x2 grid, with house in center
// [0][1]
// [2][3]
var fieldPositions = [{
x: 0,
y: 0
},
// top-left
{
x: FIELD_W + FIELD_GAP,
y: 0
},
// top-right
{
x: 0,
y: FIELD_H + FIELD_GAP
},
// bottom-left
{
x: FIELD_W + FIELD_GAP,
y: FIELD_H + FIELD_GAP
} // bottom-right
];
// Center everything in game area
var totalW = FIELD_W * 2 + FIELD_GAP;
var totalH = FIELD_H * 2 + FIELD_GAP;
var offsetX = Math.floor((2048 - totalW) / 2);
var offsetY = Math.floor((2732 - totalH) / 2);
// --- Create fields ---
// Load unlocked fields from storage, default to [true, false, false, false] (only first field unlocked)
// Defensive: ensure unlockedFields is an array of length 4
var unlockedFields = storage.unlockedFields;
if (!Array.isArray(unlockedFields) || unlockedFields.length !== 4) {
unlockedFields = [true, false, false, false];
storage.unlockedFields = unlockedFields;
}
for (var i = 0; i < FIELD_COUNT; ++i) {
var f = new Field();
f.index = i;
// Use unlockedFields to determine locked state
f.locked = !unlockedFields[i];
f.lockCost = FIELD_LOCK_COSTS[i];
var pos = fieldPositions[i];
var fx = offsetX + pos.x;
var fy = offsetY + pos.y;
// Skip drawing fences
f.placeWheats(fx, fy, FIELD_W, FIELD_H, WHEAT_PER_FIELD);
if (f.locked) {
// Center of field
f.showLock(fx + FIELD_W / 2, fy + FIELD_H / 2);
}
game.addChild(f);
fields.push(f);
}
// Center reference point for positions
var centerX = offsetX + FIELD_W;
var centerY = offsetY + FIELD_H - 250;
// --- Create farmer ---
farmer = new Farmer();
farmer.x = storage.farmerX || centerX;
farmer.y = storage.farmerY || centerY - 120; // Start at center area
game.addChild(farmer);
// --- Create sickle (attached to farmer's hand) ---
sickle = LK.getAsset('sickle', {
anchorX: 0.2,
// hand position, left side of ellipse
anchorY: 0.7,
// slightly below center
x: 0,
y: 30
});
sickle.alpha = 1;
// --- Money display ---
moneyTxt = new Text2('$0', {
size: 100,
fill: "#fff"
});
moneyTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(moneyTxt);
// --- Information message on right side ---
// Create the info message text
var infoMsgTxt = new Text2('Game controls: Use mouse to move.\nFor mobile, tap to move.', {
size: 80,
fill: "#fff"
});
infoMsgTxt.anchor.set(1, 0.5); // Right align, vertically centered
// Position at right side, vertically centered (avoid top 100px for menu)
infoMsgTxt.x = 2048 - 40; // 40px padding from right edge
infoMsgTxt.y = 2732 / 2;
game.addChild(infoMsgTxt);
// Hide after 30 seconds (1800 frames at 60fps)
LK.setTimeout(function () {
if (infoMsgTxt && infoMsgTxt.parent) {
infoMsgTxt.destroy();
}
}, 30000);
// --- Missions UI ---
// "Missions" title
var missionsTitle = new Text2('Missions', {
size: 90,
fill: "#fff"
});
missionsTitle.anchor.set(0, 0); // Left align, top
// Place on the left side of the center part of the screen
var missionAreaX = 180; // 180px from left edge, safe from menu
var missionAreaY = 120; // Below top menu area
missionsTitle.x = missionAreaX;
missionsTitle.y = missionAreaY;
game.addChild(missionsTitle);
// Mission bar background (smaller, left side)
var missionBarBg = LK.getAsset('centerCircle', {
anchorX: 0,
anchorY: 0,
x: missionAreaX,
y: missionsTitle.y + missionsTitle.height + 18,
scaleX: 2.2,
scaleY: 0.5,
alpha: 0.25
});
game.addChild(missionBarBg);
// Mission bar fill (progress)
var missionBarFill = LK.getAsset('centerCircle', {
anchorX: 0,
anchorY: 0,
x: missionAreaX,
y: missionsTitle.y + missionsTitle.height + 18,
scaleX: 0,
// Will be set dynamically
scaleY: 0.5,
tint: 0xFFD700,
// Gold color
alpha: 0.85
});
game.addChild(missionBarFill);
// Mission text
var missionText = new Text2('Reach $100,000', {
size: 60,
fill: "#fff"
});
missionText.anchor.set(0, 0);
missionText.x = missionAreaX + 10;
missionText.y = missionBarBg.y + missionBarBg.height + 8;
game.addChild(missionText);
// Store for update
var missionBarMaxWidth = 200; // px, visually (not used, but kept for reference)
var missionBarFillMaxScale = 2.2;
// --- Helper: update money display ---
function updateMoneyDisplay() {
moneyTxt.setText('$' + money);
// --- Missions progress update ---
if (typeof missionBarFill !== "undefined") {
// Clamp progress between 0 and 1
var progress = Math.max(0, Math.min(1, money / 100000));
missionBarFill.scaleX = missionBarFillMaxScale * progress;
}
// --- Mission complete: You Win! ---
if (money >= 100000 && !game._missionWinShown) {
game._missionWinShown = true;
LK.showYouWin();
}
}
// --- Helper: unlock field if enough money ---
function tryUnlockField(fieldIdx) {
var f = fields[fieldIdx];
if (!f.locked) return;
if (money >= f.lockCost) {
money -= f.lockCost;
updateMoneyDisplay();
f.locked = false;
f.hideLock();
// Update unlockedFields in storage only if changed
var unlockedFields = storage.unlockedFields || [true, false, false, false];
if (!unlockedFields[fieldIdx]) {
unlockedFields[fieldIdx] = true;
storage.unlockedFields = unlockedFields;
}
// Flash field green
for (var i = 0; i < f.fenceNodes.length; ++i) {
LK.effects.flashObject(f.fenceNodes[i], 0x00ff00, 600);
}
} else {
// Not enough money, flash red
LK.effects.flashScreen(0xff0000, 400);
}
}
// --- Touch/mouse handling ---
var lastDownX = 0,
lastDownY = 0;
var harvestMode = false;
var harvestWheat = null;
// Helper: find harvestable vegetable under (x, y) in unlocked fields
function findHarvestableWheat(x, y) {
for (var i = 0; i < fields.length; ++i) {
var f = fields[i];
if (f.locked) continue;
for (var j = 0; j < f.wheats.length; ++j) {
var w = f.wheats[j];
if (w.isHarvestable()) {
// Use bounding box for hit
var wx = w.x,
wy = w.y;
var ww = 60,
wh = 80;
if (x >= wx - ww / 2 && x <= wx + ww / 2 && y >= wy - wh && y <= wy) {
return w;
}
}
}
}
return null;
}
// Helper: find locked field under (x, y)
function findLockedField(x, y) {
for (var i = 0; i < fields.length; ++i) {
var f = fields[i];
if (!f.locked) continue;
// Use field area
var pos = fieldPositions[i];
var fx = offsetX + pos.x;
var fy = offsetY + pos.y;
if (x >= fx && x <= fx + FIELD_W && y >= fy && y <= fy + FIELD_H) {
return i;
}
}
return -1;
}
// --- Game event handlers ---
// Move farmer or harvest wheat
game.down = function (x, y, obj) {
// If UI handled this click, do nothing for game movement.
if (playerMovementBlockedByUIClick) {
playerMovementBlockedByUIClick = false; // Reset flag for next input
return;
}
// Shop system completely removed
// No shopButton references needed anymore
// Continue with normal game control flow
// No shop button checks needed
//
lastDownX = x;
lastDownY = y;
// Check if tapping on locked field unlock button
var lockedIdx = findLockedField(x, y);
if (lockedIdx >= 0) {
var f = fields[lockedIdx];
// If tap is near lock icon or unlock text
var centerX = offsetX + fieldPositions[lockedIdx].x + FIELD_W / 2;
var centerY = offsetY + fieldPositions[lockedIdx].y + FIELD_H / 2;
var dist = Math.sqrt((x - centerX) * (x - centerX) + (y - centerY) * (y - centerY));
if (dist < 120) {
tryUnlockField(lockedIdx);
return;
}
}
// Check for wheat
var w = findHarvestableWheat(x, y);
if (w) {
harvestMode = true;
harvestWheat = w;
// Change player to face forward during harvest
// Switch to player_down for harvesting
if (farmer.currentAsset) {
farmer.currentAsset.alpha = 0;
}
var playerDown = LK.getAsset('player_down', {
anchorX: 0.5,
anchorY: 0.5
});
playerDown.alpha = 1;
farmer.addChild(playerDown);
farmer.currentAsset = playerDown;
// Trigger sickle attack animation
farmer.attack();
// Show harvest animation effect
var harvestEffect = LK.getAsset('sprout', {
anchorX: 0.5,
anchorY: 0.5,
x: w.x,
y: w.y - 50,
alpha: 0.8,
scaleX: 2,
scaleY: 2
});
game.addChild(harvestEffect);
// Animate the harvest effect
tween(harvestEffect, {
alpha: 0,
y: harvestEffect.y - 100
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
harvestEffect.destroy();
}
});
// Harvest
w.setHarvested(true);
var reward = 100 * (FIELD_REWARD_MULT[w.fieldIndex] || 1);
money += reward;
updateMoneyDisplay();
// Flash wheat
LK.effects.flashObject(w, 0xffff00, 300);
// No setHarvested(false) here; regrow/removal handled in Wheat.update
harvestMode = false;
harvestWheat = null;
return;
}
// Move farmer with animation
farmer.moveTo(x, y);
dragging = true;
dragOffsetX = x - farmer.x;
dragOffsetY = y - farmer.y;
};
// Drag farmer
game.move = function (x, y, obj) {
// If UI interaction blocked the start of a drag, this helps.
if (playerMovementBlockedByUIClick) {
// Flag will be reset by the next 'down' event, or could be reset here if necessary,
// but typically 'move' follows a 'down' that wasn't blocked.
return;
}
// Shop system completely removed
//
if (dragging) {
farmer.moveTo(x - dragOffsetX, y - dragOffsetY);
}
if (harvestMode && harvestWheat) {
sickle.x = x;
sickle.y = y;
}
};
// End drag/harvest
game.up = function (x, y, obj) {
// If UI interaction blocked the 'down' event, 'up' should also be controlled.
if (playerMovementBlockedByUIClick) {
playerMovementBlockedByUIClick = false; // Reset flag here as well
return;
}
// Shop system completely removed
//
//
dragging = false;
harvestMode = false;
harvestWheat = null;
};
// --- Main update loop ---
// Defensive: If game is reset, clear win timer
game.update = function () {
// Update farmer
farmer.update();
// Update wheat regrow
for (var i = 0; i < fields.length; ++i) {
var f = fields[i];
f.wheats.forEach(function (wheat) {
wheat.update();
});
}
// Update clouds
for (var i = 0; i < clouds.length; i++) {
clouds[i].update();
}
// Update birds
for (var i = 0; i < birds.length; i++) {
birds[i].update();
}
};
// --- Create areas and place animals ---
// Calculate center of game screen
var gameWidth = 2048;
var gameHeight = 2732;
var gameCenterX = gameWidth / 2;
var gameCenterY = gameHeight / 2;
// Create the four areas at equal distances from center
// Area_Left
var areaLeft = new Container();
areaLeft.x = gameCenterX - AREA_DISTANCE;
areaLeft.y = gameCenterY;
areaLeft.name = "Area_Left";
game.addChild(areaLeft);
areas.push(areaLeft);
// Area_Right
var areaRight = new Container();
areaRight.x = gameCenterX + AREA_DISTANCE;
areaRight.y = gameCenterY;
areaRight.name = "Area_Right";
game.addChild(areaRight);
areas.push(areaRight);
// Area_Top
var areaTop = new Container();
areaTop.x = gameCenterX;
areaTop.y = gameCenterY - AREA_DISTANCE;
areaTop.name = "Area_Top";
game.addChild(areaTop);
areas.push(areaTop);
// Area_Bottom
var areaBottom = new Container();
areaBottom.x = gameCenterX;
areaBottom.y = gameCenterY + AREA_DISTANCE;
areaBottom.name = "Area_Bottom";
game.addChild(areaBottom);
areas.push(areaBottom);
// Place animals in the areas
// Animal_Right at bottom right of screen
var animalRight = new Animal();
animalRight.setAnimalImage('animal_right');
animalRight.name = "Animal_Right";
// Place at bottom right, accounting for anchor (0.5, 0.5) and asset size
var animalRightAsset = LK.getAsset('animal_right', {
anchorX: 0.5,
anchorY: 0.5
});
animalRight.x = gameWidth - animalRightAsset.width / 2 - 10; // 10px padding from right
animalRight.y = gameHeight - animalRightAsset.height / 2 - 10; // 10px padding from bottom
game.addChild(animalRight);
animals.push(animalRight);
// Area_Bottom has no animal
// --- Create clouds ---
var clouds = [];
// Create clouds at different positions with increased delay
function createClouds() {
// Reuse existing clouds or create new ones if pool is empty
var cloud;
if (clouds.length > 0) {
cloud = clouds.pop();
cloud.alpha = 1; // Reset alpha for visibility
cloud.visible = true; // Ensure cloud is visible
cloud.init(); // Reinitialize cloud properties
} else {
cloud = new Cloud();
game.addChild(cloud);
cloud.init(); // Initialize cloud properties
}
// Cloud width for calculations - cached outside for performance
var cloudWidth = 500;
cloud.x = -cloudWidth / 2; // Start from off-screen left
cloud.y = 180; // Higher position near the top
cloud.layer = 0; // Front layer - this will be in front
cloud.init(); // Initialize movement and timing
clouds.push(cloud);
// Schedule creation of new clouds after these disappear - extended time
LK.setTimeout(createClouds, 180000); // 180 seconds to spread out cloud creation more
}
// Start creating clouds
createClouds();
// Shop system completely removed
// --- Create zombie ---
var zombie = null;
var zombieDamageTimer = 0; // Timer to prevent continuous damage
var zombieDamageAmount = 500; // Initial zombie damage/cost
var zombieAppearCount = 0; // How many times zombie has appeared
// --- Create birds ---
var birds = [];
// Create a bird that starts at the top, flies across, disappears, and restarts in a loop
function createBird() {
// Avoid overlapping timers - clear any existing
if (window._birdCreationTimer) {
LK.clearTimeout(window._birdCreationTimer);
window._birdCreationTimer = null;
}
// Reuse existing birds or create new ones if pool is empty
var bird;
if (birds.length > 0) {
bird = birds.pop();
bird.alpha = 1; // Reset alpha for visibility
bird.visible = true; // Ensure bird is visible
bird.init(); // Reinitialize bird properties
} else {
bird = new Bird();
game.addChild(bird);
bird.init(); // Initialize bird properties
}
// Cache bird dimensions for better performance
var birdWidth = 350; // Use known width instead of creating temporary asset
var birdHeight = 350; // Use known height
bird.x = -birdWidth / 2;
bird.y = birdHeight / 2 + 20; // 20px padding from the very top
bird.init();
birds.push(bird);
// Simple flag to prevent duplicate scheduling
bird._nextBirdScheduled = false;
// Optimized check function that runs less frequently
bird._birdLoopCheck = function () {
// Only schedule new bird once if this one is gone
if ((!bird.parent || bird.alpha <= 0) && !bird._nextBirdScheduled) {
bird._nextBirdScheduled = true;
// Create new bird after a longer delay to reduce processing frequency
window._birdCreationTimer = LK.setTimeout(createBird, 240000); // 240 seconds (4 min) delay for less frequent processing
} else if (bird.parent && bird.alpha > 0) {
// Check much less frequently to reduce timer overhead
LK.setTimeout(bird._birdLoopCheck, 30000); // Check every 30 seconds instead of 15
}
};
// Start the check after a longer delay
LK.setTimeout(bird._birdLoopCheck, 30000);
}
// Start creating birds after 1 minute (60000 ms)
LK.setTimeout(createBird, 60000);
// Play background music in a loop
LK.playMusic('backgroundMusic', {
loop: true
});
// --- Initial money display ---
updateMoneyDisplay();
// --- Place trees at corners ---
// Tree size
var treeWidth = 400;
var treeHeight = 532;
// Top left corner tree
var topLeftTree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
x: offsetX / 2,
y: offsetY / 2
});
game.addChild(topLeftTree);
// Top right corner tree
var topRightTree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - offsetX / 2,
y: offsetY / 2
});
game.addChild(topRightTree);
// Bottom left corner tree
var bottomLeftTree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
x: offsetX / 2,
y: 2732 - offsetY / 2
});
game.addChild(bottomLeftTree);
// Bottom right corner tree
var bottomRightTree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - offsetX / 2,
y: 2732 - offsetY / 2
});
game.addChild(bottomRightTree);
// --- Place trees at middle sides aligned with corner trees ---
// Tree to the left of center
var leftCenterTree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
x: offsetX / 2,
y: topRightTree.y
});
game.addChild(leftCenterTree);
// Tree to the right of center
var rightCenterTree = LK.getAsset('tree', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - offsetX / 2,
y: topRightTree.y
});
game.addChild(rightCenterTree);
// --- Add Rooster that moves left-to-right, disappears at right edge, reappears after 40s and repeats ---
var rooster = null;
var roosterTimeout = null;
function spawnRooster() {
// Remove any existing rooster - with robust null check
if (rooster) {
// Only try to destroy if rooster exists and has a parent
if (rooster && rooster.parent) {
try {
rooster.destroy();
} catch (e) {
console.log("Error destroying rooster:", e);
}
}
rooster = null;
}
rooster = new Rooster();
// Start at the left center of the screen
rooster.y = 2732 / 2;
rooster.x = rooster.roosterFrames[0].width / 2 + 10;
game.addChild(rooster);
// Rooster path: left-to-right, horizontally centered
var roosterStartX = rooster.roosterFrames[0].width / 2 + 10;
var roosterEndX = 2048 - rooster.roosterFrames[0].width / 2 - 10;
var roosterSpeed = 1; // px per frame, very slow movement
rooster.targetX = roosterEndX;
rooster.direction = 1; // 1: moving right
rooster.updateRoosterMovement = function () {
// Track lastX for edge detection
if (typeof rooster.lastX === "undefined") rooster.lastX = rooster.x;
// Move only horizontally in the center
var dx = rooster.targetX - rooster.x;
var dist = Math.abs(dx);
if (dist < roosterSpeed) {
rooster.x = rooster.targetX;
} else {
rooster.x += roosterSpeed * (dx > 0 ? 1 : -1);
}
// Call animation update
if (typeof rooster.update === "function") {
rooster.update();
}
// Check if rooster just reached the right edge (disappear trigger)
if (rooster.lastX < roosterEndX && rooster.x >= roosterEndX) {
// Fade out and destroy, then respawn after 40s
tween(rooster, {
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (rooster && rooster.parent) {
rooster.destroy();
}
roosterTimeout = LK.setTimeout(spawnRooster, 40000); // 40 seconds
}
});
}
// Update lastX for next frame
rooster.lastX = rooster.x;
};
}
// Add rooster update to main game loop
var oldGameUpdate = game.update;
game.update = function () {
if (typeof oldGameUpdate === "function") oldGameUpdate();
if (rooster && rooster.parent && typeof rooster.updateRoosterMovement === "function") {
rooster.updateRoosterMovement();
}
// Update zombie
if (zombie && zombie.parent) {
zombie.update();
// Collision detection with player (only triggers on state change)
var isIntersecting = zombie.intersects(farmer);
// Check if we just started intersecting
if (!zombie.lastWasIntersecting && isIntersecting && zombieDamageTimer <= 0) {
// Zombie just touched the player, deduct gold
money = Math.max(0, money - zombieDamageAmount);
updateMoneyDisplay();
// Flash both zombie and farmer red
LK.effects.flashObject(zombie, 0xff0000, 500);
LK.effects.flashObject(farmer, 0xff0000, 500);
// Set cooldown timer to prevent continuous damage (3 second cooldown)
zombieDamageTimer = 180;
}
// Update intersection state
zombie.lastWasIntersecting = isIntersecting;
// Update damage cooldown timer
if (zombieDamageTimer > 0) {
zombieDamageTimer--;
}
}
storage.farmerX = farmer.x;
storage.farmerY = farmer.y;
storage.money = money;
};
// Create the zombie
function createZombie() {
// Reuse existing zombie or create new one if pool is empty
if (!zombie) {
zombie = new Zombie();
game.addChild(zombie);
} else {
zombie.alpha = 1;
zombie.visible = true;
zombie.isDead = false;
zombie.init(); // Reinitialize zombie properties
}
// Reset zombie properties for reuse
zombie.alpha = 1;
zombie.isDead = false;
// Position zombie off-screen to the left
zombie.x = -100;
zombie.y = 2732 / 2;
// Initialize tracking variables
zombie.lastX = zombie.x;
zombie.lastY = zombie.y;
zombie.lastWasIntersecting = false;
// Increase zombie damage each time it spawns
zombieAppearCount++;
if (zombieAppearCount === 1) {
zombieDamageAmount = 500;
} else {
zombieDamageAmount = zombieDamageAmount * 2;
}
// Schedule next zombie creation with increased delay
LK.setTimeout(createZombie, 120000); // 120 seconds delay for next zombie
}
// Start the first rooster and create zombie
spawnRooster();
// Delay the first zombie spawn by 40 seconds after game start
LK.setTimeout(createZombie, 40000);
// Zombie respawn is now handled after being killed by the player;
// --- Time and Season System ---
// --- Day and Season Variables ---
var currentDay = storage.currentDay || 1;
var currentSeason = storage.currentSeason || "Summer";
var SEASONS = ["Summer", "Autumn"];
var seasonIndex = SEASONS.indexOf(currentSeason);
if (seasonIndex === -1) seasonIndex = 0;
var autumnColors = [0xC97A3A, 0xB94E2A, 0xA65C1B, 0xE07B39, 0xD2691E, 0xB22222, 0x8B4513];
var autumnLeafAssets = []; // Will hold leaf particles
var autumnLeavesActive = false;
var autumnCropSlowdown = 2; // Crops grow 2x slower in autumn
var autumnNightStartHour = 18; // Night starts earlier (not implemented, placeholder)
var autumnGrassColor = 0xB97A56; // Dry grass color
// --- UI Elements for Day and Season ---
var dayTxt = new Text2('Day ' + currentDay, {
size: 80,
fill: "#fff"
});
dayTxt.anchor.set(1, 0);
dayTxt.x = 2048 - 40; // Right side, 40px padding
dayTxt.y = 40; // Top, 40px padding
game.addChild(dayTxt);
var seasonTxt = new Text2(SEASONS[seasonIndex], {
size: 80,
fill: "#fff"
});
seasonTxt.anchor.set(1, 0);
seasonTxt.x = 2048 - 40; // Right side, 40px padding
seasonTxt.y = 120; // Below day text
game.addChild(seasonTxt);
// --- Autumn Effects: Falling Leaves ---
function spawnAutumnLeaves() {
if (autumnLeavesActive) return;
autumnLeavesActive = true;
// Remove any old leaves
for (var i = 0; i < autumnLeafAssets.length; ++i) {
if (autumnLeafAssets[i] && autumnLeafAssets[i].parent) {
try {
autumnLeafAssets[i].destroy();
} catch (e) {}
}
}
autumnLeafAssets = [];
// Spawn 20 leaves at random positions at the top
for (var i = 0; i < 20; ++i) {
var color = autumnColors[Math.floor(Math.random() * autumnColors.length)];
var leaf = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.25 + Math.random() * 0.2,
scaleY: 0.12 + Math.random() * 0.1,
tint: color,
alpha: 0.7 + Math.random() * 0.3,
x: Math.random() * 2048,
y: -30 - Math.random() * 100
});
leaf._fallSpeed = 1.5 + Math.random() * 1.5;
leaf._drift = (Math.random() - 0.5) * 2;
leaf._spin = (Math.random() - 0.5) * 0.1;
game.addChild(leaf);
autumnLeafAssets.push(leaf);
}
}
// --- Remove Autumn Leaves ---
function removeAutumnLeaves() {
autumnLeavesActive = false;
for (var i = 0; i < autumnLeafAssets.length; ++i) {
if (autumnLeafAssets[i] && autumnLeafAssets[i].parent) {
try {
autumnLeafAssets[i].destroy();
} catch (e) {}
}
}
autumnLeafAssets = [];
}
// --- Update Autumn Leaves ---
function updateAutumnLeaves() {
if (!autumnLeavesActive) return;
for (var i = 0; i < autumnLeafAssets.length; ++i) {
var leaf = autumnLeafAssets[i];
if (!leaf) continue;
leaf.y += leaf._fallSpeed;
leaf.x += leaf._drift;
leaf.rotation += leaf._spin;
// Respawn leaf at top if it falls below screen
if (leaf.y > 2732 + 30) {
leaf.y = -30 - Math.random() * 100;
leaf.x = Math.random() * 2048;
leaf._fallSpeed = 1.5 + Math.random() * 1.5;
leaf._drift = (Math.random() - 0.5) * 2;
leaf._spin = (Math.random() - 0.5) * 0.1;
}
}
}
// --- Crop Growth Slowdown Helper ---
function getCropGrowthModifier() {
return SEASONS[seasonIndex] === "Autumn" ? autumnCropSlowdown : 1;
}
// --- Update Day and Season ---
function updateDayAndSeason() {
currentDay++;
// Change season every 3 days
if (currentDay > 3) {
currentDay = 1;
seasonIndex = (seasonIndex + 1) % SEASONS.length;
currentSeason = SEASONS[seasonIndex];
// Visual changes for each season
if (currentSeason === "Autumn") {
game.setBackgroundColor(autumnGrassColor);
spawnAutumnLeaves();
} else {
// Summer: restore green grass, remove leaves
game.setBackgroundColor(0x4caf50);
removeAutumnLeaves();
}
}
dayTxt.setText('Day ' + currentDay);
seasonTxt.setText(currentSeason);
storage.currentDay = currentDay;
storage.currentSeason = currentSeason;
}
// --- Apply Crop Growth Slowdown in Autumn ---
// Patch all crop regrowTime on season change
function applySeasonalCropGrowth() {
for (var i = 0; i < fields.length; ++i) {
var f = fields[i];
for (var j = 0; j < f.wheats.length; ++j) {
var crop = f.wheats[j];
if (typeof crop._baseRegrowTime === "undefined") {
crop._baseRegrowTime = crop.regrowTime;
}
crop.regrowTime = crop._baseRegrowTime * getCropGrowthModifier();
}
}
}
// --- Patch updateDayAndSeason to also update crop growth ---
var _oldUpdateDayAndSeason = updateDayAndSeason;
updateDayAndSeason = function updateDayAndSeason() {
_oldUpdateDayAndSeason();
applySeasonalCropGrowth();
};
// --- Initial Season Visuals and Crop Growth ---
if (currentSeason === "Autumn") {
game.setBackgroundColor(autumnGrassColor);
spawnAutumnLeaves();
} else {
game.setBackgroundColor(0x4caf50);
removeAutumnLeaves();
}
applySeasonalCropGrowth();
// --- Set interval to update day and season every minute (for testing) ---
LK.setInterval(updateDayAndSeason, 60000);
// --- Patch main game update to animate autumn leaves ---
var _oldGameUpdateForSeason = game.update;
game.update = function () {
if (typeof _oldGameUpdateForSeason === "function") _oldGameUpdateForSeason();
updateAutumnLeaves();
};