/**** * Plugins ****/ var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Button = Container.expand(function (onPress, config) { var self = Container.call(this); self.onPress = onPress; self.isPressed = false; self.id = config.id; self.disable = false; if (config.size) { var buttonWidth = config.size; var buttonHeight = config.size; } else { var buttonWidth = config.width; var buttonHeight = config.height; } self.textSize = config.textSize | Math.max(buttonWidth, buttonHeight) / 5; // Use custom background if provided, otherwise use default var backgroundAsset = config.background || 'buttonBackground'; var backgroundPressedAsset = backgroundAsset + 'Pressed'; var background = self.attachAsset(backgroundAsset, { width: buttonWidth, height: buttonHeight }); var backgroundPressed = self.attachAsset(backgroundPressedAsset, { visible: false, width: buttonWidth, height: buttonHeight }); self.text = config.text || ""; var buttonText = new Text2(self.text, { size: self.textSize, fill: 0xFFFFFF, weight: 700 }); buttonText.anchor.set(0.5, 0.5); buttonText.x = buttonWidth / 2; buttonText.y = buttonHeight / 2 - (backgroundAsset === 'buttonBackground' ? 0 : 23); self.addChild(buttonText); self.icon = config.icon; if (self.icon) { var buttonIcon = self.attachAsset(self.icon, {}); } self.down = function (x, y, obj) { if (self.disable) { return; } LK.getSound('click').play(); if (self.onPress) { // If onPress return false, then the button won't do the animation if (!self.onPress(self)) { return; } } self.isPressed = true; background.visible = false; backgroundPressed.visible = true; currentButton = self; if (buttonIcon) { buttonIcon.y = 46; } buttonText.y = buttonHeight / 2 + 23; }; self.up = function (x, y, obj) { self.isPressed = false; background.visible = true; backgroundPressed.visible = false; if (buttonIcon) { buttonIcon.y = 0; } buttonText.y = buttonHeight / 2 - 23; }; self.setDisable = function (disable) { self.disable = disable; self.tint = disable ? 0xAAAAAA : 0xFFFFFF; }; return self; }); // ButtonShop width and height is fixed. var ButtonShop = Button.expand(function (config) { var self = Button.call(this, null, { background: 'rectButtonBackground', width: 818, height: 335 }); self.config = config; // Initialize shop level in gameStatus if not exists if (!gameStatus.shopLevel[config.value]) { gameStatus.shopLevel[config.value] = 1; } // Get current cost based on level self.calculateCost = function () { var cost = config.baseCost; for (var i = 1; i < gameStatus.shopLevel[config.value]; i++) { if (config.calculateCost) { cost = config.calculateCost(i); } else { cost = Math.ceil(cost * 1.5); } } return cost; }; // Get current value based on level self.calculateValue = function () { var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel; var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue; if (config.calculateValue) { return config.calculateValue(gameStatus.shopLevel[config.value], isMaxLevel || isMaxValue); } return gameStatus.shopLevel[config.value]; }; // Create label text self.labelText = new Text2(config.label + " LV: ", { size: 60, fill: 0xFFFFFF, weight: 700 }); self.labelText.anchor.set(0.5, 0.5); self.labelText.x = 818 / 2; self.addChild(self.labelText); // Calculate initial cost before creating cost text self.cost = self.calculateCost(); // Create cost text self.costText = new Text2("Cost: " + formatMoney(self.cost), { size: 100, fill: 0xC7FFC7, weight: 700 }); self.costText.anchor.set(0.5, 0.5); self.costText.x = 818 / 2; self.addChild(self.costText); self.updateDisplay = function () { var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel; var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue; if (isMaxLevel || isMaxValue) { self.costText.setText("MAX LEVEL"); self.labelText.setText(config.label + ": MAX"); } else { var costText = formatMoney(self.cost); self.costText.setText("Cost: " + costText); self.labelText.setText(config.label + ": LV " + gameStatus.shopLevel[config.value]); } }; self.onPress = function () { var currentCost = self.cost; if (gameStatus.money >= currentCost) { gameStatus.save(); gameStatus.money -= currentCost; gameStatus.shopLevel[config.value]++; // Update the game status value based on the new level gameStatus[config.value] = self.calculateValue(); self.cost = self.calculateCost(); console.log(gameStatus[config.value]); gameStatus.updateMoney(); self.updateDisplay(); // Check if we've reached max level or max value and disable button var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel; var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue; if (isMaxLevel || isMaxValue) { self.setDisable(true); } } return true; }; self.update = function () { if (self.isPressed) { self.labelText.y = 335 / 2 - 70 + 28; self.costText.y = 335 / 2 + 40 + 28; } else { self.labelText.y = 335 / 2 - 70; self.costText.y = 335 / 2 + 40; } }; // Initialize game status value gameStatus[config.value] = self.calculateValue(); // Check if already at max level/value and disable if needed var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel; var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue; if (isMaxLevel || isMaxValue) { self.setDisable(true); } self.updateDisplay(); return self; }); var Fish = Container.expand(function (fishType) { var self = Container.call(this); // Store fish type if (fishType) { self.fishType = fishType; } else { throw "You must add a fish type."; } // Create fish visual var fishGraphics = self.attachAsset(self.fishType, { anchorX: 0.5, anchorY: 0.5 }); // Swimming properties self.speed = 2 + Math.random() * 3; // Random speed between 2-5 self.direction = Math.random() * Math.PI * 2; // Random initial direction self.turnSpeed = 0.02 + Math.random() * 0.03; // How fast fish can turn self.targetDirection = self.direction; self.wobbleAmount = 0.1 + Math.random() * 0.2; // Slight wobble for natural movement self.wobbleSpeed = 0.05 + Math.random() * 0.05; self.wobbleOffset = Math.random() * Math.PI * 2; // Boundaries with margin self.margin = 100; // Initialize position self.randomPosition = function () { self.x = self.margin + Math.random() * (2048 - self.margin * 2); self.y = self.margin + Math.random() * (2732 - self.margin * 2); }; self.format = function () { if (self.rate.toFixed(2) <= 0.5) { var rate = "frame"; } else { var rate = formatTime(self.rate, 2); } return formatMoney(self.value) + " every " + rate; }; self.destroy = function () { if (self.incomeTimer) { LK.clearInterval(self.incomeTimer); self.incomeTimer = null; } Container.prototype.destroy.call(self); self = undefined; }; self.update = function () { if (!self.hasAI) { return; } // Add wobble to movement var wobble = Math.sin(LK.ticks * self.wobbleSpeed + self.wobbleOffset) * self.wobbleAmount; // Gradually turn towards target direction var directionDiff = self.targetDirection - self.direction; // Normalize angle difference to -PI to PI while (directionDiff > Math.PI) { directionDiff -= Math.PI * 2; } while (directionDiff < -Math.PI) { directionDiff += Math.PI * 2; } self.direction += directionDiff * self.turnSpeed; // Move fish self.x += Math.cos(self.direction + wobble) * self.speed; self.y += Math.sin(self.direction + wobble) * self.speed; // Face the direction of movement fishGraphics.rotation = self.direction; // Check boundaries and change direction var turnAwayForce = 0.1; var boundaryDistance = 200; if (self.x < self.margin + boundaryDistance) { self.targetDirection = 0; // Turn right } else if (self.x > 2048 - self.margin - boundaryDistance) { self.targetDirection = Math.PI; // Turn left } if (self.y < self.margin + boundaryDistance) { self.targetDirection = Math.PI / 2; // Turn down } else if (self.y > 2732 - self.margin - boundaryDistance - 200) { self.targetDirection = -Math.PI / 2; // Turn up } // Occasionally change direction randomly if (Math.random() < 0.005) { // 0.5% chance per frame self.targetDirection = Math.random() * Math.PI * 2; } // Keep fish within bounds (hard limit) self.x = Math.max(self.margin, Math.min(2048 - self.margin, self.x)); self.y = Math.max(self.margin, Math.min(2732 - self.margin, self.y)); }; return self; }); // Anything stored in GameStatus will be saved var GameStatus = Container.expand(function () { var self = Container.call(this); self.money = 0; self.fishes = []; self.bait = 20; self.baitTimer = 300; self.fishValue = 1; self.fishMax = 3; self.fishRate = 3.6e+6; self.shopLevel = {}; self.tutorial = true; // storage.hasSaved = false; if (storage.hasSaved) { self.money = storage.money || 0; var deserializedFishes = deserializeFish(storage.fishes || ""); self.fishes = deserializedFishes; self.bait = storage.bait || 20; self.baitTimer = storage.baitTimer || 300; self.fishValue = storage.fishValue || 1; self.fishMax = storage.fishMax || 3; self.fishRate = storage.fishRate || 3.6e+6; self.shopLevel = storage.shopLevel || {}; self.tutorial = storage.tutorial || storage.tutorial !== false; var dt = Date.now() - storage.time; // dt = 1000*3600*8 var t = 0; self.fishes.forEach(function (fish, i) { if (fish.rate === 0) { var earning = Math.floor(dt / 40, dt) * fish.value; } else { var earning = Math.floor(dt / fish.rate, dt) * fish.value; } t += earning; self.money += earning; console.log("".concat(fish.fishType, ": ").concat(formatMoney(earning), ", ").concat(dt)); }); console.log("Total: ".concat(formatMoney(t))); if (self.bait < 20) { self.bait += Math.floor(dt / (300 * 1000)); self.baitTimer -= Math.floor(dt % (300 * 1000) / 1000); if (self.baitTimer < 0) { self.baitTimer = trueMod(self.baitTimer, 300); self.bait++; } if (self.bait >= 20) { self.bait = 20; } } } self.fishesAtCapacity = function () { return self.fishes.length >= self.fishMax; }; self.formatMoney = function () { if (self.money === Infinity) { return "$Infinity"; } var str = "$"; if (self.money >= 1000) { suffixes = ["K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "De"]; var digits = countNumber(self.money); var sf = suffixes[Math.floor((digits - 1) / 3) - 1]; if (sf === undefined) { str += String(self.money.toExponential(2)); } else { str += (self.money / Math.pow(10, Math.floor((digits - 1) / 3) * 3)).toFixed(2) + sf; } } else { str += self.money.toFixed(2); } return str; }; self.updateMoney = function () { if (self.children.length > 0) { self.removeChildAt(0); } var moneyText = new Text2(self.formatMoney(), { size: 125, fill: 0xD7FFD7, weight: 750 }); moneyText.y = 50; moneyText.x = 50; self.save(); self.addChildAt(moneyText, 0); }; self.save = function () { storage.hasSaved = true; storage.version = 0; storage.time = Date.now(); storage.money = self.money; var array = []; self.fishes.forEach(function (obj) { var fishData = obj.fishType + "|" + obj.value + "|" + obj.rate; array.push(fishData); }); storage.fishes = array.join("\n"); storage.bait = self.bait; storage.baitTimer = self.baitTimer; storage.fishValue = self.fishValue; storage.fishMax = self.fishMax; storage.fishRate = self.fishRate; storage.shopLevel = self.shopLevel; storage.tutorial = self.tutorial; }; self.updateMoney(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x83C7FF }); /**** * Game Code ****/ function trueMod(a, b) { return (a % b + b) % b; } function countNumber(number) { return Math.floor(Math.log10(Math.abs(number))) + 1; } function formatTime(time, point) { time = time / 1000; var str = ""; if (time > 3600) { str += String((time / 3600).toFixed(point)) + " hour"; } else if (time > 60) { str += String((time / 60).toFixed(point)) + " minute"; } else { str += String(time.toFixed(point)) + " second"; } if (time != 1) { str += "s"; } return str; } function formatMoney(money) { if (money === Infinity) { return "$Infinity"; } var str = "$"; if (money >= 1000) { var suffixes = ["K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "De"]; var digits = countNumber(money); var sf = suffixes[Math.floor((digits - 1) / 3) - 1]; if (sf === undefined) { str += String(money.toExponential(2)); } else { str += (money / Math.pow(10, Math.floor((digits - 1) / 3) * 3)).toFixed(2) + sf; } } else { str += money.toFixed(2); } return str; } function normalRandom(min, max, skew) { skew = skew | 1; var u = 0, v = 0; while (u === 0) { u = Math.random(); } while (v === 0) { v = Math.random(); } var num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v); num = num / 10.0 + 0.5; if (num > 1 || num < 0) { num = normalRandom(min, max, skew); } else { num = Math.pow(num, skew); num *= max - min; num += min; } return num; } var shine; var shine2; function deserializeFish(fishDataString) { if (!fishDataString || fishDataString.trim() === "") { return []; } var fishArray = []; var fishLines = fishDataString.split("\n"); for (var i = 0; i < fishLines.length; i++) { var line = fishLines[i].trim(); if (line === "") { continue; } var parts = line.split("|"); if (parts.length === 3) { var fishType = parts[0]; var value = parseFloat(parts[1]); var rate = parseFloat(parts[2]); // Create new fish with deserialized data var fish = new Fish(fishType); fish.scale.set(2); fish.value = value; fish.rate = rate; fish.hasAI = true; fishArray.push(fish); } } return fishArray; } var gameStatus = new GameStatus(); var tabLayout = {}; var fishTypes = ['blueFish', 'greenFish', 'pinkFish', 'redFish']; function addFish(fish) { gameStatus.caughtFish.scaleX = 2; gameStatus.caughtFish.scaleY = 2; fish.randomPosition(); gameStatus.fishes.push(fish); fishLayer.addChild(fish); // Add interval timer for this fish fish.incomeTimer = LK.setInterval(function () { gameStatus.money += fish.value; gameStatus.updateMoney(); }, fish.rate); gameStatus.save(); } function randomFish(value, rate) { if (value === undefined) { var r = 0; var v = 0; } else { var r = normalRandom(rate / 1.5, rate * 1.5); var v = Number(normalRandom(value / 1.5, value * 1.5).toFixed(2)); if (r < 0) { r = 0; } } var randomType = fishTypes[Math.floor(Math.random() * fishTypes.length)]; var fish = new Fish(randomType); fish.value = v; fish.rate = r; return fish; } // Initialise the cast screen var castLayer = new Container(); castLayer.visible = false; castLayer.attachAsset('castBackground', { alpha: 0.5 }); var text = new Text2("You caught a fish!", { size: 150, fill: 0xFFFFFF, weight: 700 }); text.x = 2048 / 2 - text.width / 2; text.y = 2732 / 2 - 900; castLayer.addChild(text); var fishText; function hideCast() { castLayer.removeChild(gameStatus.caughtFish); shine.destroy(); shine2.destroy(); fishText.destroy(); castLayer.visible = false; } var keepButton = new Button(function () { hideCast(); gameStatus.save(); gameStatus.caughtFish.hasAI = true; addFish(gameStatus.caughtFish); castLayer.visible = false; gameStatus.caughtFish = undefined; }, { text: "Keep", background: 'rectButtonBackground', width: 818, height: 335 }); keepButton.y = 2732 / 2 + 900; castLayer.addChild(keepButton); var sellButton = new Button(function () { hideCast(); gameStatus.money += gameStatus.caughtFish.value; gameStatus.updateMoney(); gameStatus.caughtFish = undefined; }, { text: "Sell", background: 'rectButtonBackground', width: 818, height: 335 }); sellButton.x = 2048 / 2 + 50; sellButton.y = 2732 / 2 + 900; castLayer.addChild(sellButton); var closeButton = new Button(hideCast, { size: 150, icon: 'closeIcon' }); closeButton.x = 2048 - 250; closeButton.y = 50; castLayer.addChild(closeButton); var passiveIncome = new Text2("Passive Income:", { size: 125, fill: 0xFFFFFF, weight: 700 }); passiveIncome.x = 2048 / 2; passiveIncome.y = 2732 / 2 - 800 + text.height; passiveIncome.anchor.set(0.5, 0); castLayer.addChild(passiveIncome); game.addChild(castLayer); function cast() { if (gameStatus.caughtFish === undefined) { gameStatus.bait--; gameStatus.caughtFish = randomFish(gameStatus.fishValue, gameStatus.fishRate); // Update bait counter baitText.setText("Bait: " + gameStatus.bait + "/20"); } if (gameStatus.tutorial && gameStatus.fishes.length == 2) { gameStatus.tutorial = false; } sellButton.visible = !gameStatus.tutorial; if (gameStatus.tutorial) { keepButton.x = 2048 / 2 - 818 / 2; } else { keepButton.x = 2048 / 2 - 818 - 50; } keepButton.setDisable(gameStatus.fishesAtCapacity()); shine = LK.getAsset('shine', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.8, scaleY: 1.8, alpha: 0.7 }); shine.x = 2048 / 2; shine.y = 2732 / 2 + 300; shine.rotation = 0; castLayer.addChild(shine); shine2 = LK.getAsset('shine', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, alpha: 0.5 }); shine2.x = 2048 / 2; shine2.y = 2732 / 2 + 300; shine2.rotation = 0; castLayer.addChild(shine2); // Render fish on cast screen gameStatus.caughtFish.x = 2048 / 2; gameStatus.caughtFish.y = 2732 / 2 + 300; gameStatus.caughtFish.scaleX = 2; gameStatus.caughtFish.scaleY = 2; gameStatus.caughtFish.hasAI = false; castLayer.addChild(gameStatus.caughtFish); fishText = new Text2(gameStatus.caughtFish.format(), { size: 125, fill: 0xC7FFC7, weight: 750 }); fishText.x = 2048 / 2; fishText.y = 2732 / 2 - 790 + passiveIncome.height + text.height; fishText.anchor.set(0.5, 0); castLayer.addChild(fishText); // Display the cast screen castLayer.visible = true; hideTab(); } function hideTab() { tabBackground.visible = false; if (currTab) { tabLayout[currTab].visible = false; currTab = null; } } function updateFishesDisplay() { if (currTab !== 'fishes') { return; } fishesHeader.setText('Capacity: ' + gameStatus.fishes.length + '/' + gameStatus.fishMax); fishesHeader.x = (946 - fishesHeader.width) / 2; // Clear previous fish display - properly destroy assets first while (tabLayout.fishes.children.length > 1) { var childToRemove = tabLayout.fishes.children[tabLayout.fishes.children.length - 1]; if (childToRemove.destroy) { childToRemove.destroy(); } else { tabLayout.fishes.removeChildAt(tabLayout.fishes.children.length - 1); } } // Display each fish var yOffset = 230; for (var i = 0; i < gameStatus.fishes.length; i++) { var fish = gameStatus.fishes[i]; // Fish icon var fishIcon = LK.getAsset(fish.fishType, {}); fishIcon.scale.set(0.75); fishIcon.x = 100; fishIcon.y = yOffset; tabLayout.fishes.addChild(fishIcon); // Fish info text var fishInfo = new Text2(fish.format(), { size: 60, fill: 0xFFFFFF, weight: 600 }); fishInfo.anchor.set(0, 0.5); fishInfo.x = 100; fishInfo.y = yOffset + fishIcon.height + 22; tabLayout.fishes.addChild(fishInfo); // Sell button for this fish var sellFishButton = new Button(function (fishIndex) { return function () { var fishToSell = gameStatus.fishes[fishIndex]; // Add money from selling gameStatus.money += fishToSell.value; gameStatus.updateMoney(); // Remove fish from array and destroy gameStatus.fishes.splice(fishIndex, 1); fishToSell.destroy(); // Update the fishes display updateFishesDisplay(); gameStatus.save(); return true; }; }(i), { text: "Sell", width: 250, height: 90 }); sellFishButton.x = 600; sellFishButton.y = yOffset + fishIcon.height / 2 - 60; if (!gameStatus.tutorial) { tabLayout.fishes.addChild(sellFishButton); } yOffset += fishIcon.height + fishInfo.height + 25; // Add separator if (i < gameStatus.fishes.length - 1) { var separator = LK.getAsset('separator', {}); separator.x = 85; separator.y = yOffset; tabLayout.fishes.addChild(separator); yOffset += separator.height + 20; } } } var currTab; function tabPressed(button) { if (castLayer.visible) { return false; } tabBackground.visible = true; if (currTab == button.id) { return true; } if (currTab) { tabLayout[currTab].visible = false; } currTab = button.id; tabLayout[button.id].visible = true; if (gameStatus.bait <= 0) { castButton.setDisable(true); } if (currTab == 'fishes') { updateFishesDisplay(); } return true; } var tabLayer = new Container(); var tabBackground = new Container(); var tabElements = new Container(); tabLayout.cast = tabElements.addChild(new Container()); tabLayout.cast.visible = false; tabLayout.fishes = tabElements.addChild(new Container()); tabLayout.fishes.visible = false; tabLayout.shop = tabElements.addChild(new Container()); tabLayout.shop.visible = false; var baitText = new Text2("Bait: " + gameStatus.bait + "/20", { size: 100, fill: 0xFFFFFF, weight: 700 }); baitText.x = (946 - baitText.width) / 2; baitText.y = 70; tabLayout.cast.addChild(baitText); var baitTimerText = new Text2("", { size: 80, fill: 0xFFFFFF, weight: 600 }); baitTimerText.x = (946 - baitTimerText.width) / 2; baitTimerText.y = baitText.y + baitText.height + 10; tabLayout.cast.addChild(baitTimerText); var castButton = new Button(cast, { width: 818, height: 335, background: 'rectButtonBackground', text: "Cast" }); castButton.x = (946 - 818) / 2; castButton.y = 95 + baitText.height; tabLayout.cast.addChild(castButton); var fishesHeader = new Text2('Capacity: ', { size: 100, fill: 0xFFFFFF, weight: 700 }); fishesHeader.x = (946 - fishesHeader.width) / 2; fishesHeader.y = 70; tabLayout.fishes.addChild(fishesHeader); var fishValueShop = new ButtonShop({ value: 'fishValue', baseCost: 1, label: 'Caught Fish Value' }); fishValueShop.x = (946 - 818) / 2; fishValueShop.y = 70; var fishValueShop = new ButtonShop({ value: 'fishValue', baseCost: 1, label: 'Caught Fish Value' }); fishValueShop.x = (946 - 818) / 2; fishValueShop.y = 70; tabLayout.shop.addChild(fishValueShop); var fishRateShop = new ButtonShop({ value: 'fishRate', baseCost: 100, label: 'Caught Fish Rate', maxLevel: 195, calculateValue: function calculateValue(level, isMax) { if (isMax) { return 0; } // Use exponential decay: rate decreases a lot at first, then less at higher levels // Lower rate = faster income generation (better for player) var baseRate = 3.6e+6; var decayRate = 0.95; var currentRate = baseRate * Math.pow(decayRate, level - 1); return currentRate; } }); fishRateShop.x = (946 - 818) / 2; fishRateShop.y = 70 + 25 + 335 * 1; tabLayout.shop.addChild(fishRateShop); var fishMaxShop = new ButtonShop({ value: 'fishMax', baseCost: 2500, label: 'Fish Capacity', maxValue: 8, calculateValue: function calculateValue(level) { return Math.min(8, level * 3); }, calculateCost: function calculateCost(level) { return level * 2500 * 1000; } }); fishMaxShop.x = (946 - 818) / 2; fishMaxShop.y = 70 + (335 + 25) * 2; tabLayout.shop.addChild(fishMaxShop); tabLayer.addChild(tabBackground); tabLayer.addChild(tabElements); tabBackground.x = 2048 - 976; tabBackground.y = 25; tabElements.x = 2048 - 976; tabElements.y = 25; tabBackground.visible = false; tabBackground.attachAsset('tabBackgroundTL', {}); tabBackground.attachAsset('tabBackgroundBL', { y: 1155 }); tabBackground.attachAsset('tabBackgroundBR', { x: 473, y: 1155 }); tabBackground.attachAsset('tabBackgroundTR', { x: 473 }); var castTab = new Button(tabPressed, { width: 300, height: 346, background: "tabButtonBackground", icon: 'castIcon', id: "cast" }); castTab.x = 2048 - 325 * 3; castTab.y = 2732 - 350; tabLayer.addChild(castTab); var fishesTab = new Button(tabPressed, { width: 300, height: 346, background: "tabButtonBackground", icon: "fishesIcon", id: "fishes" }); fishesTab.x = 2048 - 325 * 2; fishesTab.y = 2732 - 350; tabLayer.addChild(fishesTab); var shopTab = new Button(tabPressed, { width: 300, height: 346, background: "tabButtonBackground", icon: "shopIcon", id: "shop" }); shopTab.x = 2048 - 325; shopTab.y = 2732 - 350; tabLayer.addChild(shopTab); var fishLayer = new Container(); game.addChild(fishLayer); var fishes = []; game.attachAsset('waterBackground', {}); game.addChild(fishLayer); game.addChild(gameStatus); game.addChild(tabLayer); game.addChild(castLayer); // Load saved fishes and add them to the game for (var i = 0; i < gameStatus.fishes.length; i++) { var fish = gameStatus.fishes[i]; fish.randomPosition(); fishLayer.addChild(fish); // Add interval timer for this fish fish.incomeTimer = LK.setInterval(function (fishInstance) { return function () { gameStatus.money += fishInstance.value; gameStatus.updateMoney(); }; }(fish), fish.rate); } // Bait regeneration timer LK.setInterval(function () { // Only decrease timer if bait is not at maximum if (gameStatus.bait < 20) { gameStatus.baitTimer--; // When timer reaches 0, increase bait and reset timer if (gameStatus.baitTimer <= 0) { gameStatus.bait++; gameStatus.baitTimer = 300; // Update bait display baitText.setText("Bait: " + gameStatus.bait + "/20"); // Re-enable cast button if we have bait now if (gameStatus.bait > 0) { castButton.setDisable(false); } } // Update timer display var minutes = Math.floor(gameStatus.baitTimer / 60); var seconds = gameStatus.baitTimer % 60; var timerString = minutes + ":" + (seconds < 10 ? "0" : "") + seconds; baitTimerText.setText("Next bait in: " + timerString); baitTimerText.x = (946 - baitTimerText.width) / 2; castButton.y = 95 + baitText.height + baitTimerText.height; } if (gameStatus.bait >= 20) { // Hide timer when bait is at maximum baitTimerText.setText(""); castButton.y = 95 + baitText.height; } }, 1000); // Run every 1000ms (1 second) game.update = function () { // Rotate shine clockwise if visible if (shine) { shine.rotation += 0.01; // Rotate clockwise } if (shine2) { shine2.rotation -= 0.0075; // Rotate anti-clockwise, slower } }; game.down = function (x, y, obj) { if (tabBackground.x > x) { hideTab(); } };
/****
* Plugins
****/
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Button = Container.expand(function (onPress, config) {
var self = Container.call(this);
self.onPress = onPress;
self.isPressed = false;
self.id = config.id;
self.disable = false;
if (config.size) {
var buttonWidth = config.size;
var buttonHeight = config.size;
} else {
var buttonWidth = config.width;
var buttonHeight = config.height;
}
self.textSize = config.textSize | Math.max(buttonWidth, buttonHeight) / 5;
// Use custom background if provided, otherwise use default
var backgroundAsset = config.background || 'buttonBackground';
var backgroundPressedAsset = backgroundAsset + 'Pressed';
var background = self.attachAsset(backgroundAsset, {
width: buttonWidth,
height: buttonHeight
});
var backgroundPressed = self.attachAsset(backgroundPressedAsset, {
visible: false,
width: buttonWidth,
height: buttonHeight
});
self.text = config.text || "";
var buttonText = new Text2(self.text, {
size: self.textSize,
fill: 0xFFFFFF,
weight: 700
});
buttonText.anchor.set(0.5, 0.5);
buttonText.x = buttonWidth / 2;
buttonText.y = buttonHeight / 2 - (backgroundAsset === 'buttonBackground' ? 0 : 23);
self.addChild(buttonText);
self.icon = config.icon;
if (self.icon) {
var buttonIcon = self.attachAsset(self.icon, {});
}
self.down = function (x, y, obj) {
if (self.disable) {
return;
}
LK.getSound('click').play();
if (self.onPress) {
// If onPress return false, then the button won't do the animation
if (!self.onPress(self)) {
return;
}
}
self.isPressed = true;
background.visible = false;
backgroundPressed.visible = true;
currentButton = self;
if (buttonIcon) {
buttonIcon.y = 46;
}
buttonText.y = buttonHeight / 2 + 23;
};
self.up = function (x, y, obj) {
self.isPressed = false;
background.visible = true;
backgroundPressed.visible = false;
if (buttonIcon) {
buttonIcon.y = 0;
}
buttonText.y = buttonHeight / 2 - 23;
};
self.setDisable = function (disable) {
self.disable = disable;
self.tint = disable ? 0xAAAAAA : 0xFFFFFF;
};
return self;
});
// ButtonShop width and height is fixed.
var ButtonShop = Button.expand(function (config) {
var self = Button.call(this, null, {
background: 'rectButtonBackground',
width: 818,
height: 335
});
self.config = config;
// Initialize shop level in gameStatus if not exists
if (!gameStatus.shopLevel[config.value]) {
gameStatus.shopLevel[config.value] = 1;
}
// Get current cost based on level
self.calculateCost = function () {
var cost = config.baseCost;
for (var i = 1; i < gameStatus.shopLevel[config.value]; i++) {
if (config.calculateCost) {
cost = config.calculateCost(i);
} else {
cost = Math.ceil(cost * 1.5);
}
}
return cost;
};
// Get current value based on level
self.calculateValue = function () {
var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel;
var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue;
if (config.calculateValue) {
return config.calculateValue(gameStatus.shopLevel[config.value], isMaxLevel || isMaxValue);
}
return gameStatus.shopLevel[config.value];
};
// Create label text
self.labelText = new Text2(config.label + " LV: ", {
size: 60,
fill: 0xFFFFFF,
weight: 700
});
self.labelText.anchor.set(0.5, 0.5);
self.labelText.x = 818 / 2;
self.addChild(self.labelText);
// Calculate initial cost before creating cost text
self.cost = self.calculateCost();
// Create cost text
self.costText = new Text2("Cost: " + formatMoney(self.cost), {
size: 100,
fill: 0xC7FFC7,
weight: 700
});
self.costText.anchor.set(0.5, 0.5);
self.costText.x = 818 / 2;
self.addChild(self.costText);
self.updateDisplay = function () {
var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel;
var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue;
if (isMaxLevel || isMaxValue) {
self.costText.setText("MAX LEVEL");
self.labelText.setText(config.label + ": MAX");
} else {
var costText = formatMoney(self.cost);
self.costText.setText("Cost: " + costText);
self.labelText.setText(config.label + ": LV " + gameStatus.shopLevel[config.value]);
}
};
self.onPress = function () {
var currentCost = self.cost;
if (gameStatus.money >= currentCost) {
gameStatus.save();
gameStatus.money -= currentCost;
gameStatus.shopLevel[config.value]++;
// Update the game status value based on the new level
gameStatus[config.value] = self.calculateValue();
self.cost = self.calculateCost();
console.log(gameStatus[config.value]);
gameStatus.updateMoney();
self.updateDisplay();
// Check if we've reached max level or max value and disable button
var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel;
var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue;
if (isMaxLevel || isMaxValue) {
self.setDisable(true);
}
}
return true;
};
self.update = function () {
if (self.isPressed) {
self.labelText.y = 335 / 2 - 70 + 28;
self.costText.y = 335 / 2 + 40 + 28;
} else {
self.labelText.y = 335 / 2 - 70;
self.costText.y = 335 / 2 + 40;
}
};
// Initialize game status value
gameStatus[config.value] = self.calculateValue();
// Check if already at max level/value and disable if needed
var isMaxLevel = config.maxLevel && gameStatus.shopLevel[config.value] >= config.maxLevel;
var isMaxValue = config.maxValue && gameStatus[config.value] >= config.maxValue;
if (isMaxLevel || isMaxValue) {
self.setDisable(true);
}
self.updateDisplay();
return self;
});
var Fish = Container.expand(function (fishType) {
var self = Container.call(this);
// Store fish type
if (fishType) {
self.fishType = fishType;
} else {
throw "You must add a fish type.";
}
// Create fish visual
var fishGraphics = self.attachAsset(self.fishType, {
anchorX: 0.5,
anchorY: 0.5
});
// Swimming properties
self.speed = 2 + Math.random() * 3; // Random speed between 2-5
self.direction = Math.random() * Math.PI * 2; // Random initial direction
self.turnSpeed = 0.02 + Math.random() * 0.03; // How fast fish can turn
self.targetDirection = self.direction;
self.wobbleAmount = 0.1 + Math.random() * 0.2; // Slight wobble for natural movement
self.wobbleSpeed = 0.05 + Math.random() * 0.05;
self.wobbleOffset = Math.random() * Math.PI * 2;
// Boundaries with margin
self.margin = 100;
// Initialize position
self.randomPosition = function () {
self.x = self.margin + Math.random() * (2048 - self.margin * 2);
self.y = self.margin + Math.random() * (2732 - self.margin * 2);
};
self.format = function () {
if (self.rate.toFixed(2) <= 0.5) {
var rate = "frame";
} else {
var rate = formatTime(self.rate, 2);
}
return formatMoney(self.value) + " every " + rate;
};
self.destroy = function () {
if (self.incomeTimer) {
LK.clearInterval(self.incomeTimer);
self.incomeTimer = null;
}
Container.prototype.destroy.call(self);
self = undefined;
};
self.update = function () {
if (!self.hasAI) {
return;
}
// Add wobble to movement
var wobble = Math.sin(LK.ticks * self.wobbleSpeed + self.wobbleOffset) * self.wobbleAmount;
// Gradually turn towards target direction
var directionDiff = self.targetDirection - self.direction;
// Normalize angle difference to -PI to PI
while (directionDiff > Math.PI) {
directionDiff -= Math.PI * 2;
}
while (directionDiff < -Math.PI) {
directionDiff += Math.PI * 2;
}
self.direction += directionDiff * self.turnSpeed;
// Move fish
self.x += Math.cos(self.direction + wobble) * self.speed;
self.y += Math.sin(self.direction + wobble) * self.speed;
// Face the direction of movement
fishGraphics.rotation = self.direction;
// Check boundaries and change direction
var turnAwayForce = 0.1;
var boundaryDistance = 200;
if (self.x < self.margin + boundaryDistance) {
self.targetDirection = 0; // Turn right
} else if (self.x > 2048 - self.margin - boundaryDistance) {
self.targetDirection = Math.PI; // Turn left
}
if (self.y < self.margin + boundaryDistance) {
self.targetDirection = Math.PI / 2; // Turn down
} else if (self.y > 2732 - self.margin - boundaryDistance - 200) {
self.targetDirection = -Math.PI / 2; // Turn up
}
// Occasionally change direction randomly
if (Math.random() < 0.005) {
// 0.5% chance per frame
self.targetDirection = Math.random() * Math.PI * 2;
}
// Keep fish within bounds (hard limit)
self.x = Math.max(self.margin, Math.min(2048 - self.margin, self.x));
self.y = Math.max(self.margin, Math.min(2732 - self.margin, self.y));
};
return self;
});
// Anything stored in GameStatus will be saved
var GameStatus = Container.expand(function () {
var self = Container.call(this);
self.money = 0;
self.fishes = [];
self.bait = 20;
self.baitTimer = 300;
self.fishValue = 1;
self.fishMax = 3;
self.fishRate = 3.6e+6;
self.shopLevel = {};
self.tutorial = true;
// storage.hasSaved = false;
if (storage.hasSaved) {
self.money = storage.money || 0;
var deserializedFishes = deserializeFish(storage.fishes || "");
self.fishes = deserializedFishes;
self.bait = storage.bait || 20;
self.baitTimer = storage.baitTimer || 300;
self.fishValue = storage.fishValue || 1;
self.fishMax = storage.fishMax || 3;
self.fishRate = storage.fishRate || 3.6e+6;
self.shopLevel = storage.shopLevel || {};
self.tutorial = storage.tutorial || storage.tutorial !== false;
var dt = Date.now() - storage.time;
// dt = 1000*3600*8
var t = 0;
self.fishes.forEach(function (fish, i) {
if (fish.rate === 0) {
var earning = Math.floor(dt / 40, dt) * fish.value;
} else {
var earning = Math.floor(dt / fish.rate, dt) * fish.value;
}
t += earning;
self.money += earning;
console.log("".concat(fish.fishType, ": ").concat(formatMoney(earning), ", ").concat(dt));
});
console.log("Total: ".concat(formatMoney(t)));
if (self.bait < 20) {
self.bait += Math.floor(dt / (300 * 1000));
self.baitTimer -= Math.floor(dt % (300 * 1000) / 1000);
if (self.baitTimer < 0) {
self.baitTimer = trueMod(self.baitTimer, 300);
self.bait++;
}
if (self.bait >= 20) {
self.bait = 20;
}
}
}
self.fishesAtCapacity = function () {
return self.fishes.length >= self.fishMax;
};
self.formatMoney = function () {
if (self.money === Infinity) {
return "$Infinity";
}
var str = "$";
if (self.money >= 1000) {
suffixes = ["K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "De"];
var digits = countNumber(self.money);
var sf = suffixes[Math.floor((digits - 1) / 3) - 1];
if (sf === undefined) {
str += String(self.money.toExponential(2));
} else {
str += (self.money / Math.pow(10, Math.floor((digits - 1) / 3) * 3)).toFixed(2) + sf;
}
} else {
str += self.money.toFixed(2);
}
return str;
};
self.updateMoney = function () {
if (self.children.length > 0) {
self.removeChildAt(0);
}
var moneyText = new Text2(self.formatMoney(), {
size: 125,
fill: 0xD7FFD7,
weight: 750
});
moneyText.y = 50;
moneyText.x = 50;
self.save();
self.addChildAt(moneyText, 0);
};
self.save = function () {
storage.hasSaved = true;
storage.version = 0;
storage.time = Date.now();
storage.money = self.money;
var array = [];
self.fishes.forEach(function (obj) {
var fishData = obj.fishType + "|" + obj.value + "|" + obj.rate;
array.push(fishData);
});
storage.fishes = array.join("\n");
storage.bait = self.bait;
storage.baitTimer = self.baitTimer;
storage.fishValue = self.fishValue;
storage.fishMax = self.fishMax;
storage.fishRate = self.fishRate;
storage.shopLevel = self.shopLevel;
storage.tutorial = self.tutorial;
};
self.updateMoney();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x83C7FF
});
/****
* Game Code
****/
function trueMod(a, b) {
return (a % b + b) % b;
}
function countNumber(number) {
return Math.floor(Math.log10(Math.abs(number))) + 1;
}
function formatTime(time, point) {
time = time / 1000;
var str = "";
if (time > 3600) {
str += String((time / 3600).toFixed(point)) + " hour";
} else if (time > 60) {
str += String((time / 60).toFixed(point)) + " minute";
} else {
str += String(time.toFixed(point)) + " second";
}
if (time != 1) {
str += "s";
}
return str;
}
function formatMoney(money) {
if (money === Infinity) {
return "$Infinity";
}
var str = "$";
if (money >= 1000) {
var suffixes = ["K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No", "De"];
var digits = countNumber(money);
var sf = suffixes[Math.floor((digits - 1) / 3) - 1];
if (sf === undefined) {
str += String(money.toExponential(2));
} else {
str += (money / Math.pow(10, Math.floor((digits - 1) / 3) * 3)).toFixed(2) + sf;
}
} else {
str += money.toFixed(2);
}
return str;
}
function normalRandom(min, max, skew) {
skew = skew | 1;
var u = 0,
v = 0;
while (u === 0) {
u = Math.random();
}
while (v === 0) {
v = Math.random();
}
var num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
num = num / 10.0 + 0.5;
if (num > 1 || num < 0) {
num = normalRandom(min, max, skew);
} else {
num = Math.pow(num, skew);
num *= max - min;
num += min;
}
return num;
}
var shine;
var shine2;
function deserializeFish(fishDataString) {
if (!fishDataString || fishDataString.trim() === "") {
return [];
}
var fishArray = [];
var fishLines = fishDataString.split("\n");
for (var i = 0; i < fishLines.length; i++) {
var line = fishLines[i].trim();
if (line === "") {
continue;
}
var parts = line.split("|");
if (parts.length === 3) {
var fishType = parts[0];
var value = parseFloat(parts[1]);
var rate = parseFloat(parts[2]);
// Create new fish with deserialized data
var fish = new Fish(fishType);
fish.scale.set(2);
fish.value = value;
fish.rate = rate;
fish.hasAI = true;
fishArray.push(fish);
}
}
return fishArray;
}
var gameStatus = new GameStatus();
var tabLayout = {};
var fishTypes = ['blueFish', 'greenFish', 'pinkFish', 'redFish'];
function addFish(fish) {
gameStatus.caughtFish.scaleX = 2;
gameStatus.caughtFish.scaleY = 2;
fish.randomPosition();
gameStatus.fishes.push(fish);
fishLayer.addChild(fish);
// Add interval timer for this fish
fish.incomeTimer = LK.setInterval(function () {
gameStatus.money += fish.value;
gameStatus.updateMoney();
}, fish.rate);
gameStatus.save();
}
function randomFish(value, rate) {
if (value === undefined) {
var r = 0;
var v = 0;
} else {
var r = normalRandom(rate / 1.5, rate * 1.5);
var v = Number(normalRandom(value / 1.5, value * 1.5).toFixed(2));
if (r < 0) {
r = 0;
}
}
var randomType = fishTypes[Math.floor(Math.random() * fishTypes.length)];
var fish = new Fish(randomType);
fish.value = v;
fish.rate = r;
return fish;
}
// Initialise the cast screen
var castLayer = new Container();
castLayer.visible = false;
castLayer.attachAsset('castBackground', {
alpha: 0.5
});
var text = new Text2("You caught a fish!", {
size: 150,
fill: 0xFFFFFF,
weight: 700
});
text.x = 2048 / 2 - text.width / 2;
text.y = 2732 / 2 - 900;
castLayer.addChild(text);
var fishText;
function hideCast() {
castLayer.removeChild(gameStatus.caughtFish);
shine.destroy();
shine2.destroy();
fishText.destroy();
castLayer.visible = false;
}
var keepButton = new Button(function () {
hideCast();
gameStatus.save();
gameStatus.caughtFish.hasAI = true;
addFish(gameStatus.caughtFish);
castLayer.visible = false;
gameStatus.caughtFish = undefined;
}, {
text: "Keep",
background: 'rectButtonBackground',
width: 818,
height: 335
});
keepButton.y = 2732 / 2 + 900;
castLayer.addChild(keepButton);
var sellButton = new Button(function () {
hideCast();
gameStatus.money += gameStatus.caughtFish.value;
gameStatus.updateMoney();
gameStatus.caughtFish = undefined;
}, {
text: "Sell",
background: 'rectButtonBackground',
width: 818,
height: 335
});
sellButton.x = 2048 / 2 + 50;
sellButton.y = 2732 / 2 + 900;
castLayer.addChild(sellButton);
var closeButton = new Button(hideCast, {
size: 150,
icon: 'closeIcon'
});
closeButton.x = 2048 - 250;
closeButton.y = 50;
castLayer.addChild(closeButton);
var passiveIncome = new Text2("Passive Income:", {
size: 125,
fill: 0xFFFFFF,
weight: 700
});
passiveIncome.x = 2048 / 2;
passiveIncome.y = 2732 / 2 - 800 + text.height;
passiveIncome.anchor.set(0.5, 0);
castLayer.addChild(passiveIncome);
game.addChild(castLayer);
function cast() {
if (gameStatus.caughtFish === undefined) {
gameStatus.bait--;
gameStatus.caughtFish = randomFish(gameStatus.fishValue, gameStatus.fishRate);
// Update bait counter
baitText.setText("Bait: " + gameStatus.bait + "/20");
}
if (gameStatus.tutorial && gameStatus.fishes.length == 2) {
gameStatus.tutorial = false;
}
sellButton.visible = !gameStatus.tutorial;
if (gameStatus.tutorial) {
keepButton.x = 2048 / 2 - 818 / 2;
} else {
keepButton.x = 2048 / 2 - 818 - 50;
}
keepButton.setDisable(gameStatus.fishesAtCapacity());
shine = LK.getAsset('shine', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.8,
scaleY: 1.8,
alpha: 0.7
});
shine.x = 2048 / 2;
shine.y = 2732 / 2 + 300;
shine.rotation = 0;
castLayer.addChild(shine);
shine2 = LK.getAsset('shine', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
alpha: 0.5
});
shine2.x = 2048 / 2;
shine2.y = 2732 / 2 + 300;
shine2.rotation = 0;
castLayer.addChild(shine2);
// Render fish on cast screen
gameStatus.caughtFish.x = 2048 / 2;
gameStatus.caughtFish.y = 2732 / 2 + 300;
gameStatus.caughtFish.scaleX = 2;
gameStatus.caughtFish.scaleY = 2;
gameStatus.caughtFish.hasAI = false;
castLayer.addChild(gameStatus.caughtFish);
fishText = new Text2(gameStatus.caughtFish.format(), {
size: 125,
fill: 0xC7FFC7,
weight: 750
});
fishText.x = 2048 / 2;
fishText.y = 2732 / 2 - 790 + passiveIncome.height + text.height;
fishText.anchor.set(0.5, 0);
castLayer.addChild(fishText);
// Display the cast screen
castLayer.visible = true;
hideTab();
}
function hideTab() {
tabBackground.visible = false;
if (currTab) {
tabLayout[currTab].visible = false;
currTab = null;
}
}
function updateFishesDisplay() {
if (currTab !== 'fishes') {
return;
}
fishesHeader.setText('Capacity: ' + gameStatus.fishes.length + '/' + gameStatus.fishMax);
fishesHeader.x = (946 - fishesHeader.width) / 2;
// Clear previous fish display - properly destroy assets first
while (tabLayout.fishes.children.length > 1) {
var childToRemove = tabLayout.fishes.children[tabLayout.fishes.children.length - 1];
if (childToRemove.destroy) {
childToRemove.destroy();
} else {
tabLayout.fishes.removeChildAt(tabLayout.fishes.children.length - 1);
}
}
// Display each fish
var yOffset = 230;
for (var i = 0; i < gameStatus.fishes.length; i++) {
var fish = gameStatus.fishes[i];
// Fish icon
var fishIcon = LK.getAsset(fish.fishType, {});
fishIcon.scale.set(0.75);
fishIcon.x = 100;
fishIcon.y = yOffset;
tabLayout.fishes.addChild(fishIcon);
// Fish info text
var fishInfo = new Text2(fish.format(), {
size: 60,
fill: 0xFFFFFF,
weight: 600
});
fishInfo.anchor.set(0, 0.5);
fishInfo.x = 100;
fishInfo.y = yOffset + fishIcon.height + 22;
tabLayout.fishes.addChild(fishInfo);
// Sell button for this fish
var sellFishButton = new Button(function (fishIndex) {
return function () {
var fishToSell = gameStatus.fishes[fishIndex];
// Add money from selling
gameStatus.money += fishToSell.value;
gameStatus.updateMoney();
// Remove fish from array and destroy
gameStatus.fishes.splice(fishIndex, 1);
fishToSell.destroy();
// Update the fishes display
updateFishesDisplay();
gameStatus.save();
return true;
};
}(i), {
text: "Sell",
width: 250,
height: 90
});
sellFishButton.x = 600;
sellFishButton.y = yOffset + fishIcon.height / 2 - 60;
if (!gameStatus.tutorial) {
tabLayout.fishes.addChild(sellFishButton);
}
yOffset += fishIcon.height + fishInfo.height + 25;
// Add separator
if (i < gameStatus.fishes.length - 1) {
var separator = LK.getAsset('separator', {});
separator.x = 85;
separator.y = yOffset;
tabLayout.fishes.addChild(separator);
yOffset += separator.height + 20;
}
}
}
var currTab;
function tabPressed(button) {
if (castLayer.visible) {
return false;
}
tabBackground.visible = true;
if (currTab == button.id) {
return true;
}
if (currTab) {
tabLayout[currTab].visible = false;
}
currTab = button.id;
tabLayout[button.id].visible = true;
if (gameStatus.bait <= 0) {
castButton.setDisable(true);
}
if (currTab == 'fishes') {
updateFishesDisplay();
}
return true;
}
var tabLayer = new Container();
var tabBackground = new Container();
var tabElements = new Container();
tabLayout.cast = tabElements.addChild(new Container());
tabLayout.cast.visible = false;
tabLayout.fishes = tabElements.addChild(new Container());
tabLayout.fishes.visible = false;
tabLayout.shop = tabElements.addChild(new Container());
tabLayout.shop.visible = false;
var baitText = new Text2("Bait: " + gameStatus.bait + "/20", {
size: 100,
fill: 0xFFFFFF,
weight: 700
});
baitText.x = (946 - baitText.width) / 2;
baitText.y = 70;
tabLayout.cast.addChild(baitText);
var baitTimerText = new Text2("", {
size: 80,
fill: 0xFFFFFF,
weight: 600
});
baitTimerText.x = (946 - baitTimerText.width) / 2;
baitTimerText.y = baitText.y + baitText.height + 10;
tabLayout.cast.addChild(baitTimerText);
var castButton = new Button(cast, {
width: 818,
height: 335,
background: 'rectButtonBackground',
text: "Cast"
});
castButton.x = (946 - 818) / 2;
castButton.y = 95 + baitText.height;
tabLayout.cast.addChild(castButton);
var fishesHeader = new Text2('Capacity: ', {
size: 100,
fill: 0xFFFFFF,
weight: 700
});
fishesHeader.x = (946 - fishesHeader.width) / 2;
fishesHeader.y = 70;
tabLayout.fishes.addChild(fishesHeader);
var fishValueShop = new ButtonShop({
value: 'fishValue',
baseCost: 1,
label: 'Caught Fish Value'
});
fishValueShop.x = (946 - 818) / 2;
fishValueShop.y = 70;
var fishValueShop = new ButtonShop({
value: 'fishValue',
baseCost: 1,
label: 'Caught Fish Value'
});
fishValueShop.x = (946 - 818) / 2;
fishValueShop.y = 70;
tabLayout.shop.addChild(fishValueShop);
var fishRateShop = new ButtonShop({
value: 'fishRate',
baseCost: 100,
label: 'Caught Fish Rate',
maxLevel: 195,
calculateValue: function calculateValue(level, isMax) {
if (isMax) {
return 0;
}
// Use exponential decay: rate decreases a lot at first, then less at higher levels
// Lower rate = faster income generation (better for player)
var baseRate = 3.6e+6;
var decayRate = 0.95;
var currentRate = baseRate * Math.pow(decayRate, level - 1);
return currentRate;
}
});
fishRateShop.x = (946 - 818) / 2;
fishRateShop.y = 70 + 25 + 335 * 1;
tabLayout.shop.addChild(fishRateShop);
var fishMaxShop = new ButtonShop({
value: 'fishMax',
baseCost: 2500,
label: 'Fish Capacity',
maxValue: 8,
calculateValue: function calculateValue(level) {
return Math.min(8, level * 3);
},
calculateCost: function calculateCost(level) {
return level * 2500 * 1000;
}
});
fishMaxShop.x = (946 - 818) / 2;
fishMaxShop.y = 70 + (335 + 25) * 2;
tabLayout.shop.addChild(fishMaxShop);
tabLayer.addChild(tabBackground);
tabLayer.addChild(tabElements);
tabBackground.x = 2048 - 976;
tabBackground.y = 25;
tabElements.x = 2048 - 976;
tabElements.y = 25;
tabBackground.visible = false;
tabBackground.attachAsset('tabBackgroundTL', {});
tabBackground.attachAsset('tabBackgroundBL', {
y: 1155
});
tabBackground.attachAsset('tabBackgroundBR', {
x: 473,
y: 1155
});
tabBackground.attachAsset('tabBackgroundTR', {
x: 473
});
var castTab = new Button(tabPressed, {
width: 300,
height: 346,
background: "tabButtonBackground",
icon: 'castIcon',
id: "cast"
});
castTab.x = 2048 - 325 * 3;
castTab.y = 2732 - 350;
tabLayer.addChild(castTab);
var fishesTab = new Button(tabPressed, {
width: 300,
height: 346,
background: "tabButtonBackground",
icon: "fishesIcon",
id: "fishes"
});
fishesTab.x = 2048 - 325 * 2;
fishesTab.y = 2732 - 350;
tabLayer.addChild(fishesTab);
var shopTab = new Button(tabPressed, {
width: 300,
height: 346,
background: "tabButtonBackground",
icon: "shopIcon",
id: "shop"
});
shopTab.x = 2048 - 325;
shopTab.y = 2732 - 350;
tabLayer.addChild(shopTab);
var fishLayer = new Container();
game.addChild(fishLayer);
var fishes = [];
game.attachAsset('waterBackground', {});
game.addChild(fishLayer);
game.addChild(gameStatus);
game.addChild(tabLayer);
game.addChild(castLayer);
// Load saved fishes and add them to the game
for (var i = 0; i < gameStatus.fishes.length; i++) {
var fish = gameStatus.fishes[i];
fish.randomPosition();
fishLayer.addChild(fish);
// Add interval timer for this fish
fish.incomeTimer = LK.setInterval(function (fishInstance) {
return function () {
gameStatus.money += fishInstance.value;
gameStatus.updateMoney();
};
}(fish), fish.rate);
}
// Bait regeneration timer
LK.setInterval(function () {
// Only decrease timer if bait is not at maximum
if (gameStatus.bait < 20) {
gameStatus.baitTimer--;
// When timer reaches 0, increase bait and reset timer
if (gameStatus.baitTimer <= 0) {
gameStatus.bait++;
gameStatus.baitTimer = 300;
// Update bait display
baitText.setText("Bait: " + gameStatus.bait + "/20");
// Re-enable cast button if we have bait now
if (gameStatus.bait > 0) {
castButton.setDisable(false);
}
}
// Update timer display
var minutes = Math.floor(gameStatus.baitTimer / 60);
var seconds = gameStatus.baitTimer % 60;
var timerString = minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
baitTimerText.setText("Next bait in: " + timerString);
baitTimerText.x = (946 - baitTimerText.width) / 2;
castButton.y = 95 + baitText.height + baitTimerText.height;
}
if (gameStatus.bait >= 20) {
// Hide timer when bait is at maximum
baitTimerText.setText("");
castButton.y = 95 + baitText.height;
}
}, 1000); // Run every 1000ms (1 second)
game.update = function () {
// Rotate shine clockwise if visible
if (shine) {
shine.rotation += 0.01; // Rotate clockwise
}
if (shine2) {
shine2.rotation -= 0.0075; // Rotate anti-clockwise, slower
}
};
game.down = function (x, y, obj) {
if (tabBackground.x > x) {
hideTab();
}
};