/**** * Plugins ****/ var tween = LK.import("@upit/tween.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 self.update = function () { // Increment life counter self.lifeCounter++; // Create a small wavy flight pattern self.y = self.y + Math.sin(self.lifeCounter * 0.03) * 1.5; // If bird has reached the right edge of the screen if (self.x >= 2048 + birdAsset.width / 2) { // 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.destroy(); } }); } }; 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 self.update = function () { // Increment life counter self.lifeCounter++; // Track lastX for edge detection if (typeof self.lastX === "undefined") { self.lastX = self.x; } // If cloud has just reached the right edge of the screen (crossed this frame) if (self.lastX < 2048 + cloudAsset.width / 2 && self.x >= 2048 + cloudAsset.width / 2) { // Stop any existing tweens on this cloud tween.stop(self); // Fade out animation tween(self, { alpha: 0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Remove cloud from game self.destroy(); } }); } // 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; }); // Zombie respawn is now handled after being killed by the player; // IntroScreen class for game intro animation with improved loading var IntroScreen = Container.expand(function () { var self = Container.call(this); // Track loading state self.loadingPhase = 0; // 0=Loading core, 1=Loading intro, 2=Loading game assets, 3=Complete self.animationStarted = false; self.introAssetsLoaded = false; self.gameAssetsLoaded = false; // Create a dark overlay as background var overlay = LK.getAsset('centerCircle', { anchorX: 0, anchorY: 0, scaleX: 30, scaleY: 40, alpha: 1, tint: 0x000000 }); self.addChild(overlay); // Loading text var loadingText = new Text2('Loading Core Assets...', { size: 120, fill: 0xFFFFFF }); loadingText.anchor.set(0.5, 0.5); loadingText.x = 2048 / 2; loadingText.y = 2732 / 2; self.addChild(loadingText); // Animation references var sunriseBackground = null; var introClouds = []; var maleFarmer = null; var femaleFarmer = null; var sproutAsset = null; var wheatAsset = null; var carrotAsset = null; var tomatoAsset = null; var titleText = null; var subtitleText = null; var introZombie = null; var introSickle = null; var startButtonBg = null; var startText = null; // Loading dots animation self.updateLoadingText = function () { if (self.loadingPhase >= 3) { return; } var dots = ''; if (loadingText && loadingText.text) { var baseTxt = ''; if (self.loadingPhase === 0) { baseTxt = 'Loading Core Assets'; } else if (self.loadingPhase === 1) { baseTxt = 'Loading Intro Assets'; } else if (self.loadingPhase === 2) { baseTxt = 'Loading Game Assets'; } if (loadingText.text.indexOf(baseTxt) === 0) { var parts = loadingText.text.split(baseTxt); dots = parts.length > 1 ? parts[1] : ''; } } // Cycle through dot patterns if (dots === '...') { dots = ''; } else if (dots === '') { dots = '.'; } else if (dots === '.') { dots = '..'; } else if (dots === '..') { dots = '...'; } // Update text based on current loading phase var baseTxt = ''; if (self.loadingPhase === 0) { baseTxt = 'Loading Core Assets'; } else if (self.loadingPhase === 1) { baseTxt = 'Loading Intro Assets'; } else if (self.loadingPhase === 2) { baseTxt = 'Loading Game Assets'; } loadingText.setText(baseTxt + dots); // Continue animation until all assets are loaded LK.setTimeout(self.updateLoadingText, 500); }; // Check if intro assets are loaded self.checkIntroAssetsLoaded = function () { if (!self.introAssetsLoaded) { var allAssetsLoaded = true; var testAssets = ['centerCircle', 'cloud']; for (var i = 0; i < testAssets.length; i++) { var testAsset = LK.getAsset(testAssets[i], { anchorX: 0.5, anchorY: 0.5 }); if (!testAsset || !testAsset.width) { allAssetsLoaded = false; break; } } if (allAssetsLoaded) { self.introAssetsLoaded = true; self.loadingPhase = 1; // Now that intro assets are loaded, create the intro elements self.createIntroElements(); // Start loading game assets LK.setTimeout(self.checkGameAssetsLoaded, 100); } else { // Check again in a moment LK.setTimeout(self.checkIntroAssetsLoaded, 100); } } }; // Check if game assets are loaded self.checkGameAssetsLoaded = function () { if (!self.gameAssetsLoaded) { var allAssetsLoaded = true; var testAssets = ['farmer', 'wheat', 'zombie_1', 'tomato', 'carrot', 'eggplant', 'sickle']; for (var i = 0; i < testAssets.length; i++) { var testAsset = LK.getAsset(testAssets[i], { anchorX: 0.5, anchorY: 0.5 }); if (!testAsset || !testAsset.width) { allAssetsLoaded = false; break; } } if (allAssetsLoaded) { self.gameAssetsLoaded = true; self.loadingPhase = 3; // Fade out the loading text tween(loadingText, { alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { loadingText.destroy(); // Start the intro animation once all loading is complete if (!self.animationStarted) { self.animationStarted = true; self.startAnimation(); } } }); } else { // Update loading phase self.loadingPhase = 2; // Check again in a moment LK.setTimeout(self.checkGameAssetsLoaded, 100); } } }; // Create intro elements once basic assets are loaded self.createIntroElements = function () { // Add a sunrise effect background sunriseBackground = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, scaleX: 30, scaleY: 40, alpha: 0, tint: 0xf5cb6c // Sunrise yellow color }); self.addChild(sunriseBackground); // Create animated clouds for intro introClouds = []; for (var i = 0; i < 3; i++) { var cloudAsset = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5, x: -300 + i * 500, y: 400 + i * 150, alpha: 0, scaleX: 0.8 + i * 0.3, scaleY: 0.8 + i * 0.3 }); self.addChild(cloudAsset); introClouds.push(cloudAsset); } // Create title text titleText = new Text2('FARM HARVESTER', { size: 180, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 4; titleText.alpha = 0; self.addChild(titleText); // Create subtitle text subtitleText = new Text2('Grow crops, earn money, expand your farm!', { size: 90, fill: 0xFFFFFF }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 2048 / 2; subtitleText.y = titleText.y + 200; subtitleText.alpha = 0; self.addChild(subtitleText); // Create start text only, no background startText = new Text2('TAP TO START', { size: 100, fill: 0xFFFFFF }); startText.anchor.set(0.5, 0.5); startText.x = 2048 / 2; startText.y = 2732 / 2 + 600; startText.alpha = 0; self.addChild(startText); }; // Start the loading process self.updateLoadingText(); self.checkIntroAssetsLoaded(); // Lazy creation of game assets for intro only when needed self.createGameIntroAssets = function () { // Create male and female farmer for intro animation only when needed if (!maleFarmer) { maleFarmer = LK.getAsset('farmer', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 - 300, y: 2732 / 2 + 200, scaleX: 1.2, scaleY: 1.2, alpha: 0 }); self.addChild(maleFarmer); } if (!femaleFarmer) { femaleFarmer = LK.getAsset('farmer_walk', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 + 300, y: 2732 / 2 + 200, scaleX: 1.2, scaleY: 1.2, alpha: 0 }); self.addChild(femaleFarmer); } // Create growing plant animation elements only when needed if (!sproutAsset) { sproutAsset = LK.getAsset('sprout', { anchorX: 0.5, anchorY: 1, x: 2048 / 2, y: 2732 / 2 + 400, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); self.addChild(sproutAsset); } if (!wheatAsset) { wheatAsset = LK.getAsset('wheat', { anchorX: 0.5, anchorY: 1, x: 2048 / 2, y: 2732 / 2 + 400, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); self.addChild(wheatAsset); } // Create vegetables that will grow from sprout if (!carrotAsset) { carrotAsset = LK.getAsset('carrot', { anchorX: 0.5, anchorY: 1, x: 2048 / 2 - 200, y: 2732 / 2 + 400, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); self.addChild(carrotAsset); } if (!tomatoAsset) { tomatoAsset = LK.getAsset('tomato', { anchorX: 0.5, anchorY: 1, x: 2048 / 2 + 200, y: 2732 / 2 + 400, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); self.addChild(tomatoAsset); } // Add zombie for intro animation if (!introZombie) { introZombie = LK.getAsset('zombie_1', { anchorX: 0.5, anchorY: 0.5, x: 2048 + 300, y: 2732 / 2 + 300, scaleX: 1.2, scaleY: 1.2, alpha: 0 }); self.addChild(introZombie); } // Add sickle for intro animation if (!introSickle) { introSickle = LK.getAsset('sickle', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 200, scaleX: 2, scaleY: 2, alpha: 0, rotation: -Math.PI / 4 }); self.addChild(introSickle); } }; // Start animation sequence self.startAnimation = function () { // Make sure assets are loaded before starting animation if (self.loadingPhase < 3) { LK.setTimeout(self.checkGameAssetsLoaded, 100); return; } // Create game assets needed for intro animation self.createGameIntroAssets(); // Fade in sunrise effect tween(sunriseBackground, { alpha: 0.5 }, { duration: 2000, easing: tween.easeOut }); // Animate clouds drifting in for (var i = 0; i < introClouds.length; i++) { (function (index) { var cloud = introClouds[index]; LK.setTimeout(function () { tween(cloud, { alpha: 0.7, x: cloud.x + 400 }, { duration: 4000, easing: tween.easeOut }); }, 500 * index); })(i); } // Fade in and animate farmers running in from sides LK.setTimeout(function () { // Animate male farmer running in from left tween(maleFarmer, { alpha: 1, x: 2048 / 2 - 150 }, { duration: 1500, easing: tween.easeOut }); // Animate female farmer running in from right tween(femaleFarmer, { alpha: 1, x: 2048 / 2 + 150 }, { duration: 1500, easing: tween.easeOut }); }, 1000); // Animate farmers meeting in middle and "planting" sprout LK.setTimeout(function () { // Move farmers to center tween(maleFarmer, { x: 2048 / 2 - 50 }, { duration: 800, easing: tween.easeInOut }); tween(femaleFarmer, { x: 2048 / 2 + 50 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { // Show sprout growing sproutAsset.alpha = 1; tween(sproutAsset, { scaleX: 1, scaleY: 1 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { // Show wheat growing from sprout wheatAsset.alpha = 1; tween(wheatAsset, { scaleX: 1.5, scaleY: 1.5 }, { duration: 1000, easing: tween.easeOut }); // Show carrot and tomato growing carrotAsset.alpha = 1; tween(carrotAsset, { scaleX: 1.2, scaleY: 1.2 }, { duration: 1000, easing: tween.easeOut }); tomatoAsset.alpha = 1; tween(tomatoAsset, { scaleX: 1.2, scaleY: 1.2 }, { duration: 1000, easing: tween.easeOut }); } }); } }); }, 3000); // Animate zombie coming in from right LK.setTimeout(function () { introZombie.alpha = 1; tween(introZombie, { x: 2048 / 2 + 450 }, { duration: 1500, easing: tween.easeIn }); }, 5000); // Animate sickle appearing and swinging LK.setTimeout(function () { introSickle.alpha = 1; tween(introSickle, { rotation: Math.PI }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(introZombie, { alpha: 0, y: introZombie.y + 100 }, { duration: 500, easing: tween.easeIn }); } }); }, 6000); // Fade in title with a fancy effect LK.setTimeout(function () { titleText.alpha = 0.1; tween(titleText, { alpha: 1, y: titleText.y + 50 }, { duration: 1200, easing: tween.easeOut }); // Flash effect for title var _flashTitle = function flashTitle(count) { if (count <= 0) { return; } tween(titleText, { alpha: 0.7 }, { duration: 100, onFinish: function onFinish() { tween(titleText, { alpha: 1 }, { duration: 100, onFinish: function onFinish() { _flashTitle(count - 1); } }); } }); }; LK.setTimeout(function () { _flashTitle(3); }, 1200); }, 7000); // Fade in subtitle with delay LK.setTimeout(function () { tween(subtitleText, { alpha: 1, y: subtitleText.y + 30 }, { duration: 800, easing: tween.easeOut }); }, 8000); // Fade out animation elements and transition to game UI LK.setTimeout(function () { // Fade out animation elements if (maleFarmer) { tween(maleFarmer, { alpha: 0 }, { duration: 800 }); } if (femaleFarmer) { tween(femaleFarmer, { alpha: 0 }, { duration: 800 }); } if (sproutAsset) { tween(sproutAsset, { alpha: 0 }, { duration: 800 }); } if (wheatAsset) { tween(wheatAsset, { alpha: 0 }, { duration: 800 }); } if (carrotAsset) { tween(carrotAsset, { alpha: 0 }, { duration: 800 }); } if (tomatoAsset) { tween(tomatoAsset, { alpha: 0 }, { duration: 800 }); } if (introSickle) { tween(introSickle, { alpha: 0 }, { duration: 800 }); } // Fade in start text only - startButtonBg is removed tween(startText, { alpha: 1 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // No need to pulse since startButtonBg is removed } }); }, 9500); }; // Pulse animation for start button (modified to check if element exists) self.startPulse = function () { // Just pulse the start text since background is removed tween(startText, { scaleX: 1.1, scaleY: 1.1 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(startText, { scaleX: 1, scaleY: 1 }, { duration: 800, easing: tween.easeInOut, onFinish: self.startPulse }); } }); }; // Handle intro screen tap to start the game self.down = function (x, y, obj) { // Prevent multiple taps if (self._startingGame) { return; } self._startingGame = true; // Play tap animation for text instead tween(startText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Transition to yellow background tween(sunriseBackground, { alpha: 1, tint: 0xf8e9c0 }, { duration: 800, easing: tween.easeOut }); // Fade out all elements if (titleText) { tween(titleText, { alpha: 0 }, { duration: 600 }); } if (subtitleText) { tween(subtitleText, { alpha: 0 }, { duration: 600 }); } tween(startText, { alpha: 0 }, { duration: 600 }); for (var i = 0; i < introClouds.length; i++) { tween(introClouds[i], { alpha: 0 }, { duration: 600 }); } // Fade out entire intro screen with delay LK.setTimeout(function () { tween(self, { alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); // Start/enable actual game startActualGame(); } }); }, 800); } }); return true; // Prevent event bubbling }; 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 self.update = function () { if (self.harvested && self.regrowing) { if (self.regrowCounter > 0) { // Animate sprout growing to wheat/veg 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 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 if (self.isDead) { 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; } } }; 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 = 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); // --- Setup actual game start function --- var gameInitialized = false; var introScreen = null; function initializeGameElements() { if (gameInitialized) { return; } // --- Create fields --- for (var i = 0; i < FIELD_COUNT; ++i) { var f = new Field(); f.index = i; f.locked = FIELD_LOCK_COSTS[i] > 0; 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); } gameInitialized = true; } function startActualGame() { // The assets should already be loaded due to the optimized intro loading process // Initialize game elements if not already done initializeGameElements(); // Set game elements active game.gameActive = true; // Play background music LK.playMusic('backgroundMusic', { loop: true }); // Start game timers and events createClouds(); LK.setTimeout(createBird, 60000); spawnRooster(); LK.setTimeout(createZombie, 40000); } // Create intro screen with enhanced animations introScreen = new IntroScreen(); game.addChild(introScreen); // Set game to inactive until intro completes game.gameActive = false; // Start intro animation sequence introScreen.startAnimation(); // Don't initialize game elements yet - wait for intro to complete // They will be created when the player taps the start button // Center reference point for positions var centerX = offsetX + FIELD_W; var centerY = offsetY + FIELD_H - 250; // --- Create farmer --- farmer = new Farmer(); farmer.x = centerX; farmer.y = 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); // Buttons removed to simplify the game interface // --- 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(); // 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) { // Don't process game inputs if game is not active yet if (!game.gameActive) { 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) { // Don't process game inputs if game is not active yet if (!game.gameActive) { 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) { // Don't process game inputs if game is not active yet if (!game.gameActive) { 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 () { // Only update game elements if game is active (after intro) if (!game.gameActive) { // If there's an intro screen, allow it to animate even while game is inactive if (introScreen && introScreen.parent) { // Still allow intro screen animations to update if (introScreen.updateLoadingText && !introScreen.assetsLoaded) { // Loading animation is handled by setTimeout } } return; } // Update farmer if initialized if (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 two clouds at different positions function createClouds() { // Remove any existing clouds for (var i = 0; i < clouds.length; i++) { clouds[i].destroy(); } clouds = []; // Cloud width for calculations var cloudWidth = 500; // Create first cloud (front layer) var cloud1 = new Cloud(); cloud1.x = -cloudWidth / 2; // Start from off-screen left cloud1.y = 180; // Higher position near the top cloud1.layer = 0; // Front layer - this will be in front cloud1.init(); // Initialize movement and timing game.addChild(cloud1); clouds.push(cloud1); // Create second cloud (back layer) var cloud2 = new Cloud(); cloud2.x = -cloudWidth / 2 - 200; // Start more separated from first cloud cloud2.y = 280; // Position below first cloud with more separation cloud2.layer = 1; // Back layer - this will be behind cloud2.init(); // Initialize movement and timing game.addChild(cloud2); clouds.push(cloud2); // Schedule creation of new clouds after these disappear LK.setTimeout(createClouds, 61000); // 61 seconds to ensure smooth transition } // 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() { // Remove any existing birds that might be stuck for (var i = 0; i < birds.length; i++) { birds[i].destroy(); } birds = []; // Bird asset for size var tempBirdAsset = LK.getAsset('Bird', { anchorX: 0.5, anchorY: 0.5 }); var birdWidth = tempBirdAsset.width; var birdHeight = tempBirdAsset.height; // Start at the top of the screen, just off the left edge var bird = new Bird(); bird.x = -birdWidth / 2; bird.y = birdHeight / 2 + 20; // 20px padding from the very top bird.init(); game.addChild(bird); birds.push(bird); // Set up a watcher to restart the bird after 60 seconds (3600 ticks at 60fps) bird._birdLoopCheck = function () { // Only restart if bird is gone (destroyed) if (!bird.parent) { LK.setTimeout(createBird, 60000); // 60 seconds delay before next bird } else { LK.setTimeout(bird._birdLoopCheck, 200); // Check again in 200ms } }; LK.setTimeout(bird._birdLoopCheck, 200); } // Start creating birds after 1 minute (60000 ms) LK.setTimeout(createBird, 60000); // Background music will be played when the game starts after the intro sequence completes // Music playback is now handled in startActualGame() // --- 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 if (rooster && rooster.parent) { rooster.destroy(); } 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--; } } }; // Create the zombie function createZombie() { // Remove existing zombie if it exists if (zombie && zombie.parent) { zombie.destroy(); } // Create new zombie zombie = new Zombie(); // 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; game.addChild(zombie); // Increase zombie damage each time it spawns zombieAppearCount++; if (zombieAppearCount === 1) { zombieDamageAmount = 500; } else { zombieDamageAmount = zombieDamageAmount * 2; } } // Start the first rooster and create zombie spawnRooster(); // Delay the first zombie spawn by 40 seconds after game start LK.setTimeout(createZombie, 40000);
/****
* Plugins
****/
var tween = LK.import("@upit/tween.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
self.update = function () {
// Increment life counter
self.lifeCounter++;
// Create a small wavy flight pattern
self.y = self.y + Math.sin(self.lifeCounter * 0.03) * 1.5;
// If bird has reached the right edge of the screen
if (self.x >= 2048 + birdAsset.width / 2) {
// 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.destroy();
}
});
}
};
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
self.update = function () {
// Increment life counter
self.lifeCounter++;
// Track lastX for edge detection
if (typeof self.lastX === "undefined") {
self.lastX = self.x;
}
// If cloud has just reached the right edge of the screen (crossed this frame)
if (self.lastX < 2048 + cloudAsset.width / 2 && self.x >= 2048 + cloudAsset.width / 2) {
// Stop any existing tweens on this cloud
tween.stop(self);
// Fade out animation
tween(self, {
alpha: 0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Remove cloud from game
self.destroy();
}
});
}
// 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;
});
// Zombie respawn is now handled after being killed by the player;
// IntroScreen class for game intro animation with improved loading
var IntroScreen = Container.expand(function () {
var self = Container.call(this);
// Track loading state
self.loadingPhase = 0; // 0=Loading core, 1=Loading intro, 2=Loading game assets, 3=Complete
self.animationStarted = false;
self.introAssetsLoaded = false;
self.gameAssetsLoaded = false;
// Create a dark overlay as background
var overlay = LK.getAsset('centerCircle', {
anchorX: 0,
anchorY: 0,
scaleX: 30,
scaleY: 40,
alpha: 1,
tint: 0x000000
});
self.addChild(overlay);
// Loading text
var loadingText = new Text2('Loading Core Assets...', {
size: 120,
fill: 0xFFFFFF
});
loadingText.anchor.set(0.5, 0.5);
loadingText.x = 2048 / 2;
loadingText.y = 2732 / 2;
self.addChild(loadingText);
// Animation references
var sunriseBackground = null;
var introClouds = [];
var maleFarmer = null;
var femaleFarmer = null;
var sproutAsset = null;
var wheatAsset = null;
var carrotAsset = null;
var tomatoAsset = null;
var titleText = null;
var subtitleText = null;
var introZombie = null;
var introSickle = null;
var startButtonBg = null;
var startText = null;
// Loading dots animation
self.updateLoadingText = function () {
if (self.loadingPhase >= 3) {
return;
}
var dots = '';
if (loadingText && loadingText.text) {
var baseTxt = '';
if (self.loadingPhase === 0) {
baseTxt = 'Loading Core Assets';
} else if (self.loadingPhase === 1) {
baseTxt = 'Loading Intro Assets';
} else if (self.loadingPhase === 2) {
baseTxt = 'Loading Game Assets';
}
if (loadingText.text.indexOf(baseTxt) === 0) {
var parts = loadingText.text.split(baseTxt);
dots = parts.length > 1 ? parts[1] : '';
}
}
// Cycle through dot patterns
if (dots === '...') {
dots = '';
} else if (dots === '') {
dots = '.';
} else if (dots === '.') {
dots = '..';
} else if (dots === '..') {
dots = '...';
}
// Update text based on current loading phase
var baseTxt = '';
if (self.loadingPhase === 0) {
baseTxt = 'Loading Core Assets';
} else if (self.loadingPhase === 1) {
baseTxt = 'Loading Intro Assets';
} else if (self.loadingPhase === 2) {
baseTxt = 'Loading Game Assets';
}
loadingText.setText(baseTxt + dots);
// Continue animation until all assets are loaded
LK.setTimeout(self.updateLoadingText, 500);
};
// Check if intro assets are loaded
self.checkIntroAssetsLoaded = function () {
if (!self.introAssetsLoaded) {
var allAssetsLoaded = true;
var testAssets = ['centerCircle', 'cloud'];
for (var i = 0; i < testAssets.length; i++) {
var testAsset = LK.getAsset(testAssets[i], {
anchorX: 0.5,
anchorY: 0.5
});
if (!testAsset || !testAsset.width) {
allAssetsLoaded = false;
break;
}
}
if (allAssetsLoaded) {
self.introAssetsLoaded = true;
self.loadingPhase = 1;
// Now that intro assets are loaded, create the intro elements
self.createIntroElements();
// Start loading game assets
LK.setTimeout(self.checkGameAssetsLoaded, 100);
} else {
// Check again in a moment
LK.setTimeout(self.checkIntroAssetsLoaded, 100);
}
}
};
// Check if game assets are loaded
self.checkGameAssetsLoaded = function () {
if (!self.gameAssetsLoaded) {
var allAssetsLoaded = true;
var testAssets = ['farmer', 'wheat', 'zombie_1', 'tomato', 'carrot', 'eggplant', 'sickle'];
for (var i = 0; i < testAssets.length; i++) {
var testAsset = LK.getAsset(testAssets[i], {
anchorX: 0.5,
anchorY: 0.5
});
if (!testAsset || !testAsset.width) {
allAssetsLoaded = false;
break;
}
}
if (allAssetsLoaded) {
self.gameAssetsLoaded = true;
self.loadingPhase = 3;
// Fade out the loading text
tween(loadingText, {
alpha: 0
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
loadingText.destroy();
// Start the intro animation once all loading is complete
if (!self.animationStarted) {
self.animationStarted = true;
self.startAnimation();
}
}
});
} else {
// Update loading phase
self.loadingPhase = 2;
// Check again in a moment
LK.setTimeout(self.checkGameAssetsLoaded, 100);
}
}
};
// Create intro elements once basic assets are loaded
self.createIntroElements = function () {
// Add a sunrise effect background
sunriseBackground = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2,
scaleX: 30,
scaleY: 40,
alpha: 0,
tint: 0xf5cb6c // Sunrise yellow color
});
self.addChild(sunriseBackground);
// Create animated clouds for intro
introClouds = [];
for (var i = 0; i < 3; i++) {
var cloudAsset = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
x: -300 + i * 500,
y: 400 + i * 150,
alpha: 0,
scaleX: 0.8 + i * 0.3,
scaleY: 0.8 + i * 0.3
});
self.addChild(cloudAsset);
introClouds.push(cloudAsset);
}
// Create title text
titleText = new Text2('FARM HARVESTER', {
size: 180,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 4;
titleText.alpha = 0;
self.addChild(titleText);
// Create subtitle text
subtitleText = new Text2('Grow crops, earn money, expand your farm!', {
size: 90,
fill: 0xFFFFFF
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 2048 / 2;
subtitleText.y = titleText.y + 200;
subtitleText.alpha = 0;
self.addChild(subtitleText);
// Create start text only, no background
startText = new Text2('TAP TO START', {
size: 100,
fill: 0xFFFFFF
});
startText.anchor.set(0.5, 0.5);
startText.x = 2048 / 2;
startText.y = 2732 / 2 + 600;
startText.alpha = 0;
self.addChild(startText);
};
// Start the loading process
self.updateLoadingText();
self.checkIntroAssetsLoaded();
// Lazy creation of game assets for intro only when needed
self.createGameIntroAssets = function () {
// Create male and female farmer for intro animation only when needed
if (!maleFarmer) {
maleFarmer = LK.getAsset('farmer', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 - 300,
y: 2732 / 2 + 200,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
});
self.addChild(maleFarmer);
}
if (!femaleFarmer) {
femaleFarmer = LK.getAsset('farmer_walk', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2 + 300,
y: 2732 / 2 + 200,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
});
self.addChild(femaleFarmer);
}
// Create growing plant animation elements only when needed
if (!sproutAsset) {
sproutAsset = LK.getAsset('sprout', {
anchorX: 0.5,
anchorY: 1,
x: 2048 / 2,
y: 2732 / 2 + 400,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
self.addChild(sproutAsset);
}
if (!wheatAsset) {
wheatAsset = LK.getAsset('wheat', {
anchorX: 0.5,
anchorY: 1,
x: 2048 / 2,
y: 2732 / 2 + 400,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
self.addChild(wheatAsset);
}
// Create vegetables that will grow from sprout
if (!carrotAsset) {
carrotAsset = LK.getAsset('carrot', {
anchorX: 0.5,
anchorY: 1,
x: 2048 / 2 - 200,
y: 2732 / 2 + 400,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
self.addChild(carrotAsset);
}
if (!tomatoAsset) {
tomatoAsset = LK.getAsset('tomato', {
anchorX: 0.5,
anchorY: 1,
x: 2048 / 2 + 200,
y: 2732 / 2 + 400,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
self.addChild(tomatoAsset);
}
// Add zombie for intro animation
if (!introZombie) {
introZombie = LK.getAsset('zombie_1', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 + 300,
y: 2732 / 2 + 300,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0
});
self.addChild(introZombie);
}
// Add sickle for intro animation
if (!introSickle) {
introSickle = LK.getAsset('sickle', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2 - 200,
scaleX: 2,
scaleY: 2,
alpha: 0,
rotation: -Math.PI / 4
});
self.addChild(introSickle);
}
};
// Start animation sequence
self.startAnimation = function () {
// Make sure assets are loaded before starting animation
if (self.loadingPhase < 3) {
LK.setTimeout(self.checkGameAssetsLoaded, 100);
return;
}
// Create game assets needed for intro animation
self.createGameIntroAssets();
// Fade in sunrise effect
tween(sunriseBackground, {
alpha: 0.5
}, {
duration: 2000,
easing: tween.easeOut
});
// Animate clouds drifting in
for (var i = 0; i < introClouds.length; i++) {
(function (index) {
var cloud = introClouds[index];
LK.setTimeout(function () {
tween(cloud, {
alpha: 0.7,
x: cloud.x + 400
}, {
duration: 4000,
easing: tween.easeOut
});
}, 500 * index);
})(i);
}
// Fade in and animate farmers running in from sides
LK.setTimeout(function () {
// Animate male farmer running in from left
tween(maleFarmer, {
alpha: 1,
x: 2048 / 2 - 150
}, {
duration: 1500,
easing: tween.easeOut
});
// Animate female farmer running in from right
tween(femaleFarmer, {
alpha: 1,
x: 2048 / 2 + 150
}, {
duration: 1500,
easing: tween.easeOut
});
}, 1000);
// Animate farmers meeting in middle and "planting" sprout
LK.setTimeout(function () {
// Move farmers to center
tween(maleFarmer, {
x: 2048 / 2 - 50
}, {
duration: 800,
easing: tween.easeInOut
});
tween(femaleFarmer, {
x: 2048 / 2 + 50
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Show sprout growing
sproutAsset.alpha = 1;
tween(sproutAsset, {
scaleX: 1,
scaleY: 1
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Show wheat growing from sprout
wheatAsset.alpha = 1;
tween(wheatAsset, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 1000,
easing: tween.easeOut
});
// Show carrot and tomato growing
carrotAsset.alpha = 1;
tween(carrotAsset, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1000,
easing: tween.easeOut
});
tomatoAsset.alpha = 1;
tween(tomatoAsset, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1000,
easing: tween.easeOut
});
}
});
}
});
}, 3000);
// Animate zombie coming in from right
LK.setTimeout(function () {
introZombie.alpha = 1;
tween(introZombie, {
x: 2048 / 2 + 450
}, {
duration: 1500,
easing: tween.easeIn
});
}, 5000);
// Animate sickle appearing and swinging
LK.setTimeout(function () {
introSickle.alpha = 1;
tween(introSickle, {
rotation: Math.PI
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(introZombie, {
alpha: 0,
y: introZombie.y + 100
}, {
duration: 500,
easing: tween.easeIn
});
}
});
}, 6000);
// Fade in title with a fancy effect
LK.setTimeout(function () {
titleText.alpha = 0.1;
tween(titleText, {
alpha: 1,
y: titleText.y + 50
}, {
duration: 1200,
easing: tween.easeOut
});
// Flash effect for title
var _flashTitle = function flashTitle(count) {
if (count <= 0) {
return;
}
tween(titleText, {
alpha: 0.7
}, {
duration: 100,
onFinish: function onFinish() {
tween(titleText, {
alpha: 1
}, {
duration: 100,
onFinish: function onFinish() {
_flashTitle(count - 1);
}
});
}
});
};
LK.setTimeout(function () {
_flashTitle(3);
}, 1200);
}, 7000);
// Fade in subtitle with delay
LK.setTimeout(function () {
tween(subtitleText, {
alpha: 1,
y: subtitleText.y + 30
}, {
duration: 800,
easing: tween.easeOut
});
}, 8000);
// Fade out animation elements and transition to game UI
LK.setTimeout(function () {
// Fade out animation elements
if (maleFarmer) {
tween(maleFarmer, {
alpha: 0
}, {
duration: 800
});
}
if (femaleFarmer) {
tween(femaleFarmer, {
alpha: 0
}, {
duration: 800
});
}
if (sproutAsset) {
tween(sproutAsset, {
alpha: 0
}, {
duration: 800
});
}
if (wheatAsset) {
tween(wheatAsset, {
alpha: 0
}, {
duration: 800
});
}
if (carrotAsset) {
tween(carrotAsset, {
alpha: 0
}, {
duration: 800
});
}
if (tomatoAsset) {
tween(tomatoAsset, {
alpha: 0
}, {
duration: 800
});
}
if (introSickle) {
tween(introSickle, {
alpha: 0
}, {
duration: 800
});
}
// Fade in start text only - startButtonBg is removed
tween(startText, {
alpha: 1
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// No need to pulse since startButtonBg is removed
}
});
}, 9500);
};
// Pulse animation for start button (modified to check if element exists)
self.startPulse = function () {
// Just pulse the start text since background is removed
tween(startText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startText, {
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: self.startPulse
});
}
});
};
// Handle intro screen tap to start the game
self.down = function (x, y, obj) {
// Prevent multiple taps
if (self._startingGame) {
return;
}
self._startingGame = true;
// Play tap animation for text instead
tween(startText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Transition to yellow background
tween(sunriseBackground, {
alpha: 1,
tint: 0xf8e9c0
}, {
duration: 800,
easing: tween.easeOut
});
// Fade out all elements
if (titleText) {
tween(titleText, {
alpha: 0
}, {
duration: 600
});
}
if (subtitleText) {
tween(subtitleText, {
alpha: 0
}, {
duration: 600
});
}
tween(startText, {
alpha: 0
}, {
duration: 600
});
for (var i = 0; i < introClouds.length; i++) {
tween(introClouds[i], {
alpha: 0
}, {
duration: 600
});
}
// Fade out entire intro screen with delay
LK.setTimeout(function () {
tween(self, {
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
// Start/enable actual game
startActualGame();
}
});
}, 800);
}
});
return true; // Prevent event bubbling
};
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
self.update = function () {
if (self.harvested && self.regrowing) {
if (self.regrowCounter > 0) {
// Animate sprout growing to wheat/veg 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 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
if (self.isDead) {
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;
}
}
};
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 = 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);
// --- Setup actual game start function ---
var gameInitialized = false;
var introScreen = null;
function initializeGameElements() {
if (gameInitialized) {
return;
}
// --- Create fields ---
for (var i = 0; i < FIELD_COUNT; ++i) {
var f = new Field();
f.index = i;
f.locked = FIELD_LOCK_COSTS[i] > 0;
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);
}
gameInitialized = true;
}
function startActualGame() {
// The assets should already be loaded due to the optimized intro loading process
// Initialize game elements if not already done
initializeGameElements();
// Set game elements active
game.gameActive = true;
// Play background music
LK.playMusic('backgroundMusic', {
loop: true
});
// Start game timers and events
createClouds();
LK.setTimeout(createBird, 60000);
spawnRooster();
LK.setTimeout(createZombie, 40000);
}
// Create intro screen with enhanced animations
introScreen = new IntroScreen();
game.addChild(introScreen);
// Set game to inactive until intro completes
game.gameActive = false;
// Start intro animation sequence
introScreen.startAnimation();
// Don't initialize game elements yet - wait for intro to complete
// They will be created when the player taps the start button
// Center reference point for positions
var centerX = offsetX + FIELD_W;
var centerY = offsetY + FIELD_H - 250;
// --- Create farmer ---
farmer = new Farmer();
farmer.x = centerX;
farmer.y = 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);
// Buttons removed to simplify the game interface
// --- 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();
// 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) {
// Don't process game inputs if game is not active yet
if (!game.gameActive) {
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) {
// Don't process game inputs if game is not active yet
if (!game.gameActive) {
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) {
// Don't process game inputs if game is not active yet
if (!game.gameActive) {
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 () {
// Only update game elements if game is active (after intro)
if (!game.gameActive) {
// If there's an intro screen, allow it to animate even while game is inactive
if (introScreen && introScreen.parent) {
// Still allow intro screen animations to update
if (introScreen.updateLoadingText && !introScreen.assetsLoaded) {
// Loading animation is handled by setTimeout
}
}
return;
}
// Update farmer if initialized
if (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 two clouds at different positions
function createClouds() {
// Remove any existing clouds
for (var i = 0; i < clouds.length; i++) {
clouds[i].destroy();
}
clouds = [];
// Cloud width for calculations
var cloudWidth = 500;
// Create first cloud (front layer)
var cloud1 = new Cloud();
cloud1.x = -cloudWidth / 2; // Start from off-screen left
cloud1.y = 180; // Higher position near the top
cloud1.layer = 0; // Front layer - this will be in front
cloud1.init(); // Initialize movement and timing
game.addChild(cloud1);
clouds.push(cloud1);
// Create second cloud (back layer)
var cloud2 = new Cloud();
cloud2.x = -cloudWidth / 2 - 200; // Start more separated from first cloud
cloud2.y = 280; // Position below first cloud with more separation
cloud2.layer = 1; // Back layer - this will be behind
cloud2.init(); // Initialize movement and timing
game.addChild(cloud2);
clouds.push(cloud2);
// Schedule creation of new clouds after these disappear
LK.setTimeout(createClouds, 61000); // 61 seconds to ensure smooth transition
}
// 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() {
// Remove any existing birds that might be stuck
for (var i = 0; i < birds.length; i++) {
birds[i].destroy();
}
birds = [];
// Bird asset for size
var tempBirdAsset = LK.getAsset('Bird', {
anchorX: 0.5,
anchorY: 0.5
});
var birdWidth = tempBirdAsset.width;
var birdHeight = tempBirdAsset.height;
// Start at the top of the screen, just off the left edge
var bird = new Bird();
bird.x = -birdWidth / 2;
bird.y = birdHeight / 2 + 20; // 20px padding from the very top
bird.init();
game.addChild(bird);
birds.push(bird);
// Set up a watcher to restart the bird after 60 seconds (3600 ticks at 60fps)
bird._birdLoopCheck = function () {
// Only restart if bird is gone (destroyed)
if (!bird.parent) {
LK.setTimeout(createBird, 60000); // 60 seconds delay before next bird
} else {
LK.setTimeout(bird._birdLoopCheck, 200); // Check again in 200ms
}
};
LK.setTimeout(bird._birdLoopCheck, 200);
}
// Start creating birds after 1 minute (60000 ms)
LK.setTimeout(createBird, 60000);
// Background music will be played when the game starts after the intro sequence completes
// Music playback is now handled in startActualGame()
// --- 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
if (rooster && rooster.parent) {
rooster.destroy();
}
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--;
}
}
};
// Create the zombie
function createZombie() {
// Remove existing zombie if it exists
if (zombie && zombie.parent) {
zombie.destroy();
}
// Create new zombie
zombie = new Zombie();
// 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;
game.addChild(zombie);
// Increase zombie damage each time it spawns
zombieAppearCount++;
if (zombieAppearCount === 1) {
zombieDamageAmount = 500;
} else {
zombieDamageAmount = zombieDamageAmount * 2;
}
}
// Start the first rooster and create zombie
spawnRooster();
// Delay the first zombie spawn by 40 seconds after game start
LK.setTimeout(createZombie, 40000);