User prompt
The fish have to come in pattern that are achievable by the player. No notes in different lanes in the same position. Prefer lanes next to the current lane when switching lanes in the pattern. Lower the number of fish.
User prompt
Now spread the lanes and hooks/lines out vertically so that the second lowest hook is at the center of the screen. Adjust lane touch detection to match.
User prompt
Okay, now instead of 3 hooks on the same line, split them into three different lines of different lengths that are spaced out equally along the boats length on the X axis.
User prompt
Bring the top of the water 10% lower and adjust the rest to match.
User prompt
Update with: var GAME_CONFIG = { SCREEN_CENTER_X: 1024, SCREEN_CENTER_Y: 900, BOAT_Y: 300, WATER_SURFACE_Y: 350, // 3 Lane System LANES: [ { y: 700, name: "shallow" }, // Top lane { y: 900, name: "medium" }, // Middle lane { y: 1100, name: "deep" } // Bottom lane ], // Timing windows PERFECT_WINDOW: 40, GOOD_WINDOW: 80, MISS_WINDOW: 120, // Rest of config stays the same... DEPTHS: [ { level: 1, name: "Shallow Waters", fishSpeed: 6, fishValue: 1, upgradeCost: 0, songs: [ { name: "Gentle Waves", bpm: 100, duration: 60000, pattern: "simple", cost: 0 }, { name: "Morning Tide", bpm: 110, duration: 75000, pattern: "simple", cost: 50 } ] }, // ... other depths ], // Updated patterns with hold fish PATTERNS: { simple: { beatsPerFish: 1, doubleSpawnChance: 0.05, rareSpawnChance: 0.02, holdFishChance: 0.1 }, medium: { beatsPerFish: 0.75, doubleSpawnChance: 0.1, rareSpawnChance: 0.05, holdFishChance: 0.2 }, complex: { beatsPerFish: 0.5, doubleSpawnChance: 0.15, rareSpawnChance: 0.08, holdFishChance: 0.3 }, expert: { beatsPerFish: 0.25, doubleSpawnChance: 0.2, rareSpawnChance: 0.12, holdFishChance: 0.4 } } }; /**** * Updated Fish Class with Hold Mechanics ****/ var Fish = Container.expand(function(type, value, speed, lane, isHoldFish) { var self = Container.call(this); var assetName = type + 'Fish'; var fishGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.type = type; self.value = value; self.speed = speed; self.lane = lane; self.isHoldFish = isHoldFish || false; self.caught = false; self.isSpecial = type === 'rare'; self.shimmerTime = 0; // Hold fish visual indicator if (self.isHoldFish) { fishGraphics.tint = 0xFFD700; // Golden tint for hold fish self.holdStarted = false; self.holdDuration = 0; self.requiredHoldTime = 800; // 800ms hold required } self.update = function() { if (!self.caught) { self.x += self.speed; if (self.isSpecial) { self.shimmerTime += 0.1; fishGraphics.alpha = 0.8 + Math.sin(self.shimmerTime) * 0.2; } } }; self.catchFish = function() { self.caught = true; tween(self, { y: GAME_CONFIG.BOAT_Y, x: GAME_CONFIG.SCREEN_CENTER_X, scaleX: 0.5, scaleY: 0.5, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function() { self.destroy(); } }); }; return self; }); /**** * Updated Fishing Screen Creation with 3 Hooks ****/ function createFishingScreen() { // Water background var water = fishingScreen.addChild(LK.getAsset('water', { x: 0, y: GAME_CONFIG.WATER_SURFACE_Y, alpha: 0.7 })); // Water surface var waterSurface = fishingScreen.addChild(LK.getAsset('waterSurface', { x: 0, y: GAME_CONFIG.WATER_SURFACE_Y, alpha: 0.8 })); // Boat var boat = fishingScreen.addChild(LK.getAsset('boat', { anchorX: 0.5, anchorY: 1, x: GAME_CONFIG.SCREEN_CENTER_X, y: GAME_CONFIG.WATER_SURFACE_Y })); // Create 3 fishing lines and hooks var hooks = []; var lines = []; for (var i = 0; i < 3; i++) { var lane = GAME_CONFIG.LANES[i]; // Fishing line var line = fishingScreen.addChild(LK.getAsset('fishingLine', { anchorX: 0.5, anchorY: 0, x: GAME_CONFIG.SCREEN_CENTER_X, y: GAME_CONFIG.WATER_SURFACE_Y, height: lane.y - GAME_CONFIG.WATER_SURFACE_Y })); lines.push(line); // Hook var hook = fishingScreen.addChild(LK.getAsset('hook', { anchorX: 0.5, anchorY: 0.5, x: GAME_CONFIG.SCREEN_CENTER_X, y: lane.y })); hook.originalY = lane.y; hook.lane = i; hooks.push(hook); } // Lane indicators var laneLabels = []; var laneNames = ['SHALLOW', 'MEDIUM', 'DEEP']; for (var i = 0; i < 3; i++) { var label = new Text2(laneNames[i], { size: 30, fill: 0xFFFFFF, alpha: 0.7 }); label.anchor.set(0, 0.5); label.x = 50; label.y = GAME_CONFIG.LANES[i].y; fishingScreen.addChild(label); laneLabels.push(label); } // UI elements var scoreText = new Text2('Score: 0', { size: 50, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 50; scoreText.y = 50; fishingScreen.addChild(scoreText); var fishText = new Text2('Fish: 0/0', { size: 40, fill: 0xFFFFFF }); fishText.anchor.set(0, 0); fishText.x = 50; fishText.y = 120; fishingScreen.addChild(fishText); var comboText = new Text2('Combo: 0', { size: 40, fill: 0xFF9800 }); comboText.anchor.set(0, 0); comboText.x = 50; comboText.y: 180; fishingScreen.addChild(comboText); // Hold instruction var holdText = new Text2('TAP: Normal Fish | HOLD: Golden Fish', { size: 35, fill: 0xFFD700, alpha: 0.8 }); holdText.anchor.set(0.5, 0); holdText.x = GAME_CONFIG.SCREEN_CENTER_X; holdText.y = 1400; fishingScreen.addChild(holdText); return { boat: boat, hooks: hooks, lines: lines, laneLabels: laneLabels, scoreText: scoreText, fishText: fishText, comboText: comboText }; } /**** * Updated Fish Spawning with Lane System ****/ function spawnFish() { var depthConfig = GameState.getCurrentDepthConfig(); var songConfig = GameState.getCurrentSongConfig(); var pattern = GAME_CONFIG.PATTERNS[songConfig.pattern]; // Choose random lane var laneIndex = Math.floor(Math.random() * 3); var lane = GAME_CONFIG.LANES[laneIndex]; // Determine if this is a hold fish var isHoldFish = Math.random() < pattern.holdFishChance; // Determine fish type and value var fishType, fishValue; var rand = Math.random(); if (rand < pattern.rareSpawnChance) { fishType = 'rare'; fishValue = depthConfig.fishValue * 4; } else if (GameState.selectedDepth >= 2 && rand < 0.3) { fishType = 'deep'; fishValue = Math.floor(depthConfig.fishValue * 2); } else if (GameState.selectedDepth >= 1 && rand < 0.6) { fishType = 'medium'; fishValue = Math.floor(depthConfig.fishValue * 1.5); } else { fishType = 'shallow'; fishValue = depthConfig.fishValue; } // Bonus value for hold fish if (isHoldFish) { fishValue = Math.floor(fishValue * 1.5); } // Create fish var fish = new Fish( fishType, fishValue, Math.random() < 0.5 ? depthConfig.fishSpeed : -depthConfig.fishSpeed, laneIndex, isHoldFish ); fish.x = fish.speed > 0 ? -100 : 2148; fish.y = lane.y; fishArray.push(fish); fishingScreen.addChild(fish); GameState.sessionFishSpawned++; } /**** * Updated Input Handling with Lane Detection and Hold Logic ****/ var inputState = { touching: false, touchLane: -1, touchStartTime: 0, holdFish: null }; function getTouchLane(y) { // Determine which lane the touch is in if (y < 800) return 0; // Shallow lane else if (y < 1000) return 1; // Medium lane else return 2; // Deep lane } function checkCatch(touchLane, isHold, holdDuration) { var hookX = GAME_CONFIG.SCREEN_CENTER_X; var targetY = GAME_CONFIG.LANES[touchLane].y; var closestFish = null; var closestDistance = Infinity; // Find closest fish in the touched lane for (var i = 0; i < fishArray.length; i++) { var fish = fishArray[i]; if (!fish.caught && fish.lane === touchLane) { var distance = Math.abs(fish.x - hookX); if (distance < closestDistance) { closestDistance = distance; closestFish = fish; } } } if (!closestFish) { showFeedback('miss', touchLane); LK.getSound('miss').play(); GameState.combo = 0; return; } // Check if input type matches fish type if (closestFish.isHoldFish && !isHold) { // Hold fish tapped instead of held showFeedback('miss', touchLane); LK.getSound('miss').play(); GameState.combo = 0; return; } else if (!closestFish.isHoldFish && isHold) { // Normal fish held instead of tapped showFeedback('miss', touchLane); LK.getSound('miss').play(); GameState.combo = 0; return; } // For hold fish, check if held long enough if (closestFish.isHoldFish && holdDuration < closestFish.requiredHoldTime) { showFeedback('miss', touchLane); LK.getSound('miss').play(); GameState.combo = 0; return; } var points = 0; var multiplier = Math.max(1, Math.floor(GameState.combo / 10) + 1); if (closestDistance < GAME_CONFIG.PERFECT_WINDOW) { points = closestFish.value * 2 * multiplier; showFeedback('perfect', touchLane); GameState.combo++; } else if (closestDistance < GAME_CONFIG.GOOD_WINDOW) { points = closestFish.value * multiplier; showFeedback('good', touchLane); GameState.combo++; } else if (closestDistance < GAME_CONFIG.MISS_WINDOW) { points = Math.max(1, Math.floor(closestFish.value * 0.5 * multiplier)); showFeedback('good', touchLane); GameState.combo++; } else { showFeedback('miss', touchLane); LK.getSound('miss').play(); GameState.combo = 0; return; } // Successfully caught fish closestFish.catchFish(); fishArray.splice(fishArray.indexOf(closestFish), 1); GameState.sessionScore += points; GameState.money += points; GameState.sessionFishCaught++; GameState.totalFishCaught++; GameState.maxCombo = Math.max(GameState.maxCombo, GameState.combo); LK.getSound('catch').play(); // Hook animation for the specific lane animateHookCatch(touchLane); } function showFeedback(type, lane) { // Create feedback indicator at the specific lane var indicator = new FeedbackIndicator(type); indicator.x = GAME_CONFIG.SCREEN_CENTER_X; indicator.y = GAME_CONFIG.LANES[lane].y; fishingScreen.addChild(indicator); indicator.show(); } function animateHookCatch(laneIndex) { var hook = fishingElements.hooks[laneIndex]; var originalY = hook.originalY; tween(hook, { y: originalY - 30 }, { duration: 150, easing: tween.easeOut, onFinish: function() { tween(hook, { y: originalY }, { duration: 150, easing: tween.easeIn }); } }); } /**** * Updated Input System for Fishing Screen ****/ function handleFishingInput(x, y, isDown) { if (!GameState.gameActive) return; var touchLane = getTouchLane(y); var currentTime = LK.ticks * (1000 / 60); if (isDown) { // Touch started inputState.touching = true; inputState.touchLane = touchLane; inputState.touchStartTime = currentTime; inputState.holdFish = null; // Check for hold fish in this lane for (var i = 0; i < fishArray.length; i++) { var fish = fishArray[i]; if (fish.lane === touchLane && fish.isHoldFish && !fish.caught) { var distance = Math.abs(fish.x - GAME_CONFIG.SCREEN_CENTER_X); if (distance < GAME_CONFIG.MISS_WINDOW) { inputState.holdFish = fish; break; } } } } else { // Touch ended if (inputState.touching && inputState.touchLane === touchLane) { var holdDuration = currentTime - inputState.touchStartTime; var isHold = holdDuration > 200; // 200ms minimum for hold checkCatch(touchLane, isHold, holdDuration); } inputState.touching = false; inputState.holdFish = null; } } /**** * Updated Main Input Handler ****/ game.down = function(x, y, obj) { switch(GameState.currentScreen) { case 'fishing': handleFishingInput(x, y, true); break; // ... other screen handling stays the same } }; game.up = function(x, y, obj) { switch(GameState.currentScreen) { case 'fishing': handleFishingInput(x, y, false); break; } }; /**** * Updated Game Loop with Hold Fish Logic ****/ game.update = function() { if (GameState.currentScreen !== 'fishing' || !GameState.gameActive) { return; } var currentTime = LK.ticks * (1000 / 60); // Initialize game timer if (GameState.songStartTime === 0) { GameState.songStartTime = currentTime; } var songConfig = GameState.getCurrentSongConfig(); var pattern = GAME_CONFIG.PATTERNS[songConfig.pattern]; var beatInterval = 60000 / songConfig.bpm; var spawnInterval = beatInterval * pattern.beatsPerFish; // Check song end if (currentTime - GameState.songStartTime >= songConfig.duration) { endFishingSession(); return; } // Spawn fish on beat if (currentTime - GameState.lastBeatTime >= spawnInterval) { GameState.lastBeatTime = currentTime; GameState.beatCount++; spawnFish(); } // Update fish for (var i = fishArray.length - 1; i >= 0; i--) { var fish = fishArray[i]; fish.update(); // Update hold fish progress if (fish.isHoldFish && inputState.holdFish === fish && inputState.touching) { var holdDuration = currentTime - inputState.touchStartTime; // Visual feedback for holding progress could go here if (holdDuration >= fish.requiredHoldTime && !fish.holdStarted) { fish.holdStarted = true; // Could show visual indication that hold is successful } } // Remove off-screen fish if (fish.x < -150 || fish.x > 2198) { fish.destroy(); fishArray.splice(i, 1); } } // Update UI updateFishingUI(); }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Fix the touch handler for the start button.
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: setTimeout is not a function' in or related to this line: 'setTimeout(function () {' Line Number: 456
Code edit (1 edits merged)
Please save this source code
User prompt
Rhythm Reel: Beat Fishing
Initial prompt
Create a single-screen rhythm fishing game with these specifications: **Visual Layout:** Side-view with boat at top of screen, fishing line with hook at fixed depth, water background below. **Core Gameplay:** Fish swim horizontally (left-to-right or right-to-left) at hook level, arriving on musical beats. Player taps anywhere on screen when fish aligns with hook to catch it. Missed fish swim away. **Mechanics:** - Fish movement synced to 4/4 beat of background music - Perfect timing = catch fish (visual feedback: fish quickly pulled up to boat) - Hook returns to position immediately for next beat - Track caught fish count and display score **Basic Features:** - One fish type, simple alternating spawn directions - Basic tap detection when fish crosses hook position - Catch/miss visual and audio feedback - End-of-song results screen showing fish caught
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var FeedbackIndicator = Container.expand(function (type) { var self = Container.call(this); var indicator = self.attachAsset(type + 'Indicator', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.show = function () { indicator.alpha = 1; indicator.scaleX = 0.5; indicator.scaleY = 0.5; tween(indicator, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 600, easing: tween.easeOut }); }; return self; }); var Fish = Container.expand(function (type, value, speed) { var self = Container.call(this); var assetName = type + 'Fish'; var fishGraphics = self.attachAsset(assetName, { anchorX: 0.5, anchorY: 0.5 }); self.type = type; self.value = value; self.speed = speed; self.caught = false; self.isSpecial = false; // Add shimmer effect for rare fish if (type === 'rare') { self.isSpecial = true; self.shimmerTime = 0; } self.update = function () { if (!self.caught) { self.x += self.speed; // Shimmer effect for special fish if (self.isSpecial) { self.shimmerTime += 0.1; fishGraphics.alpha = 0.8 + Math.sin(self.shimmerTime) * 0.2; } } }; self.catchFish = function () { self.caught = true; // Catch animation tween(self, { y: GAME_CONFIG.BOAT_Y, x: GAME_CONFIG.SCREEN_CENTER_X, scaleX: 0.5, scaleY: 0.5, alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { self.destroy(); } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB }); /**** * Game Code ****/ /**** * Game Configuration ****/ // Audio // Feedback effects // UI Elements // Fish types by depth // Game containers var GAME_CONFIG = { SCREEN_CENTER_X: 1024, WATER_SURFACE_Y: 400, BOAT_Y: 360, // Timing windows (in pixels from hook) PERFECT_WINDOW: 40, GOOD_WINDOW: 80, MISS_WINDOW: 120, // Depth levels DEPTHS: [{ level: 1, depth: 150, name: "Shallow Waters", fishSpeed: 6, fishValue: 10, upgradeCost: 0 }, { level: 2, depth: 250, name: "Mid Waters", fishSpeed: 7, fishValue: 25, upgradeCost: 500 }, { level: 3, depth: 350, name: "Deep Waters", fishSpeed: 8, fishValue: 50, upgradeCost: 1500 }, { level: 4, depth: 450, name: "Abyss", fishSpeed: 9, fishValue: 100, upgradeCost: 5000 }], // Fish spawn patterns by complexity PATTERNS: { simple: { beatsPerFish: 1, doubleSpawnChance: 0.1, rareSpawnChance: 0.05 }, medium: { beatsPerFish: 0.5, doubleSpawnChance: 0.2, rareSpawnChance: 0.1 }, complex: { beatsPerFish: 0.25, doubleSpawnChance: 0.3, rareSpawnChance: 0.15 } }, // Songs and their difficulties SONGS: [{ name: "Gentle Waves", bpm: 100, duration: 90000, pattern: "simple" }, { name: "Ocean Current", bpm: 120, duration: 120000, pattern: "medium" }, { name: "Storm Surge", bpm: 140, duration: 150000, pattern: "complex" }] }; var GameState = { // Player progression currentDepth: 0, money: 0, totalFishCaught: 0, // Current session sessionScore: 0, sessionFishCaught: 0, sessionFishSpawned: 0, combo: 0, maxCombo: 0, // Game state gameActive: false, inShop: false, currentSong: 0, // Timing songStartTime: 0, lastBeatTime: 0, beatCount: 0, getCurrentDepthConfig: function getCurrentDepthConfig() { return GAME_CONFIG.DEPTHS[this.currentDepth]; }, getCurrentSongConfig: function getCurrentSongConfig() { return GAME_CONFIG.SONGS[this.currentSong]; }, canUpgrade: function canUpgrade() { var nextDepth = GAME_CONFIG.DEPTHS[this.currentDepth + 1]; return nextDepth && this.money >= nextDepth.upgradeCost; }, upgrade: function upgrade() { if (this.canUpgrade()) { var nextDepth = GAME_CONFIG.DEPTHS[this.currentDepth + 1]; this.money -= nextDepth.upgradeCost; this.currentDepth++; return true; } return false; } }; var gameWorld = game.addChild(new Container()); var uiLayer = game.addChild(new Container()); var shopLayer = game.addChild(new Container()); // Game objects var fishArray = []; var feedbackIndicators = { perfect: new FeedbackIndicator('perfect'), good: new FeedbackIndicator('good'), miss: new FeedbackIndicator('miss') }; /**** * Create Game World ****/ function createGameWorld() { // Water background var water = gameWorld.addChild(LK.getAsset('water', { x: 0, y: GAME_CONFIG.WATER_SURFACE_Y, alpha: 0.7 })); // Water surface line var waterSurface = gameWorld.addChild(LK.getAsset('waterSurface', { x: 0, y: GAME_CONFIG.WATER_SURFACE_Y, alpha: 0.8 })); // Boat var boat = gameWorld.addChild(LK.getAsset('boat', { anchorX: 0.5, anchorY: 1, x: GAME_CONFIG.SCREEN_CENTER_X, y: GAME_CONFIG.WATER_SURFACE_Y })); // Fishing line var fishingLine = gameWorld.addChild(LK.getAsset('fishingLine', { anchorX: 0.5, anchorY: 0, x: GAME_CONFIG.SCREEN_CENTER_X, y: GAME_CONFIG.WATER_SURFACE_Y })); // Hook var hook = gameWorld.addChild(LK.getAsset('hook', { anchorX: 0.5, anchorY: 0.5, x: GAME_CONFIG.SCREEN_CENTER_X, y: GAME_CONFIG.WATER_SURFACE_Y + GameState.getCurrentDepthConfig().depth })); // Add feedback indicators Object.keys(feedbackIndicators).forEach(function (key) { var indicator = feedbackIndicators[key]; indicator.x = GAME_CONFIG.SCREEN_CENTER_X; indicator.y = GAME_CONFIG.WATER_SURFACE_Y + GameState.getCurrentDepthConfig().depth; gameWorld.addChild(indicator); }); return { boat: boat, hook: hook, fishingLine: fishingLine }; } /**** * Create UI ****/ function createUI() { // Score display var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); scoreText.x = 50; scoreText.y = 50; uiLayer.addChild(scoreText); // Money display var moneyText = new Text2('Money: $' + GameState.money, { size: 60, fill: 0xFFD700 }); moneyText.anchor.set(0, 0); moneyText.x = 50; moneyText.y = 130; uiLayer.addChild(moneyText); // Fish caught display var fishText = new Text2('Fish: 0/0', { size: 50, fill: 0xFFFFFF }); fishText.anchor.set(0, 0); fishText.x = 50; fishText.y = 210; uiLayer.addChild(fishText); // Combo display var comboText = new Text2('Combo: 0', { size: 50, fill: 0xFF9800 }); comboText.anchor.set(0, 0); comboText.x = 50; comboText.y = 280; uiLayer.addChild(comboText); // Depth display var depthText = new Text2(GameState.getCurrentDepthConfig().name, { size: 50, fill: 0x4FC3F7 }); depthText.anchor.set(1, 0); depthText.x = 1900; depthText.y = 50; uiLayer.addChild(depthText); // Shop button var shopButton = uiLayer.addChild(LK.getAsset('shopButton', { anchorX: 0.5, anchorY: 0.5, x: 1800, y: 150 })); var shopButtonText = new Text2('SHOP', { size: 40, fill: 0xFFFFFF }); shopButtonText.anchor.set(0.5, 0.5); shopButtonText.x = 1800; shopButtonText.y = 150; uiLayer.addChild(shopButtonText); return { scoreText: scoreText, moneyText: moneyText, fishText: fishText, comboText: comboText, depthText: depthText, shopButton: shopButton }; } /**** * Create Shop ****/ function createShop() { var shopBg = shopLayer.addChild(LK.getAsset('shopBackground', { anchorX: 0.5, anchorY: 0.5, x: GAME_CONFIG.SCREEN_CENTER_X, y: 900, alpha: 0.9 })); var shopTitle = new Text2('FISHING SHOP', { size: 80, fill: 0xFFFFFF }); shopTitle.anchor.set(0.5, 0.5); shopTitle.x = GAME_CONFIG.SCREEN_CENTER_X; shopTitle.y = 500; shopLayer.addChild(shopTitle); // Close button var closeButton = shopLayer.addChild(LK.getAsset('upgradeButton', { anchorX: 0.5, anchorY: 0.5, x: GAME_CONFIG.SCREEN_CENTER_X, y: 1400, tint: 0xf44336 })); var closeText = new Text2('CLOSE SHOP', { size: 40, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.x = GAME_CONFIG.SCREEN_CENTER_X; closeText.y = 1400; shopLayer.addChild(closeText); // Upgrade button var upgradeButton = shopLayer.addChild(LK.getAsset('upgradeButton', { anchorX: 0.5, anchorY: 0.5, x: GAME_CONFIG.SCREEN_CENTER_X, y: 1000 })); var upgradeText = new Text2('UPGRADE ROD', { size: 40, fill: 0xFFFFFF }); upgradeText.anchor.set(0.5, 0.5); upgradeText.x = GAME_CONFIG.SCREEN_CENTER_X; upgradeText.y = 1000; shopLayer.addChild(upgradeText); shopLayer.visible = false; return { shopBg: shopBg, upgradeButton: upgradeButton, closeButton: closeButton, upgradeText: upgradeText }; } /**** * Game Logic Functions ****/ function spawnFish() { var depthConfig = GameState.getCurrentDepthConfig(); var songConfig = GameState.getCurrentSongConfig(); var pattern = GAME_CONFIG.PATTERNS[songConfig.pattern]; // Determine fish type and value var fishType, fishValue; var rand = Math.random(); if (rand < pattern.rareSpawnChance) { fishType = 'rare'; fishValue = depthConfig.fishValue * 3; } else if (GameState.currentDepth >= 2 && rand < 0.3) { fishType = 'deep'; fishValue = depthConfig.fishValue * 1.5; } else if (GameState.currentDepth >= 1 && rand < 0.6) { fishType = 'medium'; fishValue = depthConfig.fishValue * 1.2; } else { fishType = 'shallow'; fishValue = depthConfig.fishValue; } // Create fish var fish = new Fish(fishType, fishValue, Math.random() < 0.5 ? depthConfig.fishSpeed : -depthConfig.fishSpeed); fish.x = fish.speed > 0 ? -100 : 2148; fish.y = GAME_CONFIG.WATER_SURFACE_Y + depthConfig.depth; fishArray.push(fish); gameWorld.addChild(fish); GameState.sessionFishSpawned++; // Possible double spawn for complex patterns if (Math.random() < pattern.doubleSpawnChance) { LK.setTimeout(function () { spawnFish(); }, 200); } } function checkCatch(hookX, hookY) { var closestFish = null; var closestDistance = Infinity; // Find closest fish to hook for (var i = 0; i < fishArray.length; i++) { var fish = fishArray[i]; if (!fish.caught) { var distance = Math.abs(fish.x - hookX); if (distance < closestDistance) { closestDistance = distance; closestFish = fish; } } } if (!closestFish) { // No fish nearby - show miss feedbackIndicators.miss.show(); LK.getSound('miss').play(); GameState.combo = 0; return; } var points = 0; var multiplier = Math.max(1, Math.floor(GameState.combo / 5) + 1); if (closestDistance < GAME_CONFIG.PERFECT_WINDOW) { // Perfect catch points = closestFish.value * 2 * multiplier; feedbackIndicators.perfect.show(); GameState.combo++; } else if (closestDistance < GAME_CONFIG.GOOD_WINDOW) { // Good catch points = closestFish.value * multiplier; feedbackIndicators.good.show(); GameState.combo++; } else if (closestDistance < GAME_CONFIG.MISS_WINDOW) { // Near miss - still counts but lower points points = Math.floor(closestFish.value * 0.5 * multiplier); feedbackIndicators.good.show(); GameState.combo++; } else { // Complete miss feedbackIndicators.miss.show(); LK.getSound('miss').play(); GameState.combo = 0; return; } // Successfully caught fish closestFish.catchFish(); fishArray.splice(fishArray.indexOf(closestFish), 1); GameState.sessionScore += points; GameState.money += points; GameState.sessionFishCaught++; GameState.totalFishCaught++; GameState.maxCombo = Math.max(GameState.maxCombo, GameState.combo); LK.getSound('catch').play(); if (points > closestFish.value * 1.5) { LK.getSound('coin').play(); } // Hook animation animateHookCatch(); } function animateHookCatch() { var hook = gameWorld.children.find(function (child) { return child.width === 24 && child.height === 32; }); if (hook) { var originalY = hook.y; tween(hook, { y: originalY - 30 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(hook, { y: originalY }, { duration: 150, easing: tween.easeIn }); } }); } } function updateUI() { var ui = uiElements; if (ui) { ui.scoreText.setText('Score: ' + GameState.sessionScore); ui.moneyText.setText('Money: $' + GameState.money); ui.fishText.setText('Fish: ' + GameState.sessionFishCaught + '/' + GameState.sessionFishSpawned); ui.comboText.setText('Combo: ' + GameState.combo); ui.depthText.setText(GameState.getCurrentDepthConfig().name); } } function updateShop() { var shop = shopElements; if (shop && GameState.inShop) { var canUpgrade = GameState.canUpgrade(); var nextDepth = GAME_CONFIG.DEPTHS[GameState.currentDepth + 1]; if (nextDepth) { var cost = nextDepth.upgradeCost; var text = canUpgrade ? 'UPGRADE ROD ($' + cost + ')' : 'NEED $' + cost; shop.upgradeText.setText(text); shop.upgradeButton.tint = canUpgrade ? 0x1976d2 : 0x666666; } else { shop.upgradeText.setText('MAX DEPTH REACHED'); shop.upgradeButton.tint = 0x666666; } } } function toggleShop() { GameState.inShop = !GameState.inShop; shopLayer.visible = GameState.inShop; if (GameState.inShop) { updateShop(); } } function handleUpgrade() { if (GameState.upgrade()) { LK.getSound('upgrade').play(); // Update fishing line and hook position var depthConfig = GameState.getCurrentDepthConfig(); var hook = gameWorld.children.find(function (child) { return child.width === 24 && child.height === 32; }); var line = gameWorld.children.find(function (child) { return child.width === 6 && child.height === 600; }); if (hook) { hook.y = GAME_CONFIG.WATER_SURFACE_Y + depthConfig.depth; } if (line) { line.height = depthConfig.depth; } // Update feedback indicator positions Object.keys(feedbackIndicators).forEach(function (key) { feedbackIndicators[key].y = GAME_CONFIG.WATER_SURFACE_Y + depthConfig.depth; }); updateShop(); } } function endGame() { GameState.gameActive = false; // Clear remaining fish for (var i = fishArray.length - 1; i >= 0; i--) { fishArray[i].destroy(); } fishArray = []; // Show results var resultsContainer = new Container(); game.addChild(resultsContainer); var resultsBg = resultsContainer.addChild(LK.getAsset('shopBackground', { x: 0, y: 0, alpha: 0.9 })); var title = new Text2('Fishing Session Complete!', { size: 100, fill: 0xFFFFFF }); title.anchor.set(0.5, 0.5); title.x = GAME_CONFIG.SCREEN_CENTER_X; title.y = 600; resultsContainer.addChild(title); var scoreResult = new Text2('Score: ' + GameState.sessionScore, { size: 80, fill: 0xFFD700 }); scoreResult.anchor.set(0.5, 0.5); scoreResult.x = GAME_CONFIG.SCREEN_CENTER_X; scoreResult.y = 750; resultsContainer.addChild(scoreResult); var fishResult = new Text2('Fish: ' + GameState.sessionFishCaught + '/' + GameState.sessionFishSpawned, { size: 60, fill: 0xFFFFFF }); fishResult.anchor.set(0.5, 0.5); fishResult.x = GAME_CONFIG.SCREEN_CENTER_X; fishResult.y = 850; resultsContainer.addChild(fishResult); var comboResult = new Text2('Max Combo: ' + GameState.maxCombo, { size: 60, fill: 0xFF9800 }); comboResult.anchor.set(0.5, 0.5); comboResult.x = GAME_CONFIG.SCREEN_CENTER_X; comboResult.y = 950; resultsContainer.addChild(comboResult); var moneyEarned = new Text2('Money Earned: $' + GameState.sessionScore, { size: 60, fill: 0x4CAF50 }); moneyEarned.anchor.set(0.5, 0.5); moneyEarned.x = GAME_CONFIG.SCREEN_CENTER_X; moneyEarned.y = 1050; resultsContainer.addChild(moneyEarned); // Accuracy calculation var accuracy = GameState.sessionFishSpawned > 0 ? Math.round(GameState.sessionFishCaught / GameState.sessionFishSpawned * 100) : 0; var accuracyResult = new Text2('Accuracy: ' + accuracy + '%', { size: 60, fill: 0x2196F3 }); accuracyResult.anchor.set(0.5, 0.5); accuracyResult.x = GAME_CONFIG.SCREEN_CENTER_X; accuracyResult.y = 1150; resultsContainer.addChild(accuracyResult); // Fade in results resultsContainer.alpha = 0; tween(resultsContainer, { alpha: 1 }, { duration: 800, easing: tween.easeOut }); // Reset session stats but keep progression GameState.sessionScore = 0; GameState.sessionFishCaught = 0; GameState.sessionFishSpawned = 0; GameState.combo = 0; GameState.maxCombo = 0; LK.setTimeout(function () { LK.showGameOver(); }, 4000); } var gameObjects = createGameWorld(); var uiElements = createUI(); var shopElements = createShop(); /**** * Input Handling ****/ game.down = function (x, y, obj) { if (GameState.inShop) { // Shop interactions if (shopElements.upgradeButton.containsPoint && shopElements.upgradeButton.containsPoint(x, y)) { handleUpgrade(); } else if (shopElements.closeButton.containsPoint && shopElements.closeButton.containsPoint(x, y)) { toggleShop(); } } else if (GameState.gameActive) { // Fishing gameplay checkCatch(gameObjects.hook.x, gameObjects.hook.y); } else { // Shop button when not in game if (uiElements.shopButton.containsPoint && uiElements.shopButton.containsPoint(x, y)) { toggleShop(); } } }; /**** * Main Game Loop ****/ game.update = function () { var currentTime = LK.ticks * (1000 / 60); // Initialize game on first update if (GameState.songStartTime === 0) { GameState.songStartTime = currentTime; GameState.gameActive = true; LK.playMusic('rhythmTrack'); } if (!GameState.gameActive) { return; } var songConfig = GameState.getCurrentSongConfig(); var pattern = GAME_CONFIG.PATTERNS[songConfig.pattern]; var beatInterval = 60000 / songConfig.bpm; var spawnInterval = beatInterval * pattern.beatsPerFish; // Check song end if (currentTime - GameState.songStartTime >= songConfig.duration) { endGame(); return; } // Spawn fish on beat if (currentTime - GameState.lastBeatTime >= spawnInterval) { GameState.lastBeatTime = currentTime; GameState.beatCount++; spawnFish(); } // Update fish for (var i = fishArray.length - 1; i >= 0; i--) { var fish = fishArray[i]; fish.update(); // Remove off-screen fish if (fish.x < -150 || fish.x > 2198) { fish.destroy(); fishArray.splice(i, 1); } } // Update UI updateUI(); };
===================================================================
--- original.js
+++ change.js
@@ -5,56 +5,76 @@
/****
* Classes
****/
-var Fish = Container.expand(function () {
+var FeedbackIndicator = Container.expand(function (type) {
var self = Container.call(this);
- var fishGraphics = self.attachAsset('fish', {
+ var indicator = self.attachAsset(type + 'Indicator', {
anchorX: 0.5,
+ anchorY: 0.5,
+ alpha: 0
+ });
+ self.show = function () {
+ indicator.alpha = 1;
+ indicator.scaleX = 0.5;
+ indicator.scaleY = 0.5;
+ tween(indicator, {
+ scaleX: 1.5,
+ scaleY: 1.5,
+ alpha: 0
+ }, {
+ duration: 600,
+ easing: tween.easeOut
+ });
+ };
+ return self;
+});
+var Fish = Container.expand(function (type, value, speed) {
+ var self = Container.call(this);
+ var assetName = type + 'Fish';
+ var fishGraphics = self.attachAsset(assetName, {
+ anchorX: 0.5,
anchorY: 0.5
});
- self.speed = 8;
+ self.type = type;
+ self.value = value;
+ self.speed = speed;
self.caught = false;
+ self.isSpecial = false;
+ // Add shimmer effect for rare fish
+ if (type === 'rare') {
+ self.isSpecial = true;
+ self.shimmerTime = 0;
+ }
self.update = function () {
if (!self.caught) {
self.x += self.speed;
+ // Shimmer effect for special fish
+ if (self.isSpecial) {
+ self.shimmerTime += 0.1;
+ fishGraphics.alpha = 0.8 + Math.sin(self.shimmerTime) * 0.2;
+ }
}
};
self.catchFish = function () {
self.caught = true;
+ // Catch animation
tween(self, {
- y: self.y - 100,
+ y: GAME_CONFIG.BOAT_Y,
+ x: GAME_CONFIG.SCREEN_CENTER_X,
+ scaleX: 0.5,
+ scaleY: 0.5,
alpha: 0
}, {
- duration: 500,
+ duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
-var PerfectIndicator = Container.expand(function () {
- var self = Container.call(this);
- var indicator = self.attachAsset('perfectIndicator', {
- anchorX: 0.5,
- anchorY: 0.5,
- alpha: 0
- });
- self.show = function () {
- indicator.alpha = 1;
- tween(indicator, {
- scaleX: 2,
- scaleY: 2,
- alpha: 0
- }, {
- duration: 400,
- easing: tween.easeOut
- });
- };
- return self;
-});
/****
* Initialize Game
****/
@@ -64,221 +84,641 @@
/****
* Game Code
****/
-// Game constants
-var HOOK_DEPTH = 800;
-var FISH_Y = HOOK_DEPTH;
-var BOAT_Y = 200;
-var PERFECT_TIMING_WINDOW = 50;
-var GOOD_TIMING_WINDOW = 100;
-var BPM = 120;
-var BEAT_INTERVAL = 60000 / BPM;
-var SONG_DURATION = 60000; // 1 minute song
-// Game variables
+/****
+* Game Configuration
+****/
+// Audio
+// Feedback effects
+// UI Elements
+// Fish types by depth
+// Game containers
+var GAME_CONFIG = {
+ SCREEN_CENTER_X: 1024,
+ WATER_SURFACE_Y: 400,
+ BOAT_Y: 360,
+ // Timing windows (in pixels from hook)
+ PERFECT_WINDOW: 40,
+ GOOD_WINDOW: 80,
+ MISS_WINDOW: 120,
+ // Depth levels
+ DEPTHS: [{
+ level: 1,
+ depth: 150,
+ name: "Shallow Waters",
+ fishSpeed: 6,
+ fishValue: 10,
+ upgradeCost: 0
+ }, {
+ level: 2,
+ depth: 250,
+ name: "Mid Waters",
+ fishSpeed: 7,
+ fishValue: 25,
+ upgradeCost: 500
+ }, {
+ level: 3,
+ depth: 350,
+ name: "Deep Waters",
+ fishSpeed: 8,
+ fishValue: 50,
+ upgradeCost: 1500
+ }, {
+ level: 4,
+ depth: 450,
+ name: "Abyss",
+ fishSpeed: 9,
+ fishValue: 100,
+ upgradeCost: 5000
+ }],
+ // Fish spawn patterns by complexity
+ PATTERNS: {
+ simple: {
+ beatsPerFish: 1,
+ doubleSpawnChance: 0.1,
+ rareSpawnChance: 0.05
+ },
+ medium: {
+ beatsPerFish: 0.5,
+ doubleSpawnChance: 0.2,
+ rareSpawnChance: 0.1
+ },
+ complex: {
+ beatsPerFish: 0.25,
+ doubleSpawnChance: 0.3,
+ rareSpawnChance: 0.15
+ }
+ },
+ // Songs and their difficulties
+ SONGS: [{
+ name: "Gentle Waves",
+ bpm: 100,
+ duration: 90000,
+ pattern: "simple"
+ }, {
+ name: "Ocean Current",
+ bpm: 120,
+ duration: 120000,
+ pattern: "medium"
+ }, {
+ name: "Storm Surge",
+ bpm: 140,
+ duration: 150000,
+ pattern: "complex"
+ }]
+};
+var GameState = {
+ // Player progression
+ currentDepth: 0,
+ money: 0,
+ totalFishCaught: 0,
+ // Current session
+ sessionScore: 0,
+ sessionFishCaught: 0,
+ sessionFishSpawned: 0,
+ combo: 0,
+ maxCombo: 0,
+ // Game state
+ gameActive: false,
+ inShop: false,
+ currentSong: 0,
+ // Timing
+ songStartTime: 0,
+ lastBeatTime: 0,
+ beatCount: 0,
+ getCurrentDepthConfig: function getCurrentDepthConfig() {
+ return GAME_CONFIG.DEPTHS[this.currentDepth];
+ },
+ getCurrentSongConfig: function getCurrentSongConfig() {
+ return GAME_CONFIG.SONGS[this.currentSong];
+ },
+ canUpgrade: function canUpgrade() {
+ var nextDepth = GAME_CONFIG.DEPTHS[this.currentDepth + 1];
+ return nextDepth && this.money >= nextDepth.upgradeCost;
+ },
+ upgrade: function upgrade() {
+ if (this.canUpgrade()) {
+ var nextDepth = GAME_CONFIG.DEPTHS[this.currentDepth + 1];
+ this.money -= nextDepth.upgradeCost;
+ this.currentDepth++;
+ return true;
+ }
+ return false;
+ }
+};
+var gameWorld = game.addChild(new Container());
+var uiLayer = game.addChild(new Container());
+var shopLayer = game.addChild(new Container());
+// Game objects
var fishArray = [];
-var score = 0;
-var fishCaught = 0;
-var totalFishSpawned = 0;
-var gameActive = true;
-var lastBeatTime = 0;
-var gameStartTime = 0;
-// Create water background
-var water = game.addChild(LK.getAsset('water', {
- x: 0,
- y: 532,
- alpha: 0.3
-}));
-// Create boat
-var boat = game.addChild(LK.getAsset('boat', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 1024,
- y: BOAT_Y
-}));
-// Create fishing line
-var fishingLine = game.addChild(LK.getAsset('fishingLine', {
- anchorX: 0.5,
- anchorY: 0,
- x: 1024,
- y: BOAT_Y + 50
-}));
-// Create hook
-var hook = game.addChild(LK.getAsset('hook', {
- anchorX: 0.5,
- anchorY: 0.5,
- x: 1024,
- y: HOOK_DEPTH
-}));
-// Create perfect timing indicator
-var perfectIndicator = game.addChild(new PerfectIndicator());
-perfectIndicator.x = 1024;
-perfectIndicator.y = HOOK_DEPTH;
-// Score display
-var scoreText = new Text2('Score: 0', {
- size: 80,
- fill: 0xFFFFFF
-});
-scoreText.anchor.set(0.5, 0);
-LK.gui.top.addChild(scoreText);
-// Fish caught display
-var fishCaughtText = new Text2('Fish: 0', {
- size: 60,
- fill: 0xFFFFFF
-});
-fishCaughtText.anchor.set(0.5, 0);
-fishCaughtText.y = 100;
-LK.gui.top.addChild(fishCaughtText);
-// Function to spawn fish on beat
+var feedbackIndicators = {
+ perfect: new FeedbackIndicator('perfect'),
+ good: new FeedbackIndicator('good'),
+ miss: new FeedbackIndicator('miss')
+};
+/****
+* Create Game World
+****/
+function createGameWorld() {
+ // Water background
+ var water = gameWorld.addChild(LK.getAsset('water', {
+ x: 0,
+ y: GAME_CONFIG.WATER_SURFACE_Y,
+ alpha: 0.7
+ }));
+ // Water surface line
+ var waterSurface = gameWorld.addChild(LK.getAsset('waterSurface', {
+ x: 0,
+ y: GAME_CONFIG.WATER_SURFACE_Y,
+ alpha: 0.8
+ }));
+ // Boat
+ var boat = gameWorld.addChild(LK.getAsset('boat', {
+ anchorX: 0.5,
+ anchorY: 1,
+ x: GAME_CONFIG.SCREEN_CENTER_X,
+ y: GAME_CONFIG.WATER_SURFACE_Y
+ }));
+ // Fishing line
+ var fishingLine = gameWorld.addChild(LK.getAsset('fishingLine', {
+ anchorX: 0.5,
+ anchorY: 0,
+ x: GAME_CONFIG.SCREEN_CENTER_X,
+ y: GAME_CONFIG.WATER_SURFACE_Y
+ }));
+ // Hook
+ var hook = gameWorld.addChild(LK.getAsset('hook', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: GAME_CONFIG.SCREEN_CENTER_X,
+ y: GAME_CONFIG.WATER_SURFACE_Y + GameState.getCurrentDepthConfig().depth
+ }));
+ // Add feedback indicators
+ Object.keys(feedbackIndicators).forEach(function (key) {
+ var indicator = feedbackIndicators[key];
+ indicator.x = GAME_CONFIG.SCREEN_CENTER_X;
+ indicator.y = GAME_CONFIG.WATER_SURFACE_Y + GameState.getCurrentDepthConfig().depth;
+ gameWorld.addChild(indicator);
+ });
+ return {
+ boat: boat,
+ hook: hook,
+ fishingLine: fishingLine
+ };
+}
+/****
+* Create UI
+****/
+function createUI() {
+ // Score display
+ var scoreText = new Text2('Score: 0', {
+ size: 60,
+ fill: 0xFFFFFF
+ });
+ scoreText.anchor.set(0, 0);
+ scoreText.x = 50;
+ scoreText.y = 50;
+ uiLayer.addChild(scoreText);
+ // Money display
+ var moneyText = new Text2('Money: $' + GameState.money, {
+ size: 60,
+ fill: 0xFFD700
+ });
+ moneyText.anchor.set(0, 0);
+ moneyText.x = 50;
+ moneyText.y = 130;
+ uiLayer.addChild(moneyText);
+ // Fish caught display
+ var fishText = new Text2('Fish: 0/0', {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ fishText.anchor.set(0, 0);
+ fishText.x = 50;
+ fishText.y = 210;
+ uiLayer.addChild(fishText);
+ // Combo display
+ var comboText = new Text2('Combo: 0', {
+ size: 50,
+ fill: 0xFF9800
+ });
+ comboText.anchor.set(0, 0);
+ comboText.x = 50;
+ comboText.y = 280;
+ uiLayer.addChild(comboText);
+ // Depth display
+ var depthText = new Text2(GameState.getCurrentDepthConfig().name, {
+ size: 50,
+ fill: 0x4FC3F7
+ });
+ depthText.anchor.set(1, 0);
+ depthText.x = 1900;
+ depthText.y = 50;
+ uiLayer.addChild(depthText);
+ // Shop button
+ var shopButton = uiLayer.addChild(LK.getAsset('shopButton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1800,
+ y: 150
+ }));
+ var shopButtonText = new Text2('SHOP', {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ shopButtonText.anchor.set(0.5, 0.5);
+ shopButtonText.x = 1800;
+ shopButtonText.y = 150;
+ uiLayer.addChild(shopButtonText);
+ return {
+ scoreText: scoreText,
+ moneyText: moneyText,
+ fishText: fishText,
+ comboText: comboText,
+ depthText: depthText,
+ shopButton: shopButton
+ };
+}
+/****
+* Create Shop
+****/
+function createShop() {
+ var shopBg = shopLayer.addChild(LK.getAsset('shopBackground', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: GAME_CONFIG.SCREEN_CENTER_X,
+ y: 900,
+ alpha: 0.9
+ }));
+ var shopTitle = new Text2('FISHING SHOP', {
+ size: 80,
+ fill: 0xFFFFFF
+ });
+ shopTitle.anchor.set(0.5, 0.5);
+ shopTitle.x = GAME_CONFIG.SCREEN_CENTER_X;
+ shopTitle.y = 500;
+ shopLayer.addChild(shopTitle);
+ // Close button
+ var closeButton = shopLayer.addChild(LK.getAsset('upgradeButton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: GAME_CONFIG.SCREEN_CENTER_X,
+ y: 1400,
+ tint: 0xf44336
+ }));
+ var closeText = new Text2('CLOSE SHOP', {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ closeText.anchor.set(0.5, 0.5);
+ closeText.x = GAME_CONFIG.SCREEN_CENTER_X;
+ closeText.y = 1400;
+ shopLayer.addChild(closeText);
+ // Upgrade button
+ var upgradeButton = shopLayer.addChild(LK.getAsset('upgradeButton', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: GAME_CONFIG.SCREEN_CENTER_X,
+ y: 1000
+ }));
+ var upgradeText = new Text2('UPGRADE ROD', {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ upgradeText.anchor.set(0.5, 0.5);
+ upgradeText.x = GAME_CONFIG.SCREEN_CENTER_X;
+ upgradeText.y = 1000;
+ shopLayer.addChild(upgradeText);
+ shopLayer.visible = false;
+ return {
+ shopBg: shopBg,
+ upgradeButton: upgradeButton,
+ closeButton: closeButton,
+ upgradeText: upgradeText
+ };
+}
+/****
+* Game Logic Functions
+****/
function spawnFish() {
- var fish = new Fish();
- var side = Math.random() < 0.5 ? -1 : 1;
- fish.x = side === -1 ? -100 : 2148;
- fish.y = FISH_Y;
- fish.speed = side === -1 ? 8 : -8;
+ var depthConfig = GameState.getCurrentDepthConfig();
+ var songConfig = GameState.getCurrentSongConfig();
+ var pattern = GAME_CONFIG.PATTERNS[songConfig.pattern];
+ // Determine fish type and value
+ var fishType, fishValue;
+ var rand = Math.random();
+ if (rand < pattern.rareSpawnChance) {
+ fishType = 'rare';
+ fishValue = depthConfig.fishValue * 3;
+ } else if (GameState.currentDepth >= 2 && rand < 0.3) {
+ fishType = 'deep';
+ fishValue = depthConfig.fishValue * 1.5;
+ } else if (GameState.currentDepth >= 1 && rand < 0.6) {
+ fishType = 'medium';
+ fishValue = depthConfig.fishValue * 1.2;
+ } else {
+ fishType = 'shallow';
+ fishValue = depthConfig.fishValue;
+ }
+ // Create fish
+ var fish = new Fish(fishType, fishValue, Math.random() < 0.5 ? depthConfig.fishSpeed : -depthConfig.fishSpeed);
+ fish.x = fish.speed > 0 ? -100 : 2148;
+ fish.y = GAME_CONFIG.WATER_SURFACE_Y + depthConfig.depth;
fishArray.push(fish);
- game.addChild(fish);
- totalFishSpawned++;
+ gameWorld.addChild(fish);
+ GameState.sessionFishSpawned++;
+ // Possible double spawn for complex patterns
+ if (Math.random() < pattern.doubleSpawnChance) {
+ LK.setTimeout(function () {
+ spawnFish();
+ }, 200);
+ }
}
-// Function to check if player tapped at the right time
-function checkCatch() {
- var hookX = hook.x;
- var caughtFish = null;
+function checkCatch(hookX, hookY) {
+ var closestFish = null;
var closestDistance = Infinity;
- // Find the closest fish to the hook
+ // Find closest fish to hook
for (var i = 0; i < fishArray.length; i++) {
var fish = fishArray[i];
if (!fish.caught) {
var distance = Math.abs(fish.x - hookX);
if (distance < closestDistance) {
closestDistance = distance;
- caughtFish = fish;
+ closestFish = fish;
}
}
}
- if (caughtFish && closestDistance < GOOD_TIMING_WINDOW) {
- // Successful catch
- caughtFish.catchFish();
- fishArray.splice(fishArray.indexOf(caughtFish), 1);
- var points = 0;
- if (closestDistance < PERFECT_TIMING_WINDOW) {
- points = 100;
- perfectIndicator.show();
- } else {
- points = 50;
- }
- score += points;
- fishCaught++;
- scoreText.setText('Score: ' + score);
- fishCaughtText.setText('Fish: ' + fishCaught);
- LK.getSound('catch').play();
- // Animate hook
+ if (!closestFish) {
+ // No fish nearby - show miss
+ feedbackIndicators.miss.show();
+ LK.getSound('miss').play();
+ GameState.combo = 0;
+ return;
+ }
+ var points = 0;
+ var multiplier = Math.max(1, Math.floor(GameState.combo / 5) + 1);
+ if (closestDistance < GAME_CONFIG.PERFECT_WINDOW) {
+ // Perfect catch
+ points = closestFish.value * 2 * multiplier;
+ feedbackIndicators.perfect.show();
+ GameState.combo++;
+ } else if (closestDistance < GAME_CONFIG.GOOD_WINDOW) {
+ // Good catch
+ points = closestFish.value * multiplier;
+ feedbackIndicators.good.show();
+ GameState.combo++;
+ } else if (closestDistance < GAME_CONFIG.MISS_WINDOW) {
+ // Near miss - still counts but lower points
+ points = Math.floor(closestFish.value * 0.5 * multiplier);
+ feedbackIndicators.good.show();
+ GameState.combo++;
+ } else {
+ // Complete miss
+ feedbackIndicators.miss.show();
+ LK.getSound('miss').play();
+ GameState.combo = 0;
+ return;
+ }
+ // Successfully caught fish
+ closestFish.catchFish();
+ fishArray.splice(fishArray.indexOf(closestFish), 1);
+ GameState.sessionScore += points;
+ GameState.money += points;
+ GameState.sessionFishCaught++;
+ GameState.totalFishCaught++;
+ GameState.maxCombo = Math.max(GameState.maxCombo, GameState.combo);
+ LK.getSound('catch').play();
+ if (points > closestFish.value * 1.5) {
+ LK.getSound('coin').play();
+ }
+ // Hook animation
+ animateHookCatch();
+}
+function animateHookCatch() {
+ var hook = gameWorld.children.find(function (child) {
+ return child.width === 24 && child.height === 32;
+ });
+ if (hook) {
+ var originalY = hook.y;
tween(hook, {
- y: hook.y - 20
+ y: originalY - 30
}, {
- duration: 100,
+ duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(hook, {
- y: HOOK_DEPTH
+ y: originalY
}, {
- duration: 100,
+ duration: 150,
easing: tween.easeIn
});
}
});
- } else {
- // Miss
- LK.getSound('miss').play();
}
}
-// Touch handler
-game.down = function (x, y, obj) {
- if (gameActive) {
- checkCatch();
+function updateUI() {
+ var ui = uiElements;
+ if (ui) {
+ ui.scoreText.setText('Score: ' + GameState.sessionScore);
+ ui.moneyText.setText('Money: $' + GameState.money);
+ ui.fishText.setText('Fish: ' + GameState.sessionFishCaught + '/' + GameState.sessionFishSpawned);
+ ui.comboText.setText('Combo: ' + GameState.combo);
+ ui.depthText.setText(GameState.getCurrentDepthConfig().name);
}
-};
-// End game function
+}
+function updateShop() {
+ var shop = shopElements;
+ if (shop && GameState.inShop) {
+ var canUpgrade = GameState.canUpgrade();
+ var nextDepth = GAME_CONFIG.DEPTHS[GameState.currentDepth + 1];
+ if (nextDepth) {
+ var cost = nextDepth.upgradeCost;
+ var text = canUpgrade ? 'UPGRADE ROD ($' + cost + ')' : 'NEED $' + cost;
+ shop.upgradeText.setText(text);
+ shop.upgradeButton.tint = canUpgrade ? 0x1976d2 : 0x666666;
+ } else {
+ shop.upgradeText.setText('MAX DEPTH REACHED');
+ shop.upgradeButton.tint = 0x666666;
+ }
+ }
+}
+function toggleShop() {
+ GameState.inShop = !GameState.inShop;
+ shopLayer.visible = GameState.inShop;
+ if (GameState.inShop) {
+ updateShop();
+ }
+}
+function handleUpgrade() {
+ if (GameState.upgrade()) {
+ LK.getSound('upgrade').play();
+ // Update fishing line and hook position
+ var depthConfig = GameState.getCurrentDepthConfig();
+ var hook = gameWorld.children.find(function (child) {
+ return child.width === 24 && child.height === 32;
+ });
+ var line = gameWorld.children.find(function (child) {
+ return child.width === 6 && child.height === 600;
+ });
+ if (hook) {
+ hook.y = GAME_CONFIG.WATER_SURFACE_Y + depthConfig.depth;
+ }
+ if (line) {
+ line.height = depthConfig.depth;
+ }
+ // Update feedback indicator positions
+ Object.keys(feedbackIndicators).forEach(function (key) {
+ feedbackIndicators[key].y = GAME_CONFIG.WATER_SURFACE_Y + depthConfig.depth;
+ });
+ updateShop();
+ }
+}
function endGame() {
- gameActive = false;
- // Remove remaining fish
+ GameState.gameActive = false;
+ // Clear remaining fish
for (var i = fishArray.length - 1; i >= 0; i--) {
fishArray[i].destroy();
}
fishArray = [];
// Show results
var resultsContainer = new Container();
game.addChild(resultsContainer);
- var resultsBg = resultsContainer.addChild(LK.getAsset('water', {
+ var resultsBg = resultsContainer.addChild(LK.getAsset('shopBackground', {
x: 0,
y: 0,
- alpha: 0.8,
- tint: 0x000000
+ alpha: 0.9
}));
- var resultsText = new Text2('Fishing Complete!', {
- size: 120,
+ var title = new Text2('Fishing Session Complete!', {
+ size: 100,
fill: 0xFFFFFF
});
- resultsText.anchor.set(0.5, 0.5);
- resultsText.x = 1024;
- resultsText.y = 800;
- resultsContainer.addChild(resultsText);
- var finalScoreText = new Text2('Final Score: ' + score, {
+ title.anchor.set(0.5, 0.5);
+ title.x = GAME_CONFIG.SCREEN_CENTER_X;
+ title.y = 600;
+ resultsContainer.addChild(title);
+ var scoreResult = new Text2('Score: ' + GameState.sessionScore, {
size: 80,
fill: 0xFFD700
});
- finalScoreText.anchor.set(0.5, 0.5);
- finalScoreText.x = 1024;
- finalScoreText.y = 1000;
- resultsContainer.addChild(finalScoreText);
- var fishCaughtResultText = new Text2('Fish Caught: ' + fishCaught + '/' + totalFishSpawned, {
+ scoreResult.anchor.set(0.5, 0.5);
+ scoreResult.x = GAME_CONFIG.SCREEN_CENTER_X;
+ scoreResult.y = 750;
+ resultsContainer.addChild(scoreResult);
+ var fishResult = new Text2('Fish: ' + GameState.sessionFishCaught + '/' + GameState.sessionFishSpawned, {
size: 60,
fill: 0xFFFFFF
});
- fishCaughtResultText.anchor.set(0.5, 0.5);
- fishCaughtResultText.x = 1024;
- fishCaughtResultText.y = 1150;
- resultsContainer.addChild(fishCaughtResultText);
+ fishResult.anchor.set(0.5, 0.5);
+ fishResult.x = GAME_CONFIG.SCREEN_CENTER_X;
+ fishResult.y = 850;
+ resultsContainer.addChild(fishResult);
+ var comboResult = new Text2('Max Combo: ' + GameState.maxCombo, {
+ size: 60,
+ fill: 0xFF9800
+ });
+ comboResult.anchor.set(0.5, 0.5);
+ comboResult.x = GAME_CONFIG.SCREEN_CENTER_X;
+ comboResult.y = 950;
+ resultsContainer.addChild(comboResult);
+ var moneyEarned = new Text2('Money Earned: $' + GameState.sessionScore, {
+ size: 60,
+ fill: 0x4CAF50
+ });
+ moneyEarned.anchor.set(0.5, 0.5);
+ moneyEarned.x = GAME_CONFIG.SCREEN_CENTER_X;
+ moneyEarned.y = 1050;
+ resultsContainer.addChild(moneyEarned);
+ // Accuracy calculation
+ var accuracy = GameState.sessionFishSpawned > 0 ? Math.round(GameState.sessionFishCaught / GameState.sessionFishSpawned * 100) : 0;
+ var accuracyResult = new Text2('Accuracy: ' + accuracy + '%', {
+ size: 60,
+ fill: 0x2196F3
+ });
+ accuracyResult.anchor.set(0.5, 0.5);
+ accuracyResult.x = GAME_CONFIG.SCREEN_CENTER_X;
+ accuracyResult.y = 1150;
+ resultsContainer.addChild(accuracyResult);
// Fade in results
resultsContainer.alpha = 0;
tween(resultsContainer, {
alpha: 1
}, {
- duration: 500,
+ duration: 800,
easing: tween.easeOut
});
- // Show game over after delay
+ // Reset session stats but keep progression
+ GameState.sessionScore = 0;
+ GameState.sessionFishCaught = 0;
+ GameState.sessionFishSpawned = 0;
+ GameState.combo = 0;
+ GameState.maxCombo = 0;
LK.setTimeout(function () {
LK.showGameOver();
- }, 3000);
+ }, 4000);
}
-// Main game update
+var gameObjects = createGameWorld();
+var uiElements = createUI();
+var shopElements = createShop();
+/****
+* Input Handling
+****/
+game.down = function (x, y, obj) {
+ if (GameState.inShop) {
+ // Shop interactions
+ if (shopElements.upgradeButton.containsPoint && shopElements.upgradeButton.containsPoint(x, y)) {
+ handleUpgrade();
+ } else if (shopElements.closeButton.containsPoint && shopElements.closeButton.containsPoint(x, y)) {
+ toggleShop();
+ }
+ } else if (GameState.gameActive) {
+ // Fishing gameplay
+ checkCatch(gameObjects.hook.x, gameObjects.hook.y);
+ } else {
+ // Shop button when not in game
+ if (uiElements.shopButton.containsPoint && uiElements.shopButton.containsPoint(x, y)) {
+ toggleShop();
+ }
+ }
+};
+/****
+* Main Game Loop
+****/
game.update = function () {
- if (!gameActive) return;
var currentTime = LK.ticks * (1000 / 60);
- // Start game timer on first update
- if (gameStartTime === 0) {
- gameStartTime = currentTime;
+ // Initialize game on first update
+ if (GameState.songStartTime === 0) {
+ GameState.songStartTime = currentTime;
+ GameState.gameActive = true;
LK.playMusic('rhythmTrack');
}
- // Check if song duration has ended
- if (currentTime - gameStartTime >= SONG_DURATION) {
+ if (!GameState.gameActive) {
+ return;
+ }
+ var songConfig = GameState.getCurrentSongConfig();
+ var pattern = GAME_CONFIG.PATTERNS[songConfig.pattern];
+ var beatInterval = 60000 / songConfig.bpm;
+ var spawnInterval = beatInterval * pattern.beatsPerFish;
+ // Check song end
+ if (currentTime - GameState.songStartTime >= songConfig.duration) {
endGame();
return;
}
// Spawn fish on beat
- if (currentTime - lastBeatTime >= BEAT_INTERVAL) {
- lastBeatTime = currentTime;
+ if (currentTime - GameState.lastBeatTime >= spawnInterval) {
+ GameState.lastBeatTime = currentTime;
+ GameState.beatCount++;
spawnFish();
}
- // Update and remove off-screen fish
+ // Update fish
for (var i = fishArray.length - 1; i >= 0; i--) {
var fish = fishArray[i];
- if (fish.lastX === undefined) fish.lastX = fish.x;
- // Check if fish went off screen
- if (fish.lastX >= -100 && fish.x < -100 || fish.lastX <= 2148 && fish.x > 2148) {
+ fish.update();
+ // Remove off-screen fish
+ if (fish.x < -150 || fish.x > 2198) {
fish.destroy();
fishArray.splice(i, 1);
}
- fish.lastX = fish.x;
}
+ // Update UI
+ updateUI();
};
\ No newline at end of file
No background.
A music note. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
A white bubble. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
Blue gradient background starting lighter blue at the top of the image and going to a darker blue at the bottom.. In-Game asset. 2d. High contrast. No shadows
A small single strand of loose floating seaweed. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
A thin wispy white cloud. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
Game logo for the game ‘Beat Fisher’. High def 80’s color themed SVG of the word.. In-Game asset. 2d. High contrast. No shadows
A yellow star burst that says 'Perfect!' in the center. 80s arcade machine graphics. In-Game asset. 2d. High contrast. No shadows
A red starburst with the word ‘Miss!’ In it. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
A green starburst with the word ‘Good!’ In it. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
White stylized square bracket. Pixelated. In-Game asset. 2d. High contrast. No shadows
A golden fish. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
A double sided fishing hook with a small speaker in the center. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
An SVG that says ‘Start’
Blue to green gradient on the word instead of pink.
Above water background image showing gradient of light to dark blue starting at the top. Looking straight down from high above.. In-Game asset. 2d. High contrast. No shadows
This boat, same perspective, boat facing down.
A small island centered with a large mountain taking up most of it with a waterfall on the south side and a fishing village just below it. Under the fishing village is a harbor with a single empty dock. The dock extends into a half open bay. 80s arcade machine inspire high definition graphics with 80s colored highlights. White background. Top down 3/4 view. In-Game asset. 2d. High contrast. No shadows
A small lock icon. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
A seagull with wings spread straight out as if soaring. Top down view, looking down from above. 80s arcade machine graphics. In-Game asset. 2d. High contrast. No shadows
A round button with an embossed edge with an anchor in the center. 80s arcade machine graphics.. In-Game asset. 2d. High contrast. No shadows
A long fluffy white cloud seen from overhead. 80s arcade machine graphics. In-Game asset. 2d. High contrast. No shadows
Much thinner border
The dark shadow silhouette of a fish. Top down view. In-Game asset. 2d. High contrast. No shadows
rhythmTrack
Music
morningtide
Music
miss
Sound effect
catch
Sound effect
catch2
Sound effect
catch3
Sound effect
catch4
Sound effect
seagull1
Sound effect
seagull2
Sound effect
seagull3
Sound effect
boatsounds
Sound effect
sunnyafternoon
Music
reel
Sound effect
sardineRiff
Sound effect
anchovyRiff
Sound effect