/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // --- PlayerHelper class for auto-farming --- var PlayerHelper = Container.expand(function () { var self = Container.call(this); // Attach player image var playerImg = self.attachAsset('Player', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.2, scaleY: 2.2 }); self.x = 0; self.y = 0; self.visible = false; self._active = false; self._timer = null; self._targetSoil = null; self._targetStage = null; self._moveTween = null; self._actionTween = null; self._soils = []; self._harvestQueue = []; self._plantQueue = []; self._state = "idle"; // "idle", "moving", "watering", "harvesting" self._startTime = 0; // Helper: move to a soil with animation self.moveToSoil = function (soil, onArrive) { if (self._moveTween) tween.stop(self._moveTween); self._state = "moving"; // Animate from current position to soil var destX = soil.x; var destY = soil.y - 80; // float above soil self._moveTween = tween(self, { x: destX, y: destY }, { duration: 420, easing: tween.easeInOut, onFinish: function onFinish() { self._state = "arrived"; if (onArrive) onArrive(); } }); }; // Helper: animate watering self.animateWatering = function (soil, onDone) { self._state = "watering"; // Animate: scale up, then back, and flash Sulama image var sulama = LK.getAsset('Sulama', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); sulama.x = soil.x; sulama.y = soil.y - 60; game.addChild(sulama); tween(sulama, { alpha: 0.7 }, { duration: 80 }); tween(sulama.scale, { x: 2.1, y: 2.1 }, { duration: 180, easing: tween.easeOut, onFinish: function onFinish() { tween(sulama.scale, { x: 1.5, y: 1.5 }, { duration: 180, easing: tween.easeIn, onFinish: function onFinish() { if (sulama.parent) sulama.parent.removeChild(sulama); if (onDone) onDone(); } }); } }); }; // Helper: animate harvesting self.animateHarvesting = function (soil, onDone) { self._state = "harvesting"; // Animate: scale up, then back, and flash Orak image var orak = LK.getAsset('Orak', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); orak.x = soil.x; orak.y = soil.y - 60; game.addChild(orak); tween(orak, { alpha: 0.7 }, { duration: 80 }); tween(orak.scale, { x: 2.1, y: 2.1 }, { duration: 180, easing: tween.easeOut, onFinish: function onFinish() { tween(orak.scale, { x: 1.5, y: 1.5 }, { duration: 180, easing: tween.easeIn, onFinish: function onFinish() { if (orak.parent) orak.parent.removeChild(orak); if (onDone) onDone(); } }); } }); }; // Helper: find next soil to act on self.findNextAction = function () { // Prioritize: harvest first, then plant for (var i = 0; i < self._soils.length; i++) { var soil = self._soils[i]; if (soil.growthStage === 4) return { soil: soil, action: "harvest" }; } for (var i = 0; i < self._soils.length; i++) { var soil = self._soils[i]; if (soil.growthStage === 0 && resourceAmounts.wheat > 0) return { soil: soil, action: "plant" }; } return null; }; // Helper: do the next action self.doNextAction = function () { if (!self._active) return; var next = self.findNextAction(); if (!next) { // Idle, check again in 1s self._state = "idle"; self._timer = LK.setTimeout(self.doNextAction, 1000); return; } var soil = next.soil; if (next.action === "harvest") { // Move to soil, then harvest self.moveToSoil(soil, function () { self.animateHarvesting(soil, function () { // Actually harvest // Always harvest exactly 3 crops per yield, never more if (typeof soil._harvestedThisYield === "undefined" || soil._harvestedThisYield !== true) { resourceAmounts.wheat += 3; saveResources(); for (var j = 0; j < resourceAmountTexts.length; j++) { if (resourceAmountTexts[j].type === "wheat") { resourceAmountTexts[j].text.setText(resourceAmounts.wheat + ""); break; } } soil._harvestedThisYield = true; } // Reset soil to empty after harvest // Reset soil to empty after harvest soil.setGrowthStage(0); if (soil.growthTimeout1) LK.clearTimeout(soil.growthTimeout1); if (soil.growthTimeout2) LK.clearTimeout(soil.growthTimeout2); if (soil.growthTimeout3) LK.clearTimeout(soil.growthTimeout3); soil.sulamaUsed = false; // Next action self._timer = LK.setTimeout(self.doNextAction, 400); }); }); } else if (next.action === "plant") { // Only plant if this soil is empty (growthStage 0) if (soil.growthStage === 0 && resourceAmounts.wheat > 0) { // Move to soil, then water/plant self.moveToSoil(soil, function () { self.animateWatering(soil, function () { // Actually plant resourceAmounts.wheat -= 1; saveResources(); for (var j = 0; j < resourceAmountTexts.length; j++) { if (resourceAmountTexts[j].type === "wheat") { resourceAmountTexts[j].text.setText(resourceAmounts.wheat + ""); break; } } LK.getSound('Plant').play(); soil.setGrowthStage(1); // Use sulama for this soil soil.sulamaUsed = true; // Start timer to wheatsec after 10s (faster) if (soil.growthTimeout1) LK.clearTimeout(soil.growthTimeout1); if (soil.growthTimeout2) LK.clearTimeout(soil.growthTimeout2); if (soil.growthTimeout3) LK.clearTimeout(soil.growthTimeout3); soil.growthTimeout1 = LK.setTimeout(function () { soil.setGrowthStage(2); soil.growthTimeout2 = LK.setTimeout(function () { soil.setGrowthStage(3); soil.growthTimeout3 = LK.setTimeout(function () { soil.setGrowthStage(4); // After growth, try next action self._timer = LK.setTimeout(self.doNextAction, 400); }, 10000); }, 10000); }, 10000); // Next action self._timer = LK.setTimeout(self.doNextAction, 400); }); }); } else { // If for some reason the soil is not empty, skip to next action self._timer = LK.setTimeout(self.doNextAction, 100); } } }; // Start helper self.start = function (soils) { self._soils = soils; self._active = true; self.visible = true; // Start at Sulama button var sulamaBtnPos = { x: sulamaBtn.x, y: sulamaBtn.y - 80 }; self.x = sulamaBtnPos.x; self.y = sulamaBtnPos.y; self._startTime = Date.now(); self.doNextAction(); }; // Stop helper self.stop = function () { self._active = false; self.visible = false; if (self._timer) LK.clearTimeout(self._timer); self._timer = null; if (self._moveTween) tween.stop(self._moveTween); if (self._actionTween) tween.stop(self._actionTween); }; return self; }); // (InventoryPopup class removed) // ResourceButton: A button for collecting a resource with cooldown var ResourceButton = Container.expand(function () { var self = Container.call(this); // Properties to be set after creation: // self.resourceType (string) // self.iconId (string) // self.cooldown (ms) // self.onCollect (function) // State self.isCoolingDown = false; self.lastCollectTime = 0; // Button background removed (resourceBtnBg no longer used) // Icon // NOTE: Do not attach icon here, as iconId is not set yet. It will be attached after construction in Game Code. // Attach a placeholder (invisible) so children[1] exists for later replacement. var icon = self.attachAsset('cowIcon', { anchorX: 0.5, anchorY: 0.5, alpha: 0 // invisible placeholder }); icon.y = 0; // Cooldown overlay (semi-transparent) // Attach a placeholder (invisible) so children[2] exists for later replacement. var cooldownOverlay = self.attachAsset('cooldownOverlay', { anchorX: 0.5, anchorY: 0.5, alpha: 0 // invisible placeholder }); // Amount text (shows +1 when collected) var plusText = new Text2("+1", { size: 80, fill: 0xFFFFFF, font: "'Growtopia', 'Arial', sans-serif" }); plusText.anchor.set(0.5, 0.5); plusText.alpha = 0; self.addChild(plusText); // Cooldown timer text var cdText = new Text2("", { size: 60, fill: 0xFFFFFF, font: "'Growtopia', 'Arial', sans-serif" }); cdText.anchor.set(0.5, 0); // Place the cooldown text below the icon (icon is at y=0, icon is 2x scaled, so offset by icon height) cdText.y = 140; self.addChild(cdText); // Button press handler self.down = function (x, y, obj) { if (self.isCoolingDown) return; // Mark as pressed, but do not collect yet self._pressed = true; self._pressValid = true; // Optionally, you could store the press position if you want to check for drags/cancels }; // Collect on up event (tap release) self.up = function (x, y, obj) { if (self.isCoolingDown) return; if (!self._pressed) return; self._pressed = false; if (!self._pressValid) return; // Play cow sound if this is the cow button if (self.resourceType === "cow") { LK.getSound('Cow').play(); } // Play wheat sound if this is the wheat button if (self.resourceType === "wheat") { LK.getSound('Wheat').play(); } // Play ore sound if this is the ore button if (self.resourceType === "ore") { LK.getSound('Ore').play(); } // Play wood sound if this is the wood button if (self.resourceType === "wood") { LK.getSound('Wood').play(); } // Play fish sound if this is the fish button if (self.resourceType === "fish") { LK.getSound('Fish').play(); } self.startCooldown(); if (typeof self.onCollect === "function") self.onCollect(); // Animate +1 plusText.alpha = 1; plusText.y = -40; tween(plusText, { y: -120, alpha: 0 }, { duration: 700, easing: tween.easeOut }); }; // Start cooldown self.startCooldown = function () { self.isCoolingDown = true; self.lastCollectTime = Date.now(); cooldownOverlay.alpha = 0.5; cdText.visible = true; }; // End cooldown self.endCooldown = function () { self.isCoolingDown = false; cooldownOverlay.alpha = 0; cdText.visible = false; }; // Update cooldown overlay and timer self.update = function () { if (!self.isCoolingDown) return; var elapsed = Date.now() - self.lastCollectTime; var left = Math.max(0, self.cooldown - elapsed); if (left <= 0) { self.endCooldown(); } else { // Show seconds left cdText.setText(Math.ceil(left / 1000) + "s"); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d2d2d }); /**** * Game Code ****/ // Resource definitions var resources = [{ type: "cow", iconId: "cowIcon", cooldown: 30000 // 30s }, { type: "fish", iconId: "fishIcon", cooldown: 30000 }, { type: "wheat", iconId: "wheatIcon", cooldown: 30000 }, { type: "ore", iconId: "oreIcon", cooldown: 30000 }, { type: "wood", iconId: "woodIcon", cooldown: 30000 }]; // --- Add 6 soil images to the left middle of the screen in a 2x3 grid, with a gap and vertically centered --- var soilImages = []; var soilCols = 2; var soilRows = 3; var soilGap = 48; // increased gap between soils // Get soil asset size (scaled) var soilSample = LK.getAsset('Soil', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); var soilW = soilSample.width; var soilH = soilSample.height; // Calculate total height of grid var totalSoilHeight = soilRows * soilH + (soilRows - 1) * soilGap; var soilStartY = (2732 - totalSoilHeight) / 2 + soilH / 2; // center grid vertically var soilStartX = 300; // left side, but not too close to the edge var soilSpacingX = soilW + soilGap; var soilSpacingY = soilH + soilGap; // --- Orak/Sulama state --- var orakActive = false; var sulamaActive = false; // --- Add Orak and Sulama buttons below the soils --- // Move both buttons further to the left (by 160px instead of 80px) var buttonY = soilStartY + soilRows * soilSpacingY + 120; var buttonX1 = soilStartX + soilW * 0.5 - 160; var buttonX2 = soilStartX + soilSpacingX + soilW * 0.5 - 100; var orakBtn = LK.getAsset('Orak', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); orakBtn.x = buttonX1; orakBtn.y = buttonY; orakBtn.interactive = true; game.addChild(orakBtn); var sulamaBtn = LK.getAsset('Sulama', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); sulamaBtn.x = buttonX2; sulamaBtn.y = buttonY; sulamaBtn.interactive = true; game.addChild(sulamaBtn); // Visual feedback for button selection function updateButtonStates() { orakBtn.alpha = orakActive ? 1 : 0.6; sulamaBtn.alpha = sulamaActive ? 1 : 0.6; } updateButtonStates(); orakBtn.down = function () { // Toggle orakActive if already active, otherwise activate and deactivate sulama if (orakActive) { orakActive = false; } else { orakActive = true; sulamaActive = false; } updateButtonStates(); // Animate Orak button: scale up then back to normal tween.stop(orakBtn); // Stop any previous tweens orakBtn.scaleX = 1.7; orakBtn.scaleY = 1.7; tween(orakBtn, { scaleX: 1.5, scaleY: 1.5 }, { duration: 180, easing: tween.easeOut }); }; sulamaBtn.down = function () { // Toggle sulamaActive if already active, otherwise activate and deactivate orak if (sulamaActive) { sulamaActive = false; } else { sulamaActive = true; orakActive = false; } updateButtonStates(); // Animate Sulama button: scale up then back to normal tween.stop(sulamaBtn); // Stop any previous tweens sulamaBtn.scaleX = 1.7; sulamaBtn.scaleY = 1.7; tween(sulamaBtn, { scaleX: 1.5, scaleY: 1.5 }, { duration: 180, easing: tween.easeOut }); }; // Do not deactivate orak/sulama when using them on soil, only deactivate when another button is pressed // --- Soil logic --- for (var row = 0; row < soilRows; row++) { for (var col = 0; col < soilCols; col++) { // Create a container for each soil plot var soilContainer = new Container(); soilContainer.x = soilStartX + col * soilSpacingX; soilContainer.y = soilStartY + row * soilSpacingY; game.addChild(soilContainer); // State: 0 = soil, 1 = wheatfirst, 2 = wheatsec, 3 = wheatthird, 4 = wheatlast soilContainer.growthStage = 0; soilContainer.growthTimeout1 = null; soilContainer.growthTimeout2 = null; soilContainer.growthTimeout3 = null; soilContainer.sulamaUsed = false; // Track if sulama was used for this soil // Add the soil image (initial) var soilImg = LK.getAsset('Soil', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); soilContainer.addChild(soilImg); soilContainer.currentImg = soilImg; // Helper to set the growth stage and update image soilContainer.setGrowthStage = function (stage) { // Remove current image if (this.currentImg && this.currentImg.parent) { this.currentImg.parent.removeChild(this.currentImg); } this.growthStage = stage; var imgId = "Soil"; if (stage === 1) imgId = "Wheatfirst";else if (stage === 2) imgId = "Wheatsec";else if (stage === 3) imgId = "Wheatthird";else if (stage === 4) imgId = "Wheatlast"; var newImg = LK.getAsset(imgId, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); this.addChild(newImg); this.currentImg = newImg; }; // Touch logic soilContainer.down = function (x, y, obj) { // Do nothing on down, only act on up }; soilContainer.up = function (x, y, obj) { var self = this; if (self.growthStage === 0) { // Planting only if sulama is active or not if (resourceAmounts.wheat > 0) { // If sulama is active, use it for this soil and set faster growth var useSulama = false; if (sulamaActive) { useSulama = true; self.sulamaUsed = true; // Do NOT deactivate sulamaActive here // sulamaActive = false; // updateButtonStates(); } else { self.sulamaUsed = false; } resourceAmounts.wheat -= 1; saveResources(); // Update wheat amount text if present for (var j = 0; j < resourceAmountTexts.length; j++) { if (resourceAmountTexts[j].type === "wheat") { resourceAmountTexts[j].text.setText(resourceAmounts.wheat + ""); break; } } // Play plant sound LK.getSound('Plant').play(); // Instantly become wheatfirst self.setGrowthStage(1); // Start timer to wheatsec after 15s or 10s if sulama if (self.growthTimeout1) LK.clearTimeout(self.growthTimeout1); if (self.growthTimeout2) LK.clearTimeout(self.growthTimeout2); if (self.growthTimeout3) LK.clearTimeout(self.growthTimeout3); var growTime = self.sulamaUsed ? 10000 : 15000; self.growthTimeout1 = LK.setTimeout(function () { self.setGrowthStage(2); // Start timer to wheatthird after 15s or 10s if sulama var growTime2 = self.sulamaUsed ? 10000 : 15000; self.growthTimeout2 = LK.setTimeout(function () { self.setGrowthStage(3); // Start timer to wheatlast after 15s or 10s if sulama var growTime3 = self.sulamaUsed ? 10000 : 15000; self.growthTimeout3 = LK.setTimeout(function () { self.setGrowthStage(4); }, growTime3); }, growTime2); }, growTime); } } else if (self.growthStage === 4) { // Only allow harvesting if orak is active if (orakActive) { resourceAmounts.wheat += 3; saveResources(); // Update wheat amount text if present for (var j = 0; j < resourceAmountTexts.length; j++) { if (resourceAmountTexts[j].type === "wheat") { resourceAmountTexts[j].text.setText(resourceAmounts.wheat + ""); break; } } self.setGrowthStage(0); if (self.growthTimeout1) LK.clearTimeout(self.growthTimeout1); if (self.growthTimeout2) LK.clearTimeout(self.growthTimeout2); if (self.growthTimeout3) LK.clearTimeout(self.growthTimeout3); self.sulamaUsed = false; // Do NOT deactivate orakActive here // orakActive = false; // updateButtonStates(); } } // Do nothing if in wheatfirst, wheatsec, or wheatthird }; // Make container interactive soilContainer.interactive = true; soilImages.push(soilContainer); } } // --- GLOBAL ARRAYS for resource buttons and amount texts --- var resourceButtons = []; var resourceAmountTexts = []; // --- PlayerHelper global instance and logic --- var playerHelper = null; var playerHelperTimeout = null; function startPlayerHelper() { // Only one at a time if (playerHelper && playerHelper._active) { if (playerHelperTimeout) LK.clearTimeout(playerHelperTimeout); playerHelper.stop(); if (playerHelper.parent) playerHelper.parent.removeChild(playerHelper); } playerHelper = new PlayerHelper(); game.addChild(playerHelper); playerHelper.start(soilImages); // Stop after 5 minutes playerHelperTimeout = LK.setTimeout(function () { if (playerHelper) { playerHelper.stop(); if (playerHelper.parent) playerHelper.parent.removeChild(playerHelper); } }, 5 * 60 * 1000); } // Resource amounts (persisted) var resourceAmounts = { cow: storage.cow || 0, fish: storage.fish || 0, wheat: storage.wheat || 0, ore: storage.ore || 0, wood: storage.wood || 0 }; // Save resource amounts to storage function saveResources() { storage.cow = resourceAmounts.cow; storage.fish = resourceAmounts.fish; storage.wheat = resourceAmounts.wheat; storage.ore = resourceAmounts.ore; storage.wood = resourceAmounts.wood; } // --- START SCREEN LOGIC --- var startScreenShown = false; var startImage = null; var startTween1 = null; var startTween2 = null; var startTween3 = null; var mainScreenInit = false; // Hide all main game UI until start screen is done function showMainScreen() { if (mainScreenInit) return; mainScreenInit = true; // Create resource buttons var btnSpacing = 350; var btnStartY = 900; var btnX = 2048 / 2; window.resourceButtons = []; for (var i = 0; i < resources.length; i++) { var res = resources[i]; var btn = new ResourceButton(); btn.resourceType = res.type; btn.iconId = res.iconId; btn.cooldown = res.cooldown; btn.x = btnX; btn.y = btnStartY + i * btnSpacing; btn.onCollect = function (rtype, btnIdx) { return function () { resourceAmounts[rtype]++; saveResources(); // Update the corresponding amount text for (var j = 0; j < resourceAmountTexts.length; j++) { if (resourceAmountTexts[j].type === rtype) { resourceAmountTexts[j].text.setText(resourceAmounts[rtype] + ""); break; } } }; }(res.type, i); // Set up icon and overlay after iconId is set if (btn.children.length > 1) { btn.removeChild(btn.children[1]); // Remove placeholder icon if exists } var icon = btn.attachAsset(res.iconId, { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); icon.y = 0; if (btn.children.length > 1) { btn.removeChild(btn.children[1]); // Remove placeholder overlay if exists } var overlay = btn.attachAsset('cooldownOverlay', { anchorX: 0.5, anchorY: 0.5, alpha: 0, scaleX: 2, scaleY: 2 }); // Add to game game.addChild(btn); resourceButtons.push(btn); } // Add resource amount text next to each button (1cm = 40px gap) window.resourceAmountTexts = []; // Amount numbers and frames next to items on the main screen have been removed as requested. } // Show start image and animate, then show main screen function showStartScreen() { if (startScreenShown) return; startScreenShown = true; // Create start image centered startImage = LK.getAsset('Start', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); startImage.x = 2048 / 2; startImage.y = 2732 / 2; startImage.alpha = 0; startImage.scaleX = 0.7; startImage.scaleY = 0.7; game.addChild(startImage); // Fade in and scale up startTween1 = tween(startImage, { alpha: 1, scaleX: 1.1, scaleY: 1.1 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Pulse scale startTween2 = tween(startImage, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { // Wait, then fade out and remove startTween3 = tween(startImage, { alpha: 0, scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { if (startImage && startImage.parent) startImage.parent.removeChild(startImage); startImage = null; showMainScreen(); } }); } }); } }); } // Start the game with the start screen showStartScreen(); // Place the start button at the same place as the pause button (top right corner) if (typeof LK.gui !== "undefined" && LK.gui.topRight) { // If there is a start button, place it at the top right if (typeof LK.startButton !== "undefined") { LK.gui.topRight.addChild(LK.startButton); } } // Add merchant button with image at the bottom right corner var merchantCornerBtn = LK.getAsset('Merchant', { anchorX: 0.5, anchorY: 0.5 }); merchantCornerBtn.x = 2048 - 180; merchantCornerBtn.y = 2732 - 180; merchantCornerBtn.scaleX = 0.8; merchantCornerBtn.scaleY = 0.8; game.addChild(merchantCornerBtn); // Open merchant popup when tapped merchantCornerBtn.down = function (x, y, obj) { if (merchantPopup) return; // Block input to resource buttons for (var i = 0; i < resourceButtons.length; i++) { resourceButtons[i].interactive = false; } // Directly open the merchant popup logic (copied from yellowBtn.down) if (merchantPopup) return; // Fade overlay merchantFade = new Container(); var fadeBg = LK.getAsset('cooldownOverlay', { anchorX: 0, anchorY: 0, width: 2048, height: 2732, color: 0x000000, alpha: 0.0 }); merchantFade.addChild(fadeBg); merchantFade.interactive = true; // Block input to game behind merchantFade.zIndex = 1000; game.addChild(merchantFade); // Animate fade in tween(fadeBg, { alpha: 0.7 }, { duration: 350, easing: tween.easeOut }); // Popup box merchantPopup = new Container(); // Expand width for more space var boxW = 1200, boxH = 1400; var popupBg = LK.getAsset('inventoryBg', { anchorX: 0.5, anchorY: 0.5, width: boxW, height: boxH, color: 0x333333 }); merchantPopup.addChild(popupBg); merchantPopup.x = 2048 / 2; merchantPopup.y = 2732 / 2; merchantPopup.zIndex = 1001; // Merchant "avatar" (just a text for now) var merchantText = new Text2("Merchant", { size: 120, fill: 0xFFE066, font: "'Growtopia', 'Arial', sans-serif" }); merchantText.anchor.set(0.5, 0); merchantText.x = 0; merchantText.y = -boxH / 2 + 60; merchantPopup.addChild(merchantText); // Info text var infoText = new Text2("Sell your items for gold!", { size: 60, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); infoText.anchor.set(0.5, 0); infoText.x = 0; infoText.y = merchantText.y + 150; merchantPopup.addChild(infoText); // Gold display if (typeof storage.gold === "undefined") storage.gold = 0; var goldAmount = storage.gold || 0; var goldText = new Text2("Gold: " + goldAmount, { size: 80, fill: 0xFFD700, font: "'Growtopia', 'Arial', sans-serif" }); goldText.anchor.set(0.5, 0); // Add a gap between gold text and info text goldText.x = 0; goldText.y = infoText.y + 80; merchantPopup.addChild(goldText); // Resource sell button definitions var sellDefs = [{ type: "wood", icon: "woodIcon", price: 2, label: "Wood" }, { type: "fish", icon: "fishIcon", price: 1, label: "Fish" }, { type: "cow", icon: "cowIcon", price: 3, label: "Cow" }, { type: "ore", icon: "oreIcon", price: 4, label: "Ore" }, { type: "wheat", icon: "wheatIcon", price: 1, label: "Wheat" }, { //{player-merchant} type: "player", icon: "Player", price: 50, label: "Helper" }]; // Layout: vertical stack, centered, with clear horizontal separation for icon, label, amount, price, and button // Move sell buttons further down to avoid overlap with gold text var sellBtnStartY = goldText.y + 180; // Increased gap below gold text var sellBtnSpacing = 210; // Increased vertical spacing between rows var sellBtnW = 1100, // Expanded width for more space sellBtnH = 150; var merchantSellBtns = []; for (var i = 0; i < sellDefs.length; i++) { (function (i) { var def = sellDefs[i]; var btnY = sellBtnStartY + i * sellBtnSpacing; // Calculate horizontal layout: [amount owned] [icon] [label] [amount input] [price] [sell button] // All elements vertically centered at btnY // Amount owned (leftmost) var amtOwned = resourceAmounts[def.type] || 0; var amtOwnedText = new Text2(amtOwned + "", { size: 64, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); amtOwnedText.anchor.set(1, 0.5); amtOwnedText.x = -sellBtnW / 2 + 60; amtOwnedText.y = btnY; merchantPopup.addChild(amtOwnedText); // Icon var icon = LK.getAsset(def.icon, { anchorX: 0.5, anchorY: 0.5 }); icon.x = -sellBtnW / 2 + 160; icon.y = btnY; merchantPopup.addChild(icon); // Resource label var resLabel = new Text2(def.label, { size: 64, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); resLabel.anchor.set(0, 0.5); resLabel.x = -sellBtnW / 2 + 260; resLabel.y = btnY; merchantPopup.addChild(resLabel); // --- Amount selector state --- var sellAmount = 1; // --- Input field for direct amount entry, centered between label and price --- // Center input between label and price var amtInputCenterX = 0; // center of the row // Place input box in the middle of the row, with "x" to its left var amtText = new Text2("x", { size: 64, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); amtText.anchor.set(1, 0.5); amtText.x = amtInputCenterX - 70; amtText.y = btnY; merchantPopup.addChild(amtText); var amtInputBg = LK.getAsset('cooldownOverlay', { anchorX: 0.5, anchorY: 0.5, width: 120, height: 80, color: 0x222222, alpha: 0.7 }); amtInputBg.x = amtInputCenterX + 10; amtInputBg.y = btnY; merchantPopup.addChild(amtInputBg); var amtInputText = new Text2(sellAmount + "", { size: 54, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); amtInputText.anchor.set(0.5, 0.5); amtInputText.x = amtInputBg.x; amtInputText.y = amtInputBg.y; merchantPopup.addChild(amtInputText); // Make input field interactive for tap-to-edit amtInputBg.interactive = true; amtInputText.interactive = true; // Helper: show a simple number input popup function showNumberInputPopup(currentValue, maxValue, onDone) { // Remove any existing popup if (merchantPopup._numberInputPopup) { merchantPopup.removeChild(merchantPopup._numberInputPopup); merchantPopup._numberInputPopup = null; } var popup = new Container(); popup.zIndex = 2000; // Add Numararka image as background behind the popup var numararkaBg = LK.getAsset('Numararka', { anchorX: 0.5, anchorY: 0.5, scaleX: 7, scaleY: 7, alpha: 1 }); // Move Numararka further down so number 6 is centered on it numararkaBg.y = 180; // Move image down by 180px (increased from 80px for better centering) // Place numararkaBg at the back of the popup popup.addChild(numararkaBg); // Removed black overlay background from number input popup popup.x = amtInputBg.x; popup.y = amtInputBg.y + 200; var prompt = new Text2("Enter amount", { size: 80, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); prompt.anchor.set(0.5, 0); prompt.y = -180; popup.addChild(prompt); var inputVal = "0"; var inputText = new Text2(inputVal, { size: 120, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); inputText.anchor.set(0.5, 0.5); inputText.y = -60; popup.addChild(inputText); // Add number buttons 1-9, 0, backspace, OK var btns = []; var btnSize = 120; var btnGap = 24; var nums = [[1, 2, 3], [4, 5, 6], [7, 8, 9], ["←", 0, "OK"]]; // Place all buttons and their labels above the numararka image (which is already at the back) for (var r = 0; r < 4; r++) { for (var c = 0; c < 3; c++) { (function (r, c) { var val = nums[r][c]; var btn = LK.getAsset('resourceBtnBg', { anchorX: 0.5, anchorY: 0.5, width: btnSize, height: btnSize, color: 0x333333, alpha: 0.95 }); btn.x = (c - 1) * (btnSize + btnGap); btn.y = 80 + r * (btnSize + btnGap); btn.interactive = true; popup.addChild(btn); var label = new Text2(val + "", { size: 72, fill: "#fff", font: "'Growtopia', 'Arial', sans-serif" }); label.anchor.set(0.5, 0.5); label.x = btn.x; label.y = btn.y; popup.addChild(label); btn.down = function () { if (val === "OK") { var n = parseInt(inputVal, 10); if (isNaN(n) || n < 1) n = 1; if (n > maxValue) n = maxValue; onDone(n); merchantPopup.removeChild(popup); merchantPopup._numberInputPopup = null; } else if (val === "←") { if (inputVal.length > 1) { inputVal = inputVal.slice(0, -1); } else { inputVal = "1"; } // Only clamp to 1, not to maxValue, so user can type any number var n = parseInt(inputVal, 10); if (isNaN(n) || n < 1) n = 1; inputVal = n + ""; inputText.setText(inputVal); } else { if (inputVal === "0") inputVal = ""; if (inputVal.length < 4) { inputVal += val + ""; var n = parseInt(inputVal, 10); if (isNaN(n) || n < 1) n = 1; inputVal = n + ""; inputText.setText(inputVal); } } }; btns.push(btn); })(r, c); } } // Add invisible overlay to catch outside clicks and close popup var closeOverlay = LK.getAsset('cooldownOverlay', { anchorX: 0.5, anchorY: 0.5, width: 2048, height: 2732, alpha: 0, color: 0x000000 }); closeOverlay.x = 0; closeOverlay.y = 0; closeOverlay.zIndex = -100; // Ensure it's behind the popup content closeOverlay.interactive = true; closeOverlay.down = function (x, y, obj) { // Only close if the click is not on a number button or input if (merchantPopup && merchantPopup._numberInputPopup) { merchantPopup.removeChild(merchantPopup._numberInputPopup); merchantPopup._numberInputPopup = null; } }; // Insert overlay as first child so it sits behind popup content but above merchantPopup bg popup.addChildAt(closeOverlay, 0); merchantPopup.addChild(popup); merchantPopup._numberInputPopup = popup; } // When input field is tapped, show number input popup amtInputBg.down = amtInputText.down = function () { var maxSell = resourceAmounts[def.type]; showNumberInputPopup(sellAmount, maxSell, function (newVal) { sellAmount = newVal; amtText.setText("x" + sellAmount); amtInputText.setText(sellAmount + ""); }); }; // (Removed arrow logic for amount increase/decrease) // Price var priceText = new Text2(def.price + " coins", { size: 56, fill: 0xFFD700, font: "'Growtopia', 'Arial', sans-serif" }); priceText.anchor.set(1, 0.5); // Add more horizontal space between amount selector and coins text priceText.x = sellBtnW / 2 - 100; // was -120, now -100 for more gap after amount selector priceText.y = btnY; merchantPopup.addChild(priceText); // Sell button var sellBtn = LK.getAsset('inventoryBtn', { anchorX: 0.5, anchorY: 0.5, color: 0xFFE066 }); sellBtn.x = sellBtnW / 2 - 30; // was -80, now -30 for more gap after coins text sellBtn.y = btnY; merchantPopup.addChild(sellBtn); // Sell button label (removed) // Sell logic var sellAllTimeout = null; var sellAllInterval = null; sellBtn.down = function (x, y, obj) { if (def.type === "player") { // Buy the helper for 5 minutes if (goldAmount >= def.price) { goldAmount -= def.price; storage.gold = goldAmount; goldText.setText("Gold: " + goldAmount); LK.getSound('Coin').play(); // Animate gold text goldText.scale.set(1.2, 1.2); tween(goldText.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.easeOut }); amtOwnedText.setText("∞"); // Start helper logic startPlayerHelper(); } return; } // Start a timeout to trigger sell all after 500ms if (resourceAmounts[def.type] > 0) { // Sell the selected amount (or as much as possible) var toSell = Math.min(sellAmount, resourceAmounts[def.type]); if (toSell > 0) { resourceAmounts[def.type] -= toSell; storage[def.type] = resourceAmounts[def.type]; goldAmount += def.price * toSell; storage.gold = goldAmount; LK.getSound('Coin').play(); amtText.setText("x" + Math.min(sellAmount, resourceAmounts[def.type])); goldText.setText("Gold: " + goldAmount); // Also update main screen resource amount for (var j = 0; j < resourceAmountTexts.length; j++) { if (resourceAmountTexts[j].type === def.type) { resourceAmountTexts[j].text.setText(resourceAmounts[def.type] + ""); break; } } // Update merchant popup amount demonstrator amtOwnedText.setText(resourceAmounts[def.type] + ""); // Clamp sellAmount to available if (resourceAmounts[def.type] < sellAmount) { sellAmount = Math.max(1, resourceAmounts[def.type]); amtText.setText("x" + sellAmount); } // Animate gold text goldText.scale.set(1.2, 1.2); tween(goldText.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.easeOut }); } } // Start long-press detection if (sellAllTimeout) LK.clearTimeout(sellAllTimeout); if (sellAllInterval) LK.clearInterval(sellAllInterval); sellAllTimeout = LK.setTimeout(function () { // Start selling all at a fast interval sellAllInterval = LK.setInterval(function () { if (resourceAmounts[def.type] > 0) { // Sell the selected amount (or as much as possible) var toSell = Math.min(sellAmount, resourceAmounts[def.type]); if (toSell > 0) { resourceAmounts[def.type] -= toSell; storage[def.type] = resourceAmounts[def.type]; goldAmount += def.price * toSell; storage.gold = goldAmount; LK.getSound('Coin').play(); amtText.setText("x" + Math.min(sellAmount, resourceAmounts[def.type])); goldText.setText("Gold: " + goldAmount); // Also update main screen resource amount for (var j = 0; j < resourceAmountTexts.length; j++) { if (resourceAmountTexts[j].type === def.type) { resourceAmountTexts[j].text.setText(resourceAmounts[def.type] + ""); break; } } // Update merchant popup amount demonstrator amtOwnedText.setText(resourceAmounts[def.type] + ""); // Clamp sellAmount to available if (resourceAmounts[def.type] < sellAmount) { sellAmount = Math.max(1, resourceAmounts[def.type]); amtText.setText("x" + sellAmount); } // Animate gold text goldText.scale.set(1.2, 1.2); tween(goldText.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.easeOut }); } } else { // Stop interval if nothing left to sell if (sellAllInterval) LK.clearInterval(sellAllInterval); sellAllInterval = null; } }, 50); // Sell every 50ms }, 500); // Long press threshold: 500ms }; // Stop selling on up sellBtn.up = function (x, y, obj) { if (sellAllTimeout) LK.clearTimeout(sellAllTimeout); sellAllTimeout = null; if (sellAllInterval) LK.clearInterval(sellAllInterval); sellAllInterval = null; }; merchantSellBtns.push({ btn: sellBtn, amtText: amtText }); })(i); } // Close button var closeBtn = LK.getAsset('closeBtn', { anchorX: 0.5, anchorY: 0.5 }); closeBtn.x = boxW / 2 - 60; closeBtn.y = -boxH / 2 + 60; merchantPopup.addChild(closeBtn); // Close logic closeBtn.down = function () { // Restore input to resource buttons for (var i = 0; i < resourceButtons.length; i++) { resourceButtons[i].interactive = true; } // Animate fade out if (merchantFade && merchantFade.children.length > 0) { tween(merchantFade.children[0], { alpha: 0 }, { duration: 250, easing: tween.easeIn, onFinish: function onFinish() { if (merchantFade && merchantFade.parent) merchantFade.parent.removeChild(merchantFade); merchantFade = null; } }); } else if (merchantFade && merchantFade.parent) { merchantFade.parent.removeChild(merchantFade); merchantFade = null; } if (merchantPopup && merchantPopup.parent) merchantPopup.parent.removeChild(merchantPopup); merchantPopup = null; }; // Add popup to game game.addChild(merchantPopup); }; // Only keep the merchant button at the bottom right corner // (Removed button "a" and its label) // --- Merchant Popup logic --- var merchantPopup = null; var merchantFade = null; // (yellowBtn and blueBtn handlers removed) // Play background music when the game starts LK.playMusic('Bg'); // Game update loop game.update = function () { // Update all resource buttons (for cooldowns) for (var i = 0; i < resourceButtons.length; i++) { resourceButtons[i].update(); } }; // --- Asset Initialization Section --- // Resource button background // Cooldown overlay (semi-transparent) // Inventory popup background // Inventory button (top right) // Close button for popup // Resource icons // Set Growtopia font as default for all Text2 if (typeof Text2 !== "undefined") { Text2.defaultFont = "'Growtopia', 'Arial', sans-serif"; } // Stop background music when the game is paused game.pause = function () { LK.stopMusic(); }; // Resume background music when the game continues game["continue"] = function () { LK.playMusic('Bg'); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// --- PlayerHelper class for auto-farming ---
var PlayerHelper = Container.expand(function () {
var self = Container.call(this);
// Attach player image
var playerImg = self.attachAsset('Player', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 2.2
});
self.x = 0;
self.y = 0;
self.visible = false;
self._active = false;
self._timer = null;
self._targetSoil = null;
self._targetStage = null;
self._moveTween = null;
self._actionTween = null;
self._soils = [];
self._harvestQueue = [];
self._plantQueue = [];
self._state = "idle"; // "idle", "moving", "watering", "harvesting"
self._startTime = 0;
// Helper: move to a soil with animation
self.moveToSoil = function (soil, onArrive) {
if (self._moveTween) tween.stop(self._moveTween);
self._state = "moving";
// Animate from current position to soil
var destX = soil.x;
var destY = soil.y - 80; // float above soil
self._moveTween = tween(self, {
x: destX,
y: destY
}, {
duration: 420,
easing: tween.easeInOut,
onFinish: function onFinish() {
self._state = "arrived";
if (onArrive) onArrive();
}
});
};
// Helper: animate watering
self.animateWatering = function (soil, onDone) {
self._state = "watering";
// Animate: scale up, then back, and flash Sulama image
var sulama = LK.getAsset('Sulama', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
sulama.x = soil.x;
sulama.y = soil.y - 60;
game.addChild(sulama);
tween(sulama, {
alpha: 0.7
}, {
duration: 80
});
tween(sulama.scale, {
x: 2.1,
y: 2.1
}, {
duration: 180,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(sulama.scale, {
x: 1.5,
y: 1.5
}, {
duration: 180,
easing: tween.easeIn,
onFinish: function onFinish() {
if (sulama.parent) sulama.parent.removeChild(sulama);
if (onDone) onDone();
}
});
}
});
};
// Helper: animate harvesting
self.animateHarvesting = function (soil, onDone) {
self._state = "harvesting";
// Animate: scale up, then back, and flash Orak image
var orak = LK.getAsset('Orak', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
orak.x = soil.x;
orak.y = soil.y - 60;
game.addChild(orak);
tween(orak, {
alpha: 0.7
}, {
duration: 80
});
tween(orak.scale, {
x: 2.1,
y: 2.1
}, {
duration: 180,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(orak.scale, {
x: 1.5,
y: 1.5
}, {
duration: 180,
easing: tween.easeIn,
onFinish: function onFinish() {
if (orak.parent) orak.parent.removeChild(orak);
if (onDone) onDone();
}
});
}
});
};
// Helper: find next soil to act on
self.findNextAction = function () {
// Prioritize: harvest first, then plant
for (var i = 0; i < self._soils.length; i++) {
var soil = self._soils[i];
if (soil.growthStage === 4) return {
soil: soil,
action: "harvest"
};
}
for (var i = 0; i < self._soils.length; i++) {
var soil = self._soils[i];
if (soil.growthStage === 0 && resourceAmounts.wheat > 0) return {
soil: soil,
action: "plant"
};
}
return null;
};
// Helper: do the next action
self.doNextAction = function () {
if (!self._active) return;
var next = self.findNextAction();
if (!next) {
// Idle, check again in 1s
self._state = "idle";
self._timer = LK.setTimeout(self.doNextAction, 1000);
return;
}
var soil = next.soil;
if (next.action === "harvest") {
// Move to soil, then harvest
self.moveToSoil(soil, function () {
self.animateHarvesting(soil, function () {
// Actually harvest
// Always harvest exactly 3 crops per yield, never more
if (typeof soil._harvestedThisYield === "undefined" || soil._harvestedThisYield !== true) {
resourceAmounts.wheat += 3;
saveResources();
for (var j = 0; j < resourceAmountTexts.length; j++) {
if (resourceAmountTexts[j].type === "wheat") {
resourceAmountTexts[j].text.setText(resourceAmounts.wheat + "");
break;
}
}
soil._harvestedThisYield = true;
}
// Reset soil to empty after harvest
// Reset soil to empty after harvest
soil.setGrowthStage(0);
if (soil.growthTimeout1) LK.clearTimeout(soil.growthTimeout1);
if (soil.growthTimeout2) LK.clearTimeout(soil.growthTimeout2);
if (soil.growthTimeout3) LK.clearTimeout(soil.growthTimeout3);
soil.sulamaUsed = false;
// Next action
self._timer = LK.setTimeout(self.doNextAction, 400);
});
});
} else if (next.action === "plant") {
// Only plant if this soil is empty (growthStage 0)
if (soil.growthStage === 0 && resourceAmounts.wheat > 0) {
// Move to soil, then water/plant
self.moveToSoil(soil, function () {
self.animateWatering(soil, function () {
// Actually plant
resourceAmounts.wheat -= 1;
saveResources();
for (var j = 0; j < resourceAmountTexts.length; j++) {
if (resourceAmountTexts[j].type === "wheat") {
resourceAmountTexts[j].text.setText(resourceAmounts.wheat + "");
break;
}
}
LK.getSound('Plant').play();
soil.setGrowthStage(1);
// Use sulama for this soil
soil.sulamaUsed = true;
// Start timer to wheatsec after 10s (faster)
if (soil.growthTimeout1) LK.clearTimeout(soil.growthTimeout1);
if (soil.growthTimeout2) LK.clearTimeout(soil.growthTimeout2);
if (soil.growthTimeout3) LK.clearTimeout(soil.growthTimeout3);
soil.growthTimeout1 = LK.setTimeout(function () {
soil.setGrowthStage(2);
soil.growthTimeout2 = LK.setTimeout(function () {
soil.setGrowthStage(3);
soil.growthTimeout3 = LK.setTimeout(function () {
soil.setGrowthStage(4);
// After growth, try next action
self._timer = LK.setTimeout(self.doNextAction, 400);
}, 10000);
}, 10000);
}, 10000);
// Next action
self._timer = LK.setTimeout(self.doNextAction, 400);
});
});
} else {
// If for some reason the soil is not empty, skip to next action
self._timer = LK.setTimeout(self.doNextAction, 100);
}
}
};
// Start helper
self.start = function (soils) {
self._soils = soils;
self._active = true;
self.visible = true;
// Start at Sulama button
var sulamaBtnPos = {
x: sulamaBtn.x,
y: sulamaBtn.y - 80
};
self.x = sulamaBtnPos.x;
self.y = sulamaBtnPos.y;
self._startTime = Date.now();
self.doNextAction();
};
// Stop helper
self.stop = function () {
self._active = false;
self.visible = false;
if (self._timer) LK.clearTimeout(self._timer);
self._timer = null;
if (self._moveTween) tween.stop(self._moveTween);
if (self._actionTween) tween.stop(self._actionTween);
};
return self;
});
// (InventoryPopup class removed)
// ResourceButton: A button for collecting a resource with cooldown
var ResourceButton = Container.expand(function () {
var self = Container.call(this);
// Properties to be set after creation:
// self.resourceType (string)
// self.iconId (string)
// self.cooldown (ms)
// self.onCollect (function)
// State
self.isCoolingDown = false;
self.lastCollectTime = 0;
// Button background removed (resourceBtnBg no longer used)
// Icon
// NOTE: Do not attach icon here, as iconId is not set yet. It will be attached after construction in Game Code.
// Attach a placeholder (invisible) so children[1] exists for later replacement.
var icon = self.attachAsset('cowIcon', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0 // invisible placeholder
});
icon.y = 0;
// Cooldown overlay (semi-transparent)
// Attach a placeholder (invisible) so children[2] exists for later replacement.
var cooldownOverlay = self.attachAsset('cooldownOverlay', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0 // invisible placeholder
});
// Amount text (shows +1 when collected)
var plusText = new Text2("+1", {
size: 80,
fill: 0xFFFFFF,
font: "'Growtopia', 'Arial', sans-serif"
});
plusText.anchor.set(0.5, 0.5);
plusText.alpha = 0;
self.addChild(plusText);
// Cooldown timer text
var cdText = new Text2("", {
size: 60,
fill: 0xFFFFFF,
font: "'Growtopia', 'Arial', sans-serif"
});
cdText.anchor.set(0.5, 0);
// Place the cooldown text below the icon (icon is at y=0, icon is 2x scaled, so offset by icon height)
cdText.y = 140;
self.addChild(cdText);
// Button press handler
self.down = function (x, y, obj) {
if (self.isCoolingDown) return;
// Mark as pressed, but do not collect yet
self._pressed = true;
self._pressValid = true;
// Optionally, you could store the press position if you want to check for drags/cancels
};
// Collect on up event (tap release)
self.up = function (x, y, obj) {
if (self.isCoolingDown) return;
if (!self._pressed) return;
self._pressed = false;
if (!self._pressValid) return;
// Play cow sound if this is the cow button
if (self.resourceType === "cow") {
LK.getSound('Cow').play();
}
// Play wheat sound if this is the wheat button
if (self.resourceType === "wheat") {
LK.getSound('Wheat').play();
}
// Play ore sound if this is the ore button
if (self.resourceType === "ore") {
LK.getSound('Ore').play();
}
// Play wood sound if this is the wood button
if (self.resourceType === "wood") {
LK.getSound('Wood').play();
}
// Play fish sound if this is the fish button
if (self.resourceType === "fish") {
LK.getSound('Fish').play();
}
self.startCooldown();
if (typeof self.onCollect === "function") self.onCollect();
// Animate +1
plusText.alpha = 1;
plusText.y = -40;
tween(plusText, {
y: -120,
alpha: 0
}, {
duration: 700,
easing: tween.easeOut
});
};
// Start cooldown
self.startCooldown = function () {
self.isCoolingDown = true;
self.lastCollectTime = Date.now();
cooldownOverlay.alpha = 0.5;
cdText.visible = true;
};
// End cooldown
self.endCooldown = function () {
self.isCoolingDown = false;
cooldownOverlay.alpha = 0;
cdText.visible = false;
};
// Update cooldown overlay and timer
self.update = function () {
if (!self.isCoolingDown) return;
var elapsed = Date.now() - self.lastCollectTime;
var left = Math.max(0, self.cooldown - elapsed);
if (left <= 0) {
self.endCooldown();
} else {
// Show seconds left
cdText.setText(Math.ceil(left / 1000) + "s");
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d2d2d
});
/****
* Game Code
****/
// Resource definitions
var resources = [{
type: "cow",
iconId: "cowIcon",
cooldown: 30000 // 30s
}, {
type: "fish",
iconId: "fishIcon",
cooldown: 30000
}, {
type: "wheat",
iconId: "wheatIcon",
cooldown: 30000
}, {
type: "ore",
iconId: "oreIcon",
cooldown: 30000
}, {
type: "wood",
iconId: "woodIcon",
cooldown: 30000
}];
// --- Add 6 soil images to the left middle of the screen in a 2x3 grid, with a gap and vertically centered ---
var soilImages = [];
var soilCols = 2;
var soilRows = 3;
var soilGap = 48; // increased gap between soils
// Get soil asset size (scaled)
var soilSample = LK.getAsset('Soil', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
var soilW = soilSample.width;
var soilH = soilSample.height;
// Calculate total height of grid
var totalSoilHeight = soilRows * soilH + (soilRows - 1) * soilGap;
var soilStartY = (2732 - totalSoilHeight) / 2 + soilH / 2; // center grid vertically
var soilStartX = 300; // left side, but not too close to the edge
var soilSpacingX = soilW + soilGap;
var soilSpacingY = soilH + soilGap;
// --- Orak/Sulama state ---
var orakActive = false;
var sulamaActive = false;
// --- Add Orak and Sulama buttons below the soils ---
// Move both buttons further to the left (by 160px instead of 80px)
var buttonY = soilStartY + soilRows * soilSpacingY + 120;
var buttonX1 = soilStartX + soilW * 0.5 - 160;
var buttonX2 = soilStartX + soilSpacingX + soilW * 0.5 - 100;
var orakBtn = LK.getAsset('Orak', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
orakBtn.x = buttonX1;
orakBtn.y = buttonY;
orakBtn.interactive = true;
game.addChild(orakBtn);
var sulamaBtn = LK.getAsset('Sulama', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
sulamaBtn.x = buttonX2;
sulamaBtn.y = buttonY;
sulamaBtn.interactive = true;
game.addChild(sulamaBtn);
// Visual feedback for button selection
function updateButtonStates() {
orakBtn.alpha = orakActive ? 1 : 0.6;
sulamaBtn.alpha = sulamaActive ? 1 : 0.6;
}
updateButtonStates();
orakBtn.down = function () {
// Toggle orakActive if already active, otherwise activate and deactivate sulama
if (orakActive) {
orakActive = false;
} else {
orakActive = true;
sulamaActive = false;
}
updateButtonStates();
// Animate Orak button: scale up then back to normal
tween.stop(orakBtn); // Stop any previous tweens
orakBtn.scaleX = 1.7;
orakBtn.scaleY = 1.7;
tween(orakBtn, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 180,
easing: tween.easeOut
});
};
sulamaBtn.down = function () {
// Toggle sulamaActive if already active, otherwise activate and deactivate orak
if (sulamaActive) {
sulamaActive = false;
} else {
sulamaActive = true;
orakActive = false;
}
updateButtonStates();
// Animate Sulama button: scale up then back to normal
tween.stop(sulamaBtn); // Stop any previous tweens
sulamaBtn.scaleX = 1.7;
sulamaBtn.scaleY = 1.7;
tween(sulamaBtn, {
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 180,
easing: tween.easeOut
});
};
// Do not deactivate orak/sulama when using them on soil, only deactivate when another button is pressed
// --- Soil logic ---
for (var row = 0; row < soilRows; row++) {
for (var col = 0; col < soilCols; col++) {
// Create a container for each soil plot
var soilContainer = new Container();
soilContainer.x = soilStartX + col * soilSpacingX;
soilContainer.y = soilStartY + row * soilSpacingY;
game.addChild(soilContainer);
// State: 0 = soil, 1 = wheatfirst, 2 = wheatsec, 3 = wheatthird, 4 = wheatlast
soilContainer.growthStage = 0;
soilContainer.growthTimeout1 = null;
soilContainer.growthTimeout2 = null;
soilContainer.growthTimeout3 = null;
soilContainer.sulamaUsed = false; // Track if sulama was used for this soil
// Add the soil image (initial)
var soilImg = LK.getAsset('Soil', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
soilContainer.addChild(soilImg);
soilContainer.currentImg = soilImg;
// Helper to set the growth stage and update image
soilContainer.setGrowthStage = function (stage) {
// Remove current image
if (this.currentImg && this.currentImg.parent) {
this.currentImg.parent.removeChild(this.currentImg);
}
this.growthStage = stage;
var imgId = "Soil";
if (stage === 1) imgId = "Wheatfirst";else if (stage === 2) imgId = "Wheatsec";else if (stage === 3) imgId = "Wheatthird";else if (stage === 4) imgId = "Wheatlast";
var newImg = LK.getAsset(imgId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
this.addChild(newImg);
this.currentImg = newImg;
};
// Touch logic
soilContainer.down = function (x, y, obj) {
// Do nothing on down, only act on up
};
soilContainer.up = function (x, y, obj) {
var self = this;
if (self.growthStage === 0) {
// Planting only if sulama is active or not
if (resourceAmounts.wheat > 0) {
// If sulama is active, use it for this soil and set faster growth
var useSulama = false;
if (sulamaActive) {
useSulama = true;
self.sulamaUsed = true;
// Do NOT deactivate sulamaActive here
// sulamaActive = false;
// updateButtonStates();
} else {
self.sulamaUsed = false;
}
resourceAmounts.wheat -= 1;
saveResources();
// Update wheat amount text if present
for (var j = 0; j < resourceAmountTexts.length; j++) {
if (resourceAmountTexts[j].type === "wheat") {
resourceAmountTexts[j].text.setText(resourceAmounts.wheat + "");
break;
}
}
// Play plant sound
LK.getSound('Plant').play();
// Instantly become wheatfirst
self.setGrowthStage(1);
// Start timer to wheatsec after 15s or 10s if sulama
if (self.growthTimeout1) LK.clearTimeout(self.growthTimeout1);
if (self.growthTimeout2) LK.clearTimeout(self.growthTimeout2);
if (self.growthTimeout3) LK.clearTimeout(self.growthTimeout3);
var growTime = self.sulamaUsed ? 10000 : 15000;
self.growthTimeout1 = LK.setTimeout(function () {
self.setGrowthStage(2);
// Start timer to wheatthird after 15s or 10s if sulama
var growTime2 = self.sulamaUsed ? 10000 : 15000;
self.growthTimeout2 = LK.setTimeout(function () {
self.setGrowthStage(3);
// Start timer to wheatlast after 15s or 10s if sulama
var growTime3 = self.sulamaUsed ? 10000 : 15000;
self.growthTimeout3 = LK.setTimeout(function () {
self.setGrowthStage(4);
}, growTime3);
}, growTime2);
}, growTime);
}
} else if (self.growthStage === 4) {
// Only allow harvesting if orak is active
if (orakActive) {
resourceAmounts.wheat += 3;
saveResources();
// Update wheat amount text if present
for (var j = 0; j < resourceAmountTexts.length; j++) {
if (resourceAmountTexts[j].type === "wheat") {
resourceAmountTexts[j].text.setText(resourceAmounts.wheat + "");
break;
}
}
self.setGrowthStage(0);
if (self.growthTimeout1) LK.clearTimeout(self.growthTimeout1);
if (self.growthTimeout2) LK.clearTimeout(self.growthTimeout2);
if (self.growthTimeout3) LK.clearTimeout(self.growthTimeout3);
self.sulamaUsed = false;
// Do NOT deactivate orakActive here
// orakActive = false;
// updateButtonStates();
}
}
// Do nothing if in wheatfirst, wheatsec, or wheatthird
};
// Make container interactive
soilContainer.interactive = true;
soilImages.push(soilContainer);
}
}
// --- GLOBAL ARRAYS for resource buttons and amount texts ---
var resourceButtons = [];
var resourceAmountTexts = [];
// --- PlayerHelper global instance and logic ---
var playerHelper = null;
var playerHelperTimeout = null;
function startPlayerHelper() {
// Only one at a time
if (playerHelper && playerHelper._active) {
if (playerHelperTimeout) LK.clearTimeout(playerHelperTimeout);
playerHelper.stop();
if (playerHelper.parent) playerHelper.parent.removeChild(playerHelper);
}
playerHelper = new PlayerHelper();
game.addChild(playerHelper);
playerHelper.start(soilImages);
// Stop after 5 minutes
playerHelperTimeout = LK.setTimeout(function () {
if (playerHelper) {
playerHelper.stop();
if (playerHelper.parent) playerHelper.parent.removeChild(playerHelper);
}
}, 5 * 60 * 1000);
}
// Resource amounts (persisted)
var resourceAmounts = {
cow: storage.cow || 0,
fish: storage.fish || 0,
wheat: storage.wheat || 0,
ore: storage.ore || 0,
wood: storage.wood || 0
};
// Save resource amounts to storage
function saveResources() {
storage.cow = resourceAmounts.cow;
storage.fish = resourceAmounts.fish;
storage.wheat = resourceAmounts.wheat;
storage.ore = resourceAmounts.ore;
storage.wood = resourceAmounts.wood;
}
// --- START SCREEN LOGIC ---
var startScreenShown = false;
var startImage = null;
var startTween1 = null;
var startTween2 = null;
var startTween3 = null;
var mainScreenInit = false;
// Hide all main game UI until start screen is done
function showMainScreen() {
if (mainScreenInit) return;
mainScreenInit = true;
// Create resource buttons
var btnSpacing = 350;
var btnStartY = 900;
var btnX = 2048 / 2;
window.resourceButtons = [];
for (var i = 0; i < resources.length; i++) {
var res = resources[i];
var btn = new ResourceButton();
btn.resourceType = res.type;
btn.iconId = res.iconId;
btn.cooldown = res.cooldown;
btn.x = btnX;
btn.y = btnStartY + i * btnSpacing;
btn.onCollect = function (rtype, btnIdx) {
return function () {
resourceAmounts[rtype]++;
saveResources();
// Update the corresponding amount text
for (var j = 0; j < resourceAmountTexts.length; j++) {
if (resourceAmountTexts[j].type === rtype) {
resourceAmountTexts[j].text.setText(resourceAmounts[rtype] + "");
break;
}
}
};
}(res.type, i);
// Set up icon and overlay after iconId is set
if (btn.children.length > 1) {
btn.removeChild(btn.children[1]); // Remove placeholder icon if exists
}
var icon = btn.attachAsset(res.iconId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
icon.y = 0;
if (btn.children.length > 1) {
btn.removeChild(btn.children[1]); // Remove placeholder overlay if exists
}
var overlay = btn.attachAsset('cooldownOverlay', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0,
scaleX: 2,
scaleY: 2
});
// Add to game
game.addChild(btn);
resourceButtons.push(btn);
}
// Add resource amount text next to each button (1cm = 40px gap)
window.resourceAmountTexts = [];
// Amount numbers and frames next to items on the main screen have been removed as requested.
}
// Show start image and animate, then show main screen
function showStartScreen() {
if (startScreenShown) return;
startScreenShown = true;
// Create start image centered
startImage = LK.getAsset('Start', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
startImage.x = 2048 / 2;
startImage.y = 2732 / 2;
startImage.alpha = 0;
startImage.scaleX = 0.7;
startImage.scaleY = 0.7;
game.addChild(startImage);
// Fade in and scale up
startTween1 = tween(startImage, {
alpha: 1,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Pulse scale
startTween2 = tween(startImage, {
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Wait, then fade out and remove
startTween3 = tween(startImage, {
alpha: 0,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
if (startImage && startImage.parent) startImage.parent.removeChild(startImage);
startImage = null;
showMainScreen();
}
});
}
});
}
});
}
// Start the game with the start screen
showStartScreen();
// Place the start button at the same place as the pause button (top right corner)
if (typeof LK.gui !== "undefined" && LK.gui.topRight) {
// If there is a start button, place it at the top right
if (typeof LK.startButton !== "undefined") {
LK.gui.topRight.addChild(LK.startButton);
}
}
// Add merchant button with image at the bottom right corner
var merchantCornerBtn = LK.getAsset('Merchant', {
anchorX: 0.5,
anchorY: 0.5
});
merchantCornerBtn.x = 2048 - 180;
merchantCornerBtn.y = 2732 - 180;
merchantCornerBtn.scaleX = 0.8;
merchantCornerBtn.scaleY = 0.8;
game.addChild(merchantCornerBtn);
// Open merchant popup when tapped
merchantCornerBtn.down = function (x, y, obj) {
if (merchantPopup) return;
// Block input to resource buttons
for (var i = 0; i < resourceButtons.length; i++) {
resourceButtons[i].interactive = false;
}
// Directly open the merchant popup logic (copied from yellowBtn.down)
if (merchantPopup) return;
// Fade overlay
merchantFade = new Container();
var fadeBg = LK.getAsset('cooldownOverlay', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732,
color: 0x000000,
alpha: 0.0
});
merchantFade.addChild(fadeBg);
merchantFade.interactive = true; // Block input to game behind
merchantFade.zIndex = 1000;
game.addChild(merchantFade);
// Animate fade in
tween(fadeBg, {
alpha: 0.7
}, {
duration: 350,
easing: tween.easeOut
});
// Popup box
merchantPopup = new Container();
// Expand width for more space
var boxW = 1200,
boxH = 1400;
var popupBg = LK.getAsset('inventoryBg', {
anchorX: 0.5,
anchorY: 0.5,
width: boxW,
height: boxH,
color: 0x333333
});
merchantPopup.addChild(popupBg);
merchantPopup.x = 2048 / 2;
merchantPopup.y = 2732 / 2;
merchantPopup.zIndex = 1001;
// Merchant "avatar" (just a text for now)
var merchantText = new Text2("Merchant", {
size: 120,
fill: 0xFFE066,
font: "'Growtopia', 'Arial', sans-serif"
});
merchantText.anchor.set(0.5, 0);
merchantText.x = 0;
merchantText.y = -boxH / 2 + 60;
merchantPopup.addChild(merchantText);
// Info text
var infoText = new Text2("Sell your items for gold!", {
size: 60,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
infoText.anchor.set(0.5, 0);
infoText.x = 0;
infoText.y = merchantText.y + 150;
merchantPopup.addChild(infoText);
// Gold display
if (typeof storage.gold === "undefined") storage.gold = 0;
var goldAmount = storage.gold || 0;
var goldText = new Text2("Gold: " + goldAmount, {
size: 80,
fill: 0xFFD700,
font: "'Growtopia', 'Arial', sans-serif"
});
goldText.anchor.set(0.5, 0);
// Add a gap between gold text and info text
goldText.x = 0;
goldText.y = infoText.y + 80;
merchantPopup.addChild(goldText);
// Resource sell button definitions
var sellDefs = [{
type: "wood",
icon: "woodIcon",
price: 2,
label: "Wood"
}, {
type: "fish",
icon: "fishIcon",
price: 1,
label: "Fish"
}, {
type: "cow",
icon: "cowIcon",
price: 3,
label: "Cow"
}, {
type: "ore",
icon: "oreIcon",
price: 4,
label: "Ore"
}, {
type: "wheat",
icon: "wheatIcon",
price: 1,
label: "Wheat"
}, {
//{player-merchant}
type: "player",
icon: "Player",
price: 50,
label: "Helper"
}];
// Layout: vertical stack, centered, with clear horizontal separation for icon, label, amount, price, and button
// Move sell buttons further down to avoid overlap with gold text
var sellBtnStartY = goldText.y + 180; // Increased gap below gold text
var sellBtnSpacing = 210; // Increased vertical spacing between rows
var sellBtnW = 1100,
// Expanded width for more space
sellBtnH = 150;
var merchantSellBtns = [];
for (var i = 0; i < sellDefs.length; i++) {
(function (i) {
var def = sellDefs[i];
var btnY = sellBtnStartY + i * sellBtnSpacing;
// Calculate horizontal layout: [amount owned] [icon] [label] [amount input] [price] [sell button]
// All elements vertically centered at btnY
// Amount owned (leftmost)
var amtOwned = resourceAmounts[def.type] || 0;
var amtOwnedText = new Text2(amtOwned + "", {
size: 64,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
amtOwnedText.anchor.set(1, 0.5);
amtOwnedText.x = -sellBtnW / 2 + 60;
amtOwnedText.y = btnY;
merchantPopup.addChild(amtOwnedText);
// Icon
var icon = LK.getAsset(def.icon, {
anchorX: 0.5,
anchorY: 0.5
});
icon.x = -sellBtnW / 2 + 160;
icon.y = btnY;
merchantPopup.addChild(icon);
// Resource label
var resLabel = new Text2(def.label, {
size: 64,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
resLabel.anchor.set(0, 0.5);
resLabel.x = -sellBtnW / 2 + 260;
resLabel.y = btnY;
merchantPopup.addChild(resLabel);
// --- Amount selector state ---
var sellAmount = 1;
// --- Input field for direct amount entry, centered between label and price ---
// Center input between label and price
var amtInputCenterX = 0; // center of the row
// Place input box in the middle of the row, with "x" to its left
var amtText = new Text2("x", {
size: 64,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
amtText.anchor.set(1, 0.5);
amtText.x = amtInputCenterX - 70;
amtText.y = btnY;
merchantPopup.addChild(amtText);
var amtInputBg = LK.getAsset('cooldownOverlay', {
anchorX: 0.5,
anchorY: 0.5,
width: 120,
height: 80,
color: 0x222222,
alpha: 0.7
});
amtInputBg.x = amtInputCenterX + 10;
amtInputBg.y = btnY;
merchantPopup.addChild(amtInputBg);
var amtInputText = new Text2(sellAmount + "", {
size: 54,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
amtInputText.anchor.set(0.5, 0.5);
amtInputText.x = amtInputBg.x;
amtInputText.y = amtInputBg.y;
merchantPopup.addChild(amtInputText);
// Make input field interactive for tap-to-edit
amtInputBg.interactive = true;
amtInputText.interactive = true;
// Helper: show a simple number input popup
function showNumberInputPopup(currentValue, maxValue, onDone) {
// Remove any existing popup
if (merchantPopup._numberInputPopup) {
merchantPopup.removeChild(merchantPopup._numberInputPopup);
merchantPopup._numberInputPopup = null;
}
var popup = new Container();
popup.zIndex = 2000;
// Add Numararka image as background behind the popup
var numararkaBg = LK.getAsset('Numararka', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 7,
scaleY: 7,
alpha: 1
});
// Move Numararka further down so number 6 is centered on it
numararkaBg.y = 180; // Move image down by 180px (increased from 80px for better centering)
// Place numararkaBg at the back of the popup
popup.addChild(numararkaBg);
// Removed black overlay background from number input popup
popup.x = amtInputBg.x;
popup.y = amtInputBg.y + 200;
var prompt = new Text2("Enter amount", {
size: 80,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
prompt.anchor.set(0.5, 0);
prompt.y = -180;
popup.addChild(prompt);
var inputVal = "0";
var inputText = new Text2(inputVal, {
size: 120,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
inputText.anchor.set(0.5, 0.5);
inputText.y = -60;
popup.addChild(inputText);
// Add number buttons 1-9, 0, backspace, OK
var btns = [];
var btnSize = 120;
var btnGap = 24;
var nums = [[1, 2, 3], [4, 5, 6], [7, 8, 9], ["←", 0, "OK"]];
// Place all buttons and their labels above the numararka image (which is already at the back)
for (var r = 0; r < 4; r++) {
for (var c = 0; c < 3; c++) {
(function (r, c) {
var val = nums[r][c];
var btn = LK.getAsset('resourceBtnBg', {
anchorX: 0.5,
anchorY: 0.5,
width: btnSize,
height: btnSize,
color: 0x333333,
alpha: 0.95
});
btn.x = (c - 1) * (btnSize + btnGap);
btn.y = 80 + r * (btnSize + btnGap);
btn.interactive = true;
popup.addChild(btn);
var label = new Text2(val + "", {
size: 72,
fill: "#fff",
font: "'Growtopia', 'Arial', sans-serif"
});
label.anchor.set(0.5, 0.5);
label.x = btn.x;
label.y = btn.y;
popup.addChild(label);
btn.down = function () {
if (val === "OK") {
var n = parseInt(inputVal, 10);
if (isNaN(n) || n < 1) n = 1;
if (n > maxValue) n = maxValue;
onDone(n);
merchantPopup.removeChild(popup);
merchantPopup._numberInputPopup = null;
} else if (val === "←") {
if (inputVal.length > 1) {
inputVal = inputVal.slice(0, -1);
} else {
inputVal = "1";
}
// Only clamp to 1, not to maxValue, so user can type any number
var n = parseInt(inputVal, 10);
if (isNaN(n) || n < 1) n = 1;
inputVal = n + "";
inputText.setText(inputVal);
} else {
if (inputVal === "0") inputVal = "";
if (inputVal.length < 4) {
inputVal += val + "";
var n = parseInt(inputVal, 10);
if (isNaN(n) || n < 1) n = 1;
inputVal = n + "";
inputText.setText(inputVal);
}
}
};
btns.push(btn);
})(r, c);
}
}
// Add invisible overlay to catch outside clicks and close popup
var closeOverlay = LK.getAsset('cooldownOverlay', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
height: 2732,
alpha: 0,
color: 0x000000
});
closeOverlay.x = 0;
closeOverlay.y = 0;
closeOverlay.zIndex = -100; // Ensure it's behind the popup content
closeOverlay.interactive = true;
closeOverlay.down = function (x, y, obj) {
// Only close if the click is not on a number button or input
if (merchantPopup && merchantPopup._numberInputPopup) {
merchantPopup.removeChild(merchantPopup._numberInputPopup);
merchantPopup._numberInputPopup = null;
}
};
// Insert overlay as first child so it sits behind popup content but above merchantPopup bg
popup.addChildAt(closeOverlay, 0);
merchantPopup.addChild(popup);
merchantPopup._numberInputPopup = popup;
}
// When input field is tapped, show number input popup
amtInputBg.down = amtInputText.down = function () {
var maxSell = resourceAmounts[def.type];
showNumberInputPopup(sellAmount, maxSell, function (newVal) {
sellAmount = newVal;
amtText.setText("x" + sellAmount);
amtInputText.setText(sellAmount + "");
});
};
// (Removed arrow logic for amount increase/decrease)
// Price
var priceText = new Text2(def.price + " coins", {
size: 56,
fill: 0xFFD700,
font: "'Growtopia', 'Arial', sans-serif"
});
priceText.anchor.set(1, 0.5);
// Add more horizontal space between amount selector and coins text
priceText.x = sellBtnW / 2 - 100; // was -120, now -100 for more gap after amount selector
priceText.y = btnY;
merchantPopup.addChild(priceText);
// Sell button
var sellBtn = LK.getAsset('inventoryBtn', {
anchorX: 0.5,
anchorY: 0.5,
color: 0xFFE066
});
sellBtn.x = sellBtnW / 2 - 30; // was -80, now -30 for more gap after coins text
sellBtn.y = btnY;
merchantPopup.addChild(sellBtn);
// Sell button label (removed)
// Sell logic
var sellAllTimeout = null;
var sellAllInterval = null;
sellBtn.down = function (x, y, obj) {
if (def.type === "player") {
// Buy the helper for 5 minutes
if (goldAmount >= def.price) {
goldAmount -= def.price;
storage.gold = goldAmount;
goldText.setText("Gold: " + goldAmount);
LK.getSound('Coin').play();
// Animate gold text
goldText.scale.set(1.2, 1.2);
tween(goldText.scale, {
x: 1,
y: 1
}, {
duration: 300,
easing: tween.easeOut
});
amtOwnedText.setText("∞");
// Start helper logic
startPlayerHelper();
}
return;
}
// Start a timeout to trigger sell all after 500ms
if (resourceAmounts[def.type] > 0) {
// Sell the selected amount (or as much as possible)
var toSell = Math.min(sellAmount, resourceAmounts[def.type]);
if (toSell > 0) {
resourceAmounts[def.type] -= toSell;
storage[def.type] = resourceAmounts[def.type];
goldAmount += def.price * toSell;
storage.gold = goldAmount;
LK.getSound('Coin').play();
amtText.setText("x" + Math.min(sellAmount, resourceAmounts[def.type]));
goldText.setText("Gold: " + goldAmount);
// Also update main screen resource amount
for (var j = 0; j < resourceAmountTexts.length; j++) {
if (resourceAmountTexts[j].type === def.type) {
resourceAmountTexts[j].text.setText(resourceAmounts[def.type] + "");
break;
}
}
// Update merchant popup amount demonstrator
amtOwnedText.setText(resourceAmounts[def.type] + "");
// Clamp sellAmount to available
if (resourceAmounts[def.type] < sellAmount) {
sellAmount = Math.max(1, resourceAmounts[def.type]);
amtText.setText("x" + sellAmount);
}
// Animate gold text
goldText.scale.set(1.2, 1.2);
tween(goldText.scale, {
x: 1,
y: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
}
// Start long-press detection
if (sellAllTimeout) LK.clearTimeout(sellAllTimeout);
if (sellAllInterval) LK.clearInterval(sellAllInterval);
sellAllTimeout = LK.setTimeout(function () {
// Start selling all at a fast interval
sellAllInterval = LK.setInterval(function () {
if (resourceAmounts[def.type] > 0) {
// Sell the selected amount (or as much as possible)
var toSell = Math.min(sellAmount, resourceAmounts[def.type]);
if (toSell > 0) {
resourceAmounts[def.type] -= toSell;
storage[def.type] = resourceAmounts[def.type];
goldAmount += def.price * toSell;
storage.gold = goldAmount;
LK.getSound('Coin').play();
amtText.setText("x" + Math.min(sellAmount, resourceAmounts[def.type]));
goldText.setText("Gold: " + goldAmount);
// Also update main screen resource amount
for (var j = 0; j < resourceAmountTexts.length; j++) {
if (resourceAmountTexts[j].type === def.type) {
resourceAmountTexts[j].text.setText(resourceAmounts[def.type] + "");
break;
}
}
// Update merchant popup amount demonstrator
amtOwnedText.setText(resourceAmounts[def.type] + "");
// Clamp sellAmount to available
if (resourceAmounts[def.type] < sellAmount) {
sellAmount = Math.max(1, resourceAmounts[def.type]);
amtText.setText("x" + sellAmount);
}
// Animate gold text
goldText.scale.set(1.2, 1.2);
tween(goldText.scale, {
x: 1,
y: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
} else {
// Stop interval if nothing left to sell
if (sellAllInterval) LK.clearInterval(sellAllInterval);
sellAllInterval = null;
}
}, 50); // Sell every 50ms
}, 500); // Long press threshold: 500ms
};
// Stop selling on up
sellBtn.up = function (x, y, obj) {
if (sellAllTimeout) LK.clearTimeout(sellAllTimeout);
sellAllTimeout = null;
if (sellAllInterval) LK.clearInterval(sellAllInterval);
sellAllInterval = null;
};
merchantSellBtns.push({
btn: sellBtn,
amtText: amtText
});
})(i);
}
// Close button
var closeBtn = LK.getAsset('closeBtn', {
anchorX: 0.5,
anchorY: 0.5
});
closeBtn.x = boxW / 2 - 60;
closeBtn.y = -boxH / 2 + 60;
merchantPopup.addChild(closeBtn);
// Close logic
closeBtn.down = function () {
// Restore input to resource buttons
for (var i = 0; i < resourceButtons.length; i++) {
resourceButtons[i].interactive = true;
}
// Animate fade out
if (merchantFade && merchantFade.children.length > 0) {
tween(merchantFade.children[0], {
alpha: 0
}, {
duration: 250,
easing: tween.easeIn,
onFinish: function onFinish() {
if (merchantFade && merchantFade.parent) merchantFade.parent.removeChild(merchantFade);
merchantFade = null;
}
});
} else if (merchantFade && merchantFade.parent) {
merchantFade.parent.removeChild(merchantFade);
merchantFade = null;
}
if (merchantPopup && merchantPopup.parent) merchantPopup.parent.removeChild(merchantPopup);
merchantPopup = null;
};
// Add popup to game
game.addChild(merchantPopup);
};
// Only keep the merchant button at the bottom right corner
// (Removed button "a" and its label)
// --- Merchant Popup logic ---
var merchantPopup = null;
var merchantFade = null;
// (yellowBtn and blueBtn handlers removed)
// Play background music when the game starts
LK.playMusic('Bg');
// Game update loop
game.update = function () {
// Update all resource buttons (for cooldowns)
for (var i = 0; i < resourceButtons.length; i++) {
resourceButtons[i].update();
}
};
// --- Asset Initialization Section ---
// Resource button background
// Cooldown overlay (semi-transparent)
// Inventory popup background
// Inventory button (top right)
// Close button for popup
// Resource icons
// Set Growtopia font as default for all Text2
if (typeof Text2 !== "undefined") {
Text2.defaultFont = "'Growtopia', 'Arial', sans-serif";
}
// Stop background music when the game is paused
game.pause = function () {
LK.stopMusic();
};
// Resume background music when the game continues
game["continue"] = function () {
LK.playMusic('Bg');
};
Merchant. In-Game asset. 2d. High contrast. No shadows
Coin, "m" sign. In-Game asset. 2d. High contrast. No shadows
Frame. In-Game asset. 2d. High contrast. No shadows
My farm, wheat, cow, ore, pickaxe. Starting screen style. In-Game asset. 2d. High contrast. No shadows
Movement button, item moving button. In-Game asset. 2d. High contrast. No shadows
A cute middle eastern farmer character. In-Game asset. 2d. High contrast. No shadows
Mowing sickle. In-Game asset. 2d. High contrast. No shadows
Watering waterdrops watering can. In-Game asset. 2d. High contrast. No shadows
Historical frame with beige interior.