User prompt
Put inventory button Vertically to the middle, horizontally to the right.
User prompt
Please fix the bug: 'Uncaught TypeError: LK.now is not a function' in or related to this line: 'self.lastCollectTime = LK.now();' Line Number: 193
User prompt
Fix the error
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'length')' in or related to this line: 'var icon = self.attachAsset(self.iconId, {' Line Number: 139
Code edit (1 edits merged)
Please save this source code
User prompt
Resource Collector: Inventory Rush
Initial prompt
There will be 5 buttons on the screen. One is cow, one is fish, one is wheat, one is ore, one is wood. And also there should be an inventory button at the right up corner. When we click the 5 buttons, the item that we have tapped should be increased. And when we tap inventory button, there will pop up an inventory screen and we should be able to see the amounts of the items we have collected. Also there should be a time limit for each of the 5 buttons. Player shouldn't be able to tap the buttons consecutively, 30 seconds would be enough.
/**** * 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.