/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Archer = Container.expand(function () { var self = Container.call(this); var bowGraphics = self.attachAsset('bow', { anchorX: 0.5, anchorY: 0.5 }); self.bow = bowGraphics; self.aimAngle = 0; self.aimAt = function (targetX, targetY) { var dx = targetX - (self.x + self.bow.x); var dy = targetY - (self.y + self.bow.y); self.aimAngle = Math.atan2(dy, dx); self.bow.rotation = self.aimAngle; }; self.shoot = function () { // Check if we can shoot - either normal arrows (under 5) or bonus arrows available if (!window.bonusArrows) window.bonusArrows = 0; var canShoot = arrows.length < 5 || window.bonusArrows > 0; if (!canShoot) { return; // Can't shoot } var arrow = new Arrow(); var bowWorldX = self.x + self.bow.x + Math.cos(self.aimAngle) * 40; var bowWorldY = self.y + self.bow.y + Math.sin(self.aimAngle) * 40; arrow.x = bowWorldX; arrow.y = bowWorldY; arrow.rotation = self.aimAngle; var power = currentPower; // Use dynamic power based on mouse distance arrow.velocityX = Math.cos(self.aimAngle) * power; arrow.velocityY = Math.sin(self.aimAngle) * power; arrows.push(arrow); game.addChild(arrow); // Use bonus arrow if we're over the 5 arrow limit if (arrows.length > 5 && window.bonusArrows > 0) { window.bonusArrows--; } LK.getSound('shoot').play(); }; return self; }); var Arrow = Container.expand(function () { var self = Container.call(this); var arrowGraphics = self.attachAsset('arrow', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.gravity = 0.3; self.active = true; self.update = function () { if (!self.active) return; self.x += self.velocityX; self.y += self.velocityY; self.velocityY += self.gravity; // Update arrow rotation to follow movement direction if (self.velocityX !== 0 || self.velocityY !== 0) { self.rotation = Math.atan2(self.velocityY, self.velocityX); } // Check platform collisions with smaller collision area for (var i = 0; i < platforms.length; i++) { var platform = platforms[i]; // Calculate distance between arrow and platform center var dx = self.x - platform.x; var dy = self.y - platform.y; // Reduce vertical collision sensitivity by using separate X and Y checks var horizontalDistance = Math.abs(dx); var verticalDistance = Math.abs(dy); // Use collision radius for platforms (100 pixels horizontal, 60 pixels vertical) if (horizontalDistance < 100 && verticalDistance < 60) { self.active = false; self.velocityX = 0; self.velocityY = 0; break; } } // Remove arrow if it goes off screen // Remove arrow if it goes off screen if (self.x > 2100 || self.y > 2800 || self.x < -50 || self.y < -50) { // Give bonus points if arrow didn't hit any platform if (self.active) { LK.setScore(LK.getScore() + 5); scoreTxt.setText(LK.getScore()); } self.destroy(); for (var i = arrows.length - 1; i >= 0; i--) { if (arrows[i] === self) { arrows.splice(i, 1); break; } } } }; return self; }); var ArrowBonus = Container.expand(function () { var self = Container.call(this); var bonusGraphics = self.attachAsset('arrowBonus', { anchorX: 0.5, anchorY: 0.5 }); self.hit = false; self.getHit = function () { if (self.hit) return; self.hit = true; // Give random 2-3 arrows var bonusArrows = Math.floor(Math.random() * 2) + 2; // Random number between 2-3 // Store bonus arrows in global variable so archer can use them if (!window.bonusArrows) window.bonusArrows = 0; window.bonusArrows += bonusArrows; // Flash effect LK.effects.flashObject(self, 0x00ff00, 300); // Remove from arrowBonuses array for (var i = arrowBonuses.length - 1; i >= 0; i--) { if (arrowBonuses[i] === self) { arrowBonuses.splice(i, 1); break; } } // Animate bonus disappearing tween(self, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); LK.getSound('arrowBonus').play(); }; return self; }); var Bomb = Container.expand(function () { var self = Container.call(this); var bombGraphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5 }); self.hit = false; self.getHit = function () { if (self.hit) return; self.hit = true; // Flash effect LK.effects.flashObject(self, 0xff0000, 300); // Remove from bombs array for (var i = bombs.length - 1; i >= 0; i--) { if (bombs[i] === self) { bombs.splice(i, 1); break; } } // Flash screen red and show game over LK.effects.flashScreen(0xff0000, 1000); LK.getSound('explosion').play(); // Animate bomb explosion tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, onFinish: function onFinish() { self.destroy(); // Show game over after explosion animation LK.setTimeout(function () { LK.showGameOver(); }, 200); } }); }; return self; }); var Fruit = Container.expand(function (type) { var self = Container.call(this); var fruitType = type || 'apple'; var fruitGraphics = self.attachAsset(fruitType, { anchorX: 0.5, anchorY: 0.5 }); self.fruitType = fruitType; self.points = fruitType === 'apple' ? 10 : fruitType === 'orange' ? 15 : 20; self.hit = false; // Start idle floating animation self.startIdleAnimation = function () { var animationDuration = 1500 + Math.random() * 1000; // 1.5-2.5 seconds var moveRange = 8 + Math.random() * 12; // 8-20 pixels movement if (self.initialY === undefined) { self.initialY = self.y; // Store the original position only once } // Find the platform this fruit is on var platformY = self.initialY + 60; // Platform is 60 pixels below fruit // Limit upward movement to not go above initial position too much var upwardY = self.initialY - moveRange; // Limit downward movement to not go below platform level var downwardY = Math.min(self.initialY + moveRange, platformY - 50); tween(self, { y: upwardY }, { duration: animationDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (!self.hit) { tween(self, { y: downwardY }, { duration: animationDuration, easing: tween.easeInOut, onFinish: function onFinish() { if (!self.hit) { self.startIdleAnimation(); } } }); } } }); }; self.getHit = function () { if (self.hit) return; self.hit = true; LK.setScore(LK.getScore() + self.points); scoreTxt.setText(LK.getScore()); // Flash effect LK.effects.flashObject(self, 0xFFFFFF, 300); // Remove from fruits array for (var i = fruits.length - 1; i >= 0; i--) { if (fruits[i] === self) { fruits.splice(i, 1); break; } } // Only remove arrows when level is completed (no fruits remain) // The arrows will be cleared when spawnFruits() is called for next level // Create explosion/disintegration effect tween(self, { scaleX: 1.5, scaleY: 1.5, rotation: Math.PI * 2 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Second phase: shrink and fade out tween(self, { alpha: 0, scaleX: 0, scaleY: 0 }, { duration: 200, easing: tween.easeIn, onFinish: function onFinish() { self.destroy(); } }); } }); LK.getSound('hit').play(); // Check win condition if (fruits.length === 0) { // Show level complete animation immediately showLevelCompleteAnimation(); } }; return self; }); var Menu = Container.expand(function () { var self = Container.call(this); // Animated background layers var menuBg1 = self.attachAsset('menuBg1', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 1 }); var menuBg2 = self.attachAsset('menuBg2', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.7 }); self.addChild(menuBg2); // Background stars removed as requested // Animate background layers tween(menuBg2, { alpha: 0.3 }, { duration: 3000, easing: tween.easeInOut, onFinish: function onFinish() { tween(menuBg2, { alpha: 0.7 }, { duration: 3000, easing: tween.easeInOut, onFinish: function onFinish() { // Continue background animation loop } }); } }); // Animated title image var titleImage = self.attachAsset('titleImage', { anchorX: 0.5, anchorY: 0.5, scaleX: 0, scaleY: 0, rotation: 0 }); titleImage.x = 1024; titleImage.y = 700; // Title entrance animation with continuous effects tween(titleImage, { scaleX: 1.2, scaleY: 1.2, rotation: Math.PI * 0.1 }, { duration: 1000, easing: tween.elasticOut, onFinish: function onFinish() { // Continuous title animation loop function animateTitle() { tween(titleImage, { scaleX: 1.1, scaleY: 1.1, rotation: -Math.PI * 0.05 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleImage, { scaleX: 1.3, scaleY: 1.3, rotation: Math.PI * 0.05 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateTitle }); } }); } animateTitle(); } }); // Title glow effect with enhanced animation using duplicate image var titleGlow = self.attachAsset('titleImage', { anchorX: 0.5, anchorY: 0.5, scaleX: 0, scaleY: 0, alpha: 0.2, tint: 0xFFFFFF }); titleGlow.x = 1024; titleGlow.y = 700; self.addChildAt(titleGlow, self.getChildIndex(titleImage)); // Enhanced glow animation tween(titleGlow, { scaleX: 1.3, scaleY: 1.3 }, { duration: 1100, easing: tween.elasticOut, onFinish: function onFinish() { // Continuous pulsing glow effect function pulseGlow() { tween(titleGlow, { alpha: 0.8, scaleX: 1.4, scaleY: 1.4 }, { duration: 1800, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleGlow, { alpha: 0.2, scaleX: 1.2, scaleY: 1.2 }, { duration: 1800, easing: tween.easeInOut, onFinish: pulseGlow }); } }); } pulseGlow(); } }); // Enhanced start button with image background var startButtonBg = self.attachAsset('startButtonBg', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1 }); startButtonBg.x = 1024; startButtonBg.y = 1950; self.addChild(startButtonBg); // Start button image overlay - removed bow as requested // Button entrance animation LK.setTimeout(function () { tween(startButtonBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 600, easing: tween.elasticOut }); // Bow animation removed as bow is no longer present }, 1200); // Button hover effect function animateButton() { tween(startButtonBg, { scaleX: 1.05, scaleY: 1.05 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(startButtonBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 1000, easing: tween.easeInOut, onFinish: animateButton }); } }); } LK.setTimeout(animateButton, 2000); // Start button click handler - make the whole button area clickable startButtonBg.down = function (x, y, obj) { // Button press animation tween(startButtonBg, { scaleX: 0.95, scaleY: 0.95 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(startButtonBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Transition to game scene showGameScene(); } }); } }); }; return self; }); var Platform = Container.expand(function () { var self = Container.call(this); var platformGraphics = self.attachAsset('platform', { anchorX: 0.5, anchorY: 0.5 }); return self; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('starBonus', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0xFFD700 // Golden color }); self.hit = false; self.getHit = function () { if (self.hit) return; self.hit = true; // Clear all fruits to complete level immediately for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; LK.setScore(LK.getScore() + fruit.points); fruit.destroy(); fruits.splice(i, 1); } scoreTxt.setText(LK.getScore()); // Flash effect LK.effects.flashObject(self, 0xFFD700, 300); // Remove from stars array for (var i = stars.length - 1; i >= 0; i--) { if (stars[i] === self) { stars.splice(i, 1); break; } } // Animate star disappearing tween(self, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, onFinish: function onFinish() { self.destroy(); } }); LK.getSound('starBonus').play(); // Update arrows display to show bonus effect var remainingArrows = Math.max(0, Math.min(5, maxArrows || 5) - arrows.length); arrowsTxt.setText('Arrows: ' + remainingArrows); // Show level complete animation immediately showLevelCompleteAnimation(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 // Black background as fallback }); /**** * Game Code ****/ // Menu scene var menuScene = null; var gameStarted = false; function showMenuScene() { // Clear game elements if they exist if (gameStarted) { // Clear all game objects for (var i = 0; i < arrows.length; i++) { arrows[i].destroy(); } arrows = []; for (var i = 0; i < fruits.length; i++) { fruits[i].destroy(); } fruits = []; for (var i = 0; i < platforms.length; i++) { platforms[i].destroy(); } platforms = []; for (var i = 0; i < bombs.length; i++) { bombs[i].destroy(); } bombs = []; for (var i = 0; i < arrowBonuses.length; i++) { arrowBonuses[i].destroy(); } arrowBonuses = []; for (var i = 0; i < stars.length; i++) { stars[i].destroy(); } stars = []; for (var i = 0; i < clouds.length; i++) { clouds[i].destroy(); } clouds = []; if (archer) { archer.destroy(); archer = null; } } // Create and show menu menuScene = new Menu(); game.addChild(menuScene); gameStarted = false; } // Define showLevelCompleteAnimation in global scope function showLevelCompleteAnimation() { // Play level complete sound for level completion LK.getSound('levelComplete').play(); // Create a large golden star in the center of the screen var star = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, scaleX: 0, scaleY: 0, alpha: 0, tint: 0xFFD700 // Golden color }); star.x = 1024; // Center of screen horizontally (2048/2) star.y = 1366; // Center of screen vertically (2732/2) game.addChild(star); // Simple growing animation tween(star, { scaleX: 2.5, scaleY: 2.5, alpha: 1 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Fade out after growing tween(star, { alpha: 0 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { star.destroy(); // After star animation completes, proceed to next level LK.setTimeout(function () { var currentLevel = Math.floor(LK.getScore() / 200) + 1; var nextTargetScore = currentLevel * 200; if (LK.getScore() >= targetScore) { targetScore = nextTargetScore + 200; targetTxt.setText('Target: ' + targetScore); spawnFruits(); } else { spawnFruits(); } }, 100); } }); } }); } // Define spawnFruits function in global scope so it can be accessed from anywhere function spawnFruits() { // Clear existing fruits for (var i = 0; i < fruits.length; i++) { fruits[i].destroy(); } fruits = []; // Clear existing bombs for (var i = 0; i < bombs.length; i++) { bombs[i].destroy(); } bombs = []; // Clear existing arrow bonuses for (var i = 0; i < arrowBonuses.length; i++) { arrowBonuses[i].destroy(); } arrowBonuses = []; // Clear existing stars for (var i = 0; i < stars.length; i++) { stars[i].destroy(); } stars = []; // Clear all arrows (including those stuck on platforms) for (var i = 0; i < arrows.length; i++) { arrows[i].destroy(); } arrows = []; // Reset bonus arrows for the new level window.bonusArrows = 0; // Ensure enough arrows for the number of fruits (minimum 3, but at least equal to fruits) var currentLevel = Math.floor(LK.getScore() / 200) + 1; var baseArrows = Math.max(3, 6 - Math.floor(currentLevel / 2)); var calculatedArrows = Math.max(baseArrows, numFruits + 1); maxArrows = Math.min(5, calculatedArrows); // Always have at least 1 more arrow than fruits, but max 5 // Clear existing platforms for (var i = 0; i < platforms.length; i++) { platforms[i].destroy(); } platforms = []; var fruitTypes = ['apple', 'orange', 'banana']; // Calculate level based on score progression var currentLevel = Math.floor(LK.getScore() / 200) + 1; // Increase number of fruits with each level (start with 2, add 1 every 2 levels, max 6) var numFruits = Math.min(6, 2 + Math.floor(currentLevel / 2)); for (var i = 0; i < numFruits; i++) { // Create platform with collision detection var platform = new Platform(); var validPosition = false; var attempts = 0; while (!validPosition && attempts < 50) { // Make platforms more spread out and harder to reach in higher levels var currentLevel = Math.floor(LK.getScore() / 200) + 1; var spreadFactor = Math.min(2, 1 + currentLevel * 0.2); // Ensure platforms stay within screen boundaries and don't appear too low var maxX = 2048 - 150; // Right boundary var maxY = 1800; // Bottom boundary - prevent platforms from appearing too low var minX = 150; // Left boundary var minY = 600; // Top boundary - keep platforms in middle area platform.x = Math.max(minX, Math.min(maxX, 600 + Math.random() * (800 * spreadFactor))); platform.y = Math.max(minY, Math.min(maxY, 800 + Math.random() * (1000 * spreadFactor))); validPosition = true; // Check distance from character to ensure platforms spawn far away var distanceFromCharacter = Math.sqrt((platform.x - 300) * (platform.x - 300) + (platform.y - 1366) * (platform.y - 1366)); if (distanceFromCharacter < 500) { validPosition = false; } // Check collision with existing platforms for (var j = 0; j < platforms.length; j++) { var existingPlatform = platforms[j]; var dx = platform.x - existingPlatform.x; var dy = platform.y - existingPlatform.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 400) { // Minimum distance between platforms validPosition = false; break; } } attempts++; } platforms.push(platform); game.addChild(platform); // Create fruit on platform var fruitType = fruitTypes[Math.floor(Math.random() * fruitTypes.length)]; var fruit = new Fruit(fruitType); fruit.x = platform.x; fruit.y = platform.y - 80; // Position above platform fruits.push(fruit); game.addChild(fruit); // Start idle animation immediately for all fruits fruit.startIdleAnimation(); } // Create separate platforms for bombs // Calculate level based on score progression (every 200 points is roughly a level) var currentLevel = Math.floor(LK.getScore() / 200) + 1; // Increase bomb count with each level, starting with 1 every 2 levels, maximum 3 var numBombs = Math.min(3, Math.floor(currentLevel / 2)); for (var i = 0; i < numBombs; i++) { // Create bomb platform with collision detection var bombPlatform = new Platform(); var validPosition = false; var attempts = 0; while (!validPosition && attempts < 50) { // Make bomb platforms more spread out and harder to avoid in higher levels var currentLevel = Math.floor(LK.getScore() / 200) + 1; var spreadFactor = Math.min(2, 1 + currentLevel * 0.2); // Ensure bomb platforms stay within screen boundaries and don't appear too low var maxX = 2048 - 150; // Right boundary var maxY = 1800; // Bottom boundary - prevent platforms from appearing too low var minX = 150; // Left boundary var minY = 600; // Top boundary - keep platforms in middle area bombPlatform.x = Math.max(minX, Math.min(maxX, 600 + Math.random() * (800 * spreadFactor))); bombPlatform.y = Math.max(minY, Math.min(maxY, 800 + Math.random() * (1000 * spreadFactor))); validPosition = true; // Check distance from character to ensure bomb platforms spawn far away var distanceFromCharacter = Math.sqrt((bombPlatform.x - 300) * (bombPlatform.x - 300) + (bombPlatform.y - 1366) * (bombPlatform.y - 1366)); if (distanceFromCharacter < 500) { validPosition = false; } // Check collision with existing platforms (both fruit and bomb platforms) for (var j = 0; j < platforms.length; j++) { var existingPlatform = platforms[j]; var dx = bombPlatform.x - existingPlatform.x; var dy = bombPlatform.y - existingPlatform.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 400) { // Minimum distance between platforms validPosition = false; break; } } attempts++; } platforms.push(bombPlatform); game.addChild(bombPlatform); // Create bomb on its own platform var bomb = new Bomb(); bomb.x = bombPlatform.x; bomb.y = bombPlatform.y - 75; // Position above platform bombs.push(bomb); game.addChild(bomb); } // Create arrow bonuses (only sometimes, based on level) var currentLevel = Math.floor(LK.getScore() / 200) + 1; // 60% chance to spawn arrow bonus, but only every 2-3 levels var shouldSpawnBonus = Math.random() < 0.6 && currentLevel > 1 && (currentLevel % 2 === 0 || currentLevel % 3 === 0); if (shouldSpawnBonus) { // Create arrow bonus platform with collision detection var bonusPlatform = new Platform(); var validPosition = false; var attempts = 0; while (!validPosition && attempts < 50) { // Ensure bonus platforms stay within screen boundaries and don't appear too low var maxX = 2048 - 150; // Right boundary var maxY = 1800; // Bottom boundary - prevent platforms from appearing too low var minX = 150; // Left boundary var minY = 600; // Top boundary - keep platforms in middle area bonusPlatform.x = Math.max(minX, Math.min(maxX, 600 + Math.random() * 800)); bonusPlatform.y = Math.max(minY, Math.min(maxY, 800 + Math.random() * 1200)); validPosition = true; // Check distance from character to ensure bonus platforms spawn far away var distanceFromCharacter = Math.sqrt((bonusPlatform.x - 300) * (bonusPlatform.x - 300) + (bonusPlatform.y - 1366) * (bonusPlatform.y - 1366)); if (distanceFromCharacter < 500) { validPosition = false; } // Check collision with existing platforms for (var j = 0; j < platforms.length; j++) { var existingPlatform = platforms[j]; var dx = bonusPlatform.x - existingPlatform.x; var dy = bonusPlatform.y - existingPlatform.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 400) { validPosition = false; break; } } attempts++; } platforms.push(bonusPlatform); game.addChild(bonusPlatform); // Create arrow bonus on platform var arrowBonus = new ArrowBonus(); arrowBonus.x = bonusPlatform.x; arrowBonus.y = bonusPlatform.y - 75; // Position above platform arrowBonuses.push(arrowBonus); game.addChild(arrowBonus); } // Create star bonuses (more frequent random appearance) var currentLevel = Math.floor(LK.getScore() / 200) + 1; // 30% chance to spawn, completely random regardless of level var shouldSpawnStar = Math.random() < 0.30 && currentLevel > 1; if (shouldSpawnStar) { // Create star platform in a very difficult to reach position var starPlatform = new Platform(); var validPosition = false; var attempts = 0; while (!validPosition && attempts < 50) { // Place in very hard to reach areas - corners and edges of map var cornerChoice = Math.floor(Math.random() * 4); // Define screen boundaries and prevent stars from appearing too low var maxX = 2048 - 150; // Right boundary var maxY = 1600; // Bottom boundary - prevent stars from appearing too low var minX = 150; // Left boundary var minY = 500; // Top boundary - keep stars in middle-upper area if (cornerChoice === 0) { // Top right corner starPlatform.x = Math.max(minX, Math.min(maxX, 1400 + Math.random() * 500)); starPlatform.y = Math.max(minY, Math.min(maxY, 600 + Math.random() * 400)); } else if (cornerChoice === 1) { // Bottom right corner starPlatform.x = Math.max(minX, Math.min(maxX, 1300 + Math.random() * 600)); starPlatform.y = Math.max(minY, Math.min(maxY, 1200 + Math.random() * 400)); } else if (cornerChoice === 2) { // Top center (high up) starPlatform.x = Math.max(minX, Math.min(maxX, 800 + Math.random() * 800)); starPlatform.y = Math.max(minY, Math.min(maxY, 500 + Math.random() * 300)); } else { // Far right edge starPlatform.x = Math.max(minX, Math.min(maxX, 1600 + Math.random() * 300)); starPlatform.y = Math.max(minY, Math.min(maxY, 1000 + Math.random() * 600)); } validPosition = true; // Ensure star platforms spawn very far from character (harder to reach) var distanceFromCharacter = Math.sqrt((starPlatform.x - 300) * (starPlatform.x - 300) + (starPlatform.y - 1366) * (starPlatform.y - 1366)); if (distanceFromCharacter < 700) { validPosition = false; } // Check collision with existing platforms for (var j = 0; j < platforms.length; j++) { var existingPlatform = platforms[j]; var dx = starPlatform.x - existingPlatform.x; var dy = starPlatform.y - existingPlatform.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 350) { validPosition = false; break; } } attempts++; } platforms.push(starPlatform); game.addChild(starPlatform); // Create star on platform - make it smaller and harder to hit var star = new Star(); star.x = starPlatform.x; star.y = starPlatform.y - 75; // Position above platform // Make star smaller and harder to hit star.scaleX = 0.8; star.scaleY = 0.8; stars.push(star); game.addChild(star); } } function showGameScene() { // Remove menu scene if (menuScene) { menuScene.destroy(); menuScene = null; } // Initialize game scene gameStarted = true; // Add background image var backgroundImage = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 1, scaleY: 1 }); game.addChildAt(backgroundImage, 0); // Add to back layer // Initialize game variables arrows = []; fruits = []; platforms = []; bombs = []; arrowBonuses = []; stars = []; clouds = []; groundVegetation = []; maxArrows = 5; targetScore = 200; // Create ground vegetation (no bushes) function createGroundVegetation() { // No ground vegetation to create } // Create background clouds function createClouds() { for (var i = 0; i < 8; i++) { var cloud = LK.getAsset('cloud', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7, scaleX: 0.8 + Math.random() * 0.6, scaleY: 0.6 + Math.random() * 0.4 }); cloud.x = Math.random() * 2200; // Spread across screen width plus some overflow cloud.y = 300 + Math.random() * 600; // Position clouds in more visible area cloud.speed = 0.3 + Math.random() * 0.7; // Random slow speed clouds.push(cloud); game.addChildAt(cloud, 1); // Add above background layer // Start floating animation animateCloud(cloud); } } function animateCloud(cloud) { var floatRange = 30 + Math.random() * 40; var originalY = cloud.y; tween(cloud, { y: originalY - floatRange }, { duration: 3000 + Math.random() * 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(cloud, { y: originalY + floatRange }, { duration: 3000 + Math.random() * 2000, easing: tween.easeInOut, onFinish: function onFinish() { animateCloud(cloud); } }); } }); } // Create archer archer = new Archer(); archer.x = 300; archer.y = 1366; game.addChild(archer); // Create score display scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Create target score display targetTxt = new Text2('Target: ' + targetScore, { size: 60, fill: 0xFFFF00 }); targetTxt.anchor.set(1, 0); targetTxt.y = 120; LK.gui.topRight.addChild(targetTxt); // Create arrows remaining display arrowsTxt = new Text2('Arrows: ' + maxArrows, { size: 60, fill: 0xFFFFFF }); arrowsTxt.anchor.set(0, 0); arrowsTxt.y = 120; LK.gui.topLeft.addChild(arrowsTxt); // Initialize clouds background createClouds(); // Initialize ground vegetation createGroundVegetation(); // Play background music LK.playMusic('Fondo'); // Initialize first level spawnFruits(); } // Declare game variables in global scope var arrows = []; var fruits = []; var platforms = []; var bombs = []; var arrowBonuses = []; var stars = []; var clouds = []; var groundVegetation = []; var maxArrows = 5; var targetScore = 200; var archer = null; var scoreTxt = null; var targetTxt = null; var arrowsTxt = null; // Start with menu scene showMenuScene(); // Mouse controls var mouseX = 0; var mouseY = 0; var currentPower = 12; // Default power game.move = function (x, y, obj) { if (!gameStarted || !archer) return; mouseX = x; mouseY = y; // Update archer aiming archer.aimAt(x, y); // Calculate power based on distance from archer position var dx = x - 300; var dy = y - 1366; var distance = Math.sqrt(dx * dx + dy * dy); // Scale power based on distance (minimum 12, maximum 28) currentPower = Math.min(28, Math.max(12, distance / 25)); }; game.down = function (x, y, obj) { if (!gameStarted || !archer) return; mouseX = x; mouseY = y; // Update archer aiming archer.aimAt(x, y); // Calculate power based on distance from archer position var dx = x - 300; var dy = y - 1366; var distance = Math.sqrt(dx * dx + dy * dy); // Scale power based on distance from archer position (minimum 12, maximum 28) currentPower = Math.min(28, Math.max(12, distance / 25)); // Use archer's shoot method archer.shoot(); // Update arrows remaining display if (arrowsTxt) { arrowsTxt.setText('Arrows: ' + (maxArrows - arrows.length)); } }; game.update = function () { if (!gameStarted) return; // Check arrow-fruit collisions for (var i = arrows.length - 1; i >= 0; i--) { var arrow = arrows[i]; if (!arrow.active) continue; // Check bomb collisions first for (var j = 0; j < bombs.length; j++) { var bomb = bombs[j]; if (!bomb.hit) { // Use distance-based collision for larger bomb sprites var dx = arrow.x - bomb.x; var dy = arrow.y - bomb.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 80) { arrow.active = false; bomb.getHit(); arrow.destroy(); arrows.splice(i, 1); return; // Exit immediately as game will be over } } } // Check arrow bonus collisions for (var j = 0; j < arrowBonuses.length; j++) { var arrowBonus = arrowBonuses[j]; if (!arrowBonus.hit) { // Use distance-based collision for larger arrow bonus sprites var dx = arrow.x - arrowBonus.x; var dy = arrow.y - arrowBonus.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { arrow.active = false; arrowBonus.getHit(); arrow.destroy(); arrows.splice(i, 1); break; } } } // Check star collisions with smaller hit area (harder to hit) for (var j = 0; j < stars.length; j++) { var star = stars[j]; if (!star.hit) { // Use distance-based collision with smaller radius for stars (harder to hit) var dx = arrow.x - star.x; var dy = arrow.y - star.y; var distance = Math.sqrt(dx * dx + dy * dy); // Collision radius for star sprites (40 pixels) if (distance < 40) { arrow.active = false; star.getHit(); arrow.destroy(); arrows.splice(i, 1); break; } } } for (var j = 0; j < fruits.length; j++) { var fruit = fruits[j]; if (!fruit.hit) { // Calculate distance between arrow and fruit center for larger collision var dx = arrow.x - fruit.x; var dy = arrow.y - fruit.y; var distance = Math.sqrt(dx * dx + dy * dy); // Use collision radius for fruits (80 pixels) if (distance < 80) { arrow.active = false; fruit.getHit(); arrow.destroy(); arrows.splice(i, 1); break; } } } } // Check if out of arrows and all arrows have stopped moving if (arrows.length >= 5 && fruits.length > 0) { var allArrowsStopped = true; for (var i = 0; i < arrows.length; i++) { if (arrows[i].active && (Math.abs(arrows[i].velocityX) > 0.5 || Math.abs(arrows[i].velocityY) > 0.5)) { allArrowsStopped = false; break; } } if (allArrowsStopped) { // Game over if all 5 arrows used and fruits still remain if (fruits.length > 0) { LK.showGameOver(); } } } // Update clouds movement for (var i = 0; i < clouds.length; i++) { var cloud = clouds[i]; cloud.x += cloud.speed; // Reset cloud position when it goes off screen if (cloud.x > 2200) { cloud.x = -200; cloud.y = 300 + Math.random() * 600; // Match new cloud positioning } } // Update arrows remaining display - always show remaining out of 5 plus bonus if (!window.bonusArrows) window.bonusArrows = 0; var remainingArrows = Math.max(0, 5 - arrows.length); var totalAvailable = remainingArrows + window.bonusArrows; arrowsTxt.setText('Arrows: ' + totalAvailable); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Archer = Container.expand(function () {
var self = Container.call(this);
var bowGraphics = self.attachAsset('bow', {
anchorX: 0.5,
anchorY: 0.5
});
self.bow = bowGraphics;
self.aimAngle = 0;
self.aimAt = function (targetX, targetY) {
var dx = targetX - (self.x + self.bow.x);
var dy = targetY - (self.y + self.bow.y);
self.aimAngle = Math.atan2(dy, dx);
self.bow.rotation = self.aimAngle;
};
self.shoot = function () {
// Check if we can shoot - either normal arrows (under 5) or bonus arrows available
if (!window.bonusArrows) window.bonusArrows = 0;
var canShoot = arrows.length < 5 || window.bonusArrows > 0;
if (!canShoot) {
return; // Can't shoot
}
var arrow = new Arrow();
var bowWorldX = self.x + self.bow.x + Math.cos(self.aimAngle) * 40;
var bowWorldY = self.y + self.bow.y + Math.sin(self.aimAngle) * 40;
arrow.x = bowWorldX;
arrow.y = bowWorldY;
arrow.rotation = self.aimAngle;
var power = currentPower; // Use dynamic power based on mouse distance
arrow.velocityX = Math.cos(self.aimAngle) * power;
arrow.velocityY = Math.sin(self.aimAngle) * power;
arrows.push(arrow);
game.addChild(arrow);
// Use bonus arrow if we're over the 5 arrow limit
if (arrows.length > 5 && window.bonusArrows > 0) {
window.bonusArrows--;
}
LK.getSound('shoot').play();
};
return self;
});
var Arrow = Container.expand(function () {
var self = Container.call(this);
var arrowGraphics = self.attachAsset('arrow', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.gravity = 0.3;
self.active = true;
self.update = function () {
if (!self.active) return;
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityY += self.gravity;
// Update arrow rotation to follow movement direction
if (self.velocityX !== 0 || self.velocityY !== 0) {
self.rotation = Math.atan2(self.velocityY, self.velocityX);
}
// Check platform collisions with smaller collision area
for (var i = 0; i < platforms.length; i++) {
var platform = platforms[i];
// Calculate distance between arrow and platform center
var dx = self.x - platform.x;
var dy = self.y - platform.y;
// Reduce vertical collision sensitivity by using separate X and Y checks
var horizontalDistance = Math.abs(dx);
var verticalDistance = Math.abs(dy);
// Use collision radius for platforms (100 pixels horizontal, 60 pixels vertical)
if (horizontalDistance < 100 && verticalDistance < 60) {
self.active = false;
self.velocityX = 0;
self.velocityY = 0;
break;
}
}
// Remove arrow if it goes off screen
// Remove arrow if it goes off screen
if (self.x > 2100 || self.y > 2800 || self.x < -50 || self.y < -50) {
// Give bonus points if arrow didn't hit any platform
if (self.active) {
LK.setScore(LK.getScore() + 5);
scoreTxt.setText(LK.getScore());
}
self.destroy();
for (var i = arrows.length - 1; i >= 0; i--) {
if (arrows[i] === self) {
arrows.splice(i, 1);
break;
}
}
}
};
return self;
});
var ArrowBonus = Container.expand(function () {
var self = Container.call(this);
var bonusGraphics = self.attachAsset('arrowBonus', {
anchorX: 0.5,
anchorY: 0.5
});
self.hit = false;
self.getHit = function () {
if (self.hit) return;
self.hit = true;
// Give random 2-3 arrows
var bonusArrows = Math.floor(Math.random() * 2) + 2; // Random number between 2-3
// Store bonus arrows in global variable so archer can use them
if (!window.bonusArrows) window.bonusArrows = 0;
window.bonusArrows += bonusArrows;
// Flash effect
LK.effects.flashObject(self, 0x00ff00, 300);
// Remove from arrowBonuses array
for (var i = arrowBonuses.length - 1; i >= 0; i--) {
if (arrowBonuses[i] === self) {
arrowBonuses.splice(i, 1);
break;
}
}
// Animate bonus disappearing
tween(self, {
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
LK.getSound('arrowBonus').play();
};
return self;
});
var Bomb = Container.expand(function () {
var self = Container.call(this);
var bombGraphics = self.attachAsset('bomb', {
anchorX: 0.5,
anchorY: 0.5
});
self.hit = false;
self.getHit = function () {
if (self.hit) return;
self.hit = true;
// Flash effect
LK.effects.flashObject(self, 0xff0000, 300);
// Remove from bombs array
for (var i = bombs.length - 1; i >= 0; i--) {
if (bombs[i] === self) {
bombs.splice(i, 1);
break;
}
}
// Flash screen red and show game over
LK.effects.flashScreen(0xff0000, 1000);
LK.getSound('explosion').play();
// Animate bomb explosion
tween(self, {
alpha: 0,
scaleX: 2,
scaleY: 2
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
// Show game over after explosion animation
LK.setTimeout(function () {
LK.showGameOver();
}, 200);
}
});
};
return self;
});
var Fruit = Container.expand(function (type) {
var self = Container.call(this);
var fruitType = type || 'apple';
var fruitGraphics = self.attachAsset(fruitType, {
anchorX: 0.5,
anchorY: 0.5
});
self.fruitType = fruitType;
self.points = fruitType === 'apple' ? 10 : fruitType === 'orange' ? 15 : 20;
self.hit = false;
// Start idle floating animation
self.startIdleAnimation = function () {
var animationDuration = 1500 + Math.random() * 1000; // 1.5-2.5 seconds
var moveRange = 8 + Math.random() * 12; // 8-20 pixels movement
if (self.initialY === undefined) {
self.initialY = self.y; // Store the original position only once
}
// Find the platform this fruit is on
var platformY = self.initialY + 60; // Platform is 60 pixels below fruit
// Limit upward movement to not go above initial position too much
var upwardY = self.initialY - moveRange;
// Limit downward movement to not go below platform level
var downwardY = Math.min(self.initialY + moveRange, platformY - 50);
tween(self, {
y: upwardY
}, {
duration: animationDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.hit) {
tween(self, {
y: downwardY
}, {
duration: animationDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
if (!self.hit) {
self.startIdleAnimation();
}
}
});
}
}
});
};
self.getHit = function () {
if (self.hit) return;
self.hit = true;
LK.setScore(LK.getScore() + self.points);
scoreTxt.setText(LK.getScore());
// Flash effect
LK.effects.flashObject(self, 0xFFFFFF, 300);
// Remove from fruits array
for (var i = fruits.length - 1; i >= 0; i--) {
if (fruits[i] === self) {
fruits.splice(i, 1);
break;
}
}
// Only remove arrows when level is completed (no fruits remain)
// The arrows will be cleared when spawnFruits() is called for next level
// Create explosion/disintegration effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI * 2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second phase: shrink and fade out
tween(self, {
alpha: 0,
scaleX: 0,
scaleY: 0
}, {
duration: 200,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
}
});
}
});
LK.getSound('hit').play();
// Check win condition
if (fruits.length === 0) {
// Show level complete animation immediately
showLevelCompleteAnimation();
}
};
return self;
});
var Menu = Container.expand(function () {
var self = Container.call(this);
// Animated background layers
var menuBg1 = self.attachAsset('menuBg1', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 1
});
var menuBg2 = self.attachAsset('menuBg2', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.7
});
self.addChild(menuBg2);
// Background stars removed as requested
// Animate background layers
tween(menuBg2, {
alpha: 0.3
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(menuBg2, {
alpha: 0.7
}, {
duration: 3000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Continue background animation loop
}
});
}
});
// Animated title image
var titleImage = self.attachAsset('titleImage', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0,
rotation: 0
});
titleImage.x = 1024;
titleImage.y = 700;
// Title entrance animation with continuous effects
tween(titleImage, {
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.PI * 0.1
}, {
duration: 1000,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Continuous title animation loop
function animateTitle() {
tween(titleImage, {
scaleX: 1.1,
scaleY: 1.1,
rotation: -Math.PI * 0.05
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleImage, {
scaleX: 1.3,
scaleY: 1.3,
rotation: Math.PI * 0.05
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateTitle
});
}
});
}
animateTitle();
}
});
// Title glow effect with enhanced animation using duplicate image
var titleGlow = self.attachAsset('titleImage', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0,
alpha: 0.2,
tint: 0xFFFFFF
});
titleGlow.x = 1024;
titleGlow.y = 700;
self.addChildAt(titleGlow, self.getChildIndex(titleImage));
// Enhanced glow animation
tween(titleGlow, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 1100,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Continuous pulsing glow effect
function pulseGlow() {
tween(titleGlow, {
alpha: 0.8,
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 1800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleGlow, {
alpha: 0.2,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1800,
easing: tween.easeInOut,
onFinish: pulseGlow
});
}
});
}
pulseGlow();
}
});
// Enhanced start button with image background
var startButtonBg = self.attachAsset('startButtonBg', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1
});
startButtonBg.x = 1024;
startButtonBg.y = 1950;
self.addChild(startButtonBg);
// Start button image overlay - removed bow as requested
// Button entrance animation
LK.setTimeout(function () {
tween(startButtonBg, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 600,
easing: tween.elasticOut
});
// Bow animation removed as bow is no longer present
}, 1200);
// Button hover effect
function animateButton() {
tween(startButtonBg, {
scaleX: 1.05,
scaleY: 1.05
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(startButtonBg, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: animateButton
});
}
});
}
LK.setTimeout(animateButton, 2000);
// Start button click handler - make the whole button area clickable
startButtonBg.down = function (x, y, obj) {
// Button press animation
tween(startButtonBg, {
scaleX: 0.95,
scaleY: 0.95
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(startButtonBg, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Transition to game scene
showGameScene();
}
});
}
});
};
return self;
});
var Platform = Container.expand(function () {
var self = Container.call(this);
var platformGraphics = self.attachAsset('platform', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('starBonus', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFD700 // Golden color
});
self.hit = false;
self.getHit = function () {
if (self.hit) return;
self.hit = true;
// Clear all fruits to complete level immediately
for (var i = fruits.length - 1; i >= 0; i--) {
var fruit = fruits[i];
LK.setScore(LK.getScore() + fruit.points);
fruit.destroy();
fruits.splice(i, 1);
}
scoreTxt.setText(LK.getScore());
// Flash effect
LK.effects.flashObject(self, 0xFFD700, 300);
// Remove from stars array
for (var i = stars.length - 1; i >= 0; i--) {
if (stars[i] === self) {
stars.splice(i, 1);
break;
}
}
// Animate star disappearing
tween(self, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
onFinish: function onFinish() {
self.destroy();
}
});
LK.getSound('starBonus').play();
// Update arrows display to show bonus effect
var remainingArrows = Math.max(0, Math.min(5, maxArrows || 5) - arrows.length);
arrowsTxt.setText('Arrows: ' + remainingArrows);
// Show level complete animation immediately
showLevelCompleteAnimation();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000 // Black background as fallback
});
/****
* Game Code
****/
// Menu scene
var menuScene = null;
var gameStarted = false;
function showMenuScene() {
// Clear game elements if they exist
if (gameStarted) {
// Clear all game objects
for (var i = 0; i < arrows.length; i++) {
arrows[i].destroy();
}
arrows = [];
for (var i = 0; i < fruits.length; i++) {
fruits[i].destroy();
}
fruits = [];
for (var i = 0; i < platforms.length; i++) {
platforms[i].destroy();
}
platforms = [];
for (var i = 0; i < bombs.length; i++) {
bombs[i].destroy();
}
bombs = [];
for (var i = 0; i < arrowBonuses.length; i++) {
arrowBonuses[i].destroy();
}
arrowBonuses = [];
for (var i = 0; i < stars.length; i++) {
stars[i].destroy();
}
stars = [];
for (var i = 0; i < clouds.length; i++) {
clouds[i].destroy();
}
clouds = [];
if (archer) {
archer.destroy();
archer = null;
}
}
// Create and show menu
menuScene = new Menu();
game.addChild(menuScene);
gameStarted = false;
}
// Define showLevelCompleteAnimation in global scope
function showLevelCompleteAnimation() {
// Play level complete sound for level completion
LK.getSound('levelComplete').play();
// Create a large golden star in the center of the screen
var star = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0,
scaleY: 0,
alpha: 0,
tint: 0xFFD700 // Golden color
});
star.x = 1024; // Center of screen horizontally (2048/2)
star.y = 1366; // Center of screen vertically (2732/2)
game.addChild(star);
// Simple growing animation
tween(star, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 1
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fade out after growing
tween(star, {
alpha: 0
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
star.destroy();
// After star animation completes, proceed to next level
LK.setTimeout(function () {
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
var nextTargetScore = currentLevel * 200;
if (LK.getScore() >= targetScore) {
targetScore = nextTargetScore + 200;
targetTxt.setText('Target: ' + targetScore);
spawnFruits();
} else {
spawnFruits();
}
}, 100);
}
});
}
});
}
// Define spawnFruits function in global scope so it can be accessed from anywhere
function spawnFruits() {
// Clear existing fruits
for (var i = 0; i < fruits.length; i++) {
fruits[i].destroy();
}
fruits = [];
// Clear existing bombs
for (var i = 0; i < bombs.length; i++) {
bombs[i].destroy();
}
bombs = [];
// Clear existing arrow bonuses
for (var i = 0; i < arrowBonuses.length; i++) {
arrowBonuses[i].destroy();
}
arrowBonuses = [];
// Clear existing stars
for (var i = 0; i < stars.length; i++) {
stars[i].destroy();
}
stars = [];
// Clear all arrows (including those stuck on platforms)
for (var i = 0; i < arrows.length; i++) {
arrows[i].destroy();
}
arrows = [];
// Reset bonus arrows for the new level
window.bonusArrows = 0;
// Ensure enough arrows for the number of fruits (minimum 3, but at least equal to fruits)
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
var baseArrows = Math.max(3, 6 - Math.floor(currentLevel / 2));
var calculatedArrows = Math.max(baseArrows, numFruits + 1);
maxArrows = Math.min(5, calculatedArrows); // Always have at least 1 more arrow than fruits, but max 5
// Clear existing platforms
for (var i = 0; i < platforms.length; i++) {
platforms[i].destroy();
}
platforms = [];
var fruitTypes = ['apple', 'orange', 'banana'];
// Calculate level based on score progression
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
// Increase number of fruits with each level (start with 2, add 1 every 2 levels, max 6)
var numFruits = Math.min(6, 2 + Math.floor(currentLevel / 2));
for (var i = 0; i < numFruits; i++) {
// Create platform with collision detection
var platform = new Platform();
var validPosition = false;
var attempts = 0;
while (!validPosition && attempts < 50) {
// Make platforms more spread out and harder to reach in higher levels
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
var spreadFactor = Math.min(2, 1 + currentLevel * 0.2);
// Ensure platforms stay within screen boundaries and don't appear too low
var maxX = 2048 - 150; // Right boundary
var maxY = 1800; // Bottom boundary - prevent platforms from appearing too low
var minX = 150; // Left boundary
var minY = 600; // Top boundary - keep platforms in middle area
platform.x = Math.max(minX, Math.min(maxX, 600 + Math.random() * (800 * spreadFactor)));
platform.y = Math.max(minY, Math.min(maxY, 800 + Math.random() * (1000 * spreadFactor)));
validPosition = true;
// Check distance from character to ensure platforms spawn far away
var distanceFromCharacter = Math.sqrt((platform.x - 300) * (platform.x - 300) + (platform.y - 1366) * (platform.y - 1366));
if (distanceFromCharacter < 500) {
validPosition = false;
}
// Check collision with existing platforms
for (var j = 0; j < platforms.length; j++) {
var existingPlatform = platforms[j];
var dx = platform.x - existingPlatform.x;
var dy = platform.y - existingPlatform.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 400) {
// Minimum distance between platforms
validPosition = false;
break;
}
}
attempts++;
}
platforms.push(platform);
game.addChild(platform);
// Create fruit on platform
var fruitType = fruitTypes[Math.floor(Math.random() * fruitTypes.length)];
var fruit = new Fruit(fruitType);
fruit.x = platform.x;
fruit.y = platform.y - 80; // Position above platform
fruits.push(fruit);
game.addChild(fruit);
// Start idle animation immediately for all fruits
fruit.startIdleAnimation();
}
// Create separate platforms for bombs
// Calculate level based on score progression (every 200 points is roughly a level)
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
// Increase bomb count with each level, starting with 1 every 2 levels, maximum 3
var numBombs = Math.min(3, Math.floor(currentLevel / 2));
for (var i = 0; i < numBombs; i++) {
// Create bomb platform with collision detection
var bombPlatform = new Platform();
var validPosition = false;
var attempts = 0;
while (!validPosition && attempts < 50) {
// Make bomb platforms more spread out and harder to avoid in higher levels
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
var spreadFactor = Math.min(2, 1 + currentLevel * 0.2);
// Ensure bomb platforms stay within screen boundaries and don't appear too low
var maxX = 2048 - 150; // Right boundary
var maxY = 1800; // Bottom boundary - prevent platforms from appearing too low
var minX = 150; // Left boundary
var minY = 600; // Top boundary - keep platforms in middle area
bombPlatform.x = Math.max(minX, Math.min(maxX, 600 + Math.random() * (800 * spreadFactor)));
bombPlatform.y = Math.max(minY, Math.min(maxY, 800 + Math.random() * (1000 * spreadFactor)));
validPosition = true;
// Check distance from character to ensure bomb platforms spawn far away
var distanceFromCharacter = Math.sqrt((bombPlatform.x - 300) * (bombPlatform.x - 300) + (bombPlatform.y - 1366) * (bombPlatform.y - 1366));
if (distanceFromCharacter < 500) {
validPosition = false;
}
// Check collision with existing platforms (both fruit and bomb platforms)
for (var j = 0; j < platforms.length; j++) {
var existingPlatform = platforms[j];
var dx = bombPlatform.x - existingPlatform.x;
var dy = bombPlatform.y - existingPlatform.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 400) {
// Minimum distance between platforms
validPosition = false;
break;
}
}
attempts++;
}
platforms.push(bombPlatform);
game.addChild(bombPlatform);
// Create bomb on its own platform
var bomb = new Bomb();
bomb.x = bombPlatform.x;
bomb.y = bombPlatform.y - 75; // Position above platform
bombs.push(bomb);
game.addChild(bomb);
}
// Create arrow bonuses (only sometimes, based on level)
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
// 60% chance to spawn arrow bonus, but only every 2-3 levels
var shouldSpawnBonus = Math.random() < 0.6 && currentLevel > 1 && (currentLevel % 2 === 0 || currentLevel % 3 === 0);
if (shouldSpawnBonus) {
// Create arrow bonus platform with collision detection
var bonusPlatform = new Platform();
var validPosition = false;
var attempts = 0;
while (!validPosition && attempts < 50) {
// Ensure bonus platforms stay within screen boundaries and don't appear too low
var maxX = 2048 - 150; // Right boundary
var maxY = 1800; // Bottom boundary - prevent platforms from appearing too low
var minX = 150; // Left boundary
var minY = 600; // Top boundary - keep platforms in middle area
bonusPlatform.x = Math.max(minX, Math.min(maxX, 600 + Math.random() * 800));
bonusPlatform.y = Math.max(minY, Math.min(maxY, 800 + Math.random() * 1200));
validPosition = true;
// Check distance from character to ensure bonus platforms spawn far away
var distanceFromCharacter = Math.sqrt((bonusPlatform.x - 300) * (bonusPlatform.x - 300) + (bonusPlatform.y - 1366) * (bonusPlatform.y - 1366));
if (distanceFromCharacter < 500) {
validPosition = false;
}
// Check collision with existing platforms
for (var j = 0; j < platforms.length; j++) {
var existingPlatform = platforms[j];
var dx = bonusPlatform.x - existingPlatform.x;
var dy = bonusPlatform.y - existingPlatform.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 400) {
validPosition = false;
break;
}
}
attempts++;
}
platforms.push(bonusPlatform);
game.addChild(bonusPlatform);
// Create arrow bonus on platform
var arrowBonus = new ArrowBonus();
arrowBonus.x = bonusPlatform.x;
arrowBonus.y = bonusPlatform.y - 75; // Position above platform
arrowBonuses.push(arrowBonus);
game.addChild(arrowBonus);
}
// Create star bonuses (more frequent random appearance)
var currentLevel = Math.floor(LK.getScore() / 200) + 1;
// 30% chance to spawn, completely random regardless of level
var shouldSpawnStar = Math.random() < 0.30 && currentLevel > 1;
if (shouldSpawnStar) {
// Create star platform in a very difficult to reach position
var starPlatform = new Platform();
var validPosition = false;
var attempts = 0;
while (!validPosition && attempts < 50) {
// Place in very hard to reach areas - corners and edges of map
var cornerChoice = Math.floor(Math.random() * 4);
// Define screen boundaries and prevent stars from appearing too low
var maxX = 2048 - 150; // Right boundary
var maxY = 1600; // Bottom boundary - prevent stars from appearing too low
var minX = 150; // Left boundary
var minY = 500; // Top boundary - keep stars in middle-upper area
if (cornerChoice === 0) {
// Top right corner
starPlatform.x = Math.max(minX, Math.min(maxX, 1400 + Math.random() * 500));
starPlatform.y = Math.max(minY, Math.min(maxY, 600 + Math.random() * 400));
} else if (cornerChoice === 1) {
// Bottom right corner
starPlatform.x = Math.max(minX, Math.min(maxX, 1300 + Math.random() * 600));
starPlatform.y = Math.max(minY, Math.min(maxY, 1200 + Math.random() * 400));
} else if (cornerChoice === 2) {
// Top center (high up)
starPlatform.x = Math.max(minX, Math.min(maxX, 800 + Math.random() * 800));
starPlatform.y = Math.max(minY, Math.min(maxY, 500 + Math.random() * 300));
} else {
// Far right edge
starPlatform.x = Math.max(minX, Math.min(maxX, 1600 + Math.random() * 300));
starPlatform.y = Math.max(minY, Math.min(maxY, 1000 + Math.random() * 600));
}
validPosition = true;
// Ensure star platforms spawn very far from character (harder to reach)
var distanceFromCharacter = Math.sqrt((starPlatform.x - 300) * (starPlatform.x - 300) + (starPlatform.y - 1366) * (starPlatform.y - 1366));
if (distanceFromCharacter < 700) {
validPosition = false;
}
// Check collision with existing platforms
for (var j = 0; j < platforms.length; j++) {
var existingPlatform = platforms[j];
var dx = starPlatform.x - existingPlatform.x;
var dy = starPlatform.y - existingPlatform.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 350) {
validPosition = false;
break;
}
}
attempts++;
}
platforms.push(starPlatform);
game.addChild(starPlatform);
// Create star on platform - make it smaller and harder to hit
var star = new Star();
star.x = starPlatform.x;
star.y = starPlatform.y - 75; // Position above platform
// Make star smaller and harder to hit
star.scaleX = 0.8;
star.scaleY = 0.8;
stars.push(star);
game.addChild(star);
}
}
function showGameScene() {
// Remove menu scene
if (menuScene) {
menuScene.destroy();
menuScene = null;
}
// Initialize game scene
gameStarted = true;
// Add background image
var backgroundImage = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
});
game.addChildAt(backgroundImage, 0); // Add to back layer
// Initialize game variables
arrows = [];
fruits = [];
platforms = [];
bombs = [];
arrowBonuses = [];
stars = [];
clouds = [];
groundVegetation = [];
maxArrows = 5;
targetScore = 200;
// Create ground vegetation (no bushes)
function createGroundVegetation() {
// No ground vegetation to create
}
// Create background clouds
function createClouds() {
for (var i = 0; i < 8; i++) {
var cloud = LK.getAsset('cloud', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7,
scaleX: 0.8 + Math.random() * 0.6,
scaleY: 0.6 + Math.random() * 0.4
});
cloud.x = Math.random() * 2200; // Spread across screen width plus some overflow
cloud.y = 300 + Math.random() * 600; // Position clouds in more visible area
cloud.speed = 0.3 + Math.random() * 0.7; // Random slow speed
clouds.push(cloud);
game.addChildAt(cloud, 1); // Add above background layer
// Start floating animation
animateCloud(cloud);
}
}
function animateCloud(cloud) {
var floatRange = 30 + Math.random() * 40;
var originalY = cloud.y;
tween(cloud, {
y: originalY - floatRange
}, {
duration: 3000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(cloud, {
y: originalY + floatRange
}, {
duration: 3000 + Math.random() * 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
animateCloud(cloud);
}
});
}
});
}
// Create archer
archer = new Archer();
archer.x = 300;
archer.y = 1366;
game.addChild(archer);
// Create score display
scoreTxt = new Text2('0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create target score display
targetTxt = new Text2('Target: ' + targetScore, {
size: 60,
fill: 0xFFFF00
});
targetTxt.anchor.set(1, 0);
targetTxt.y = 120;
LK.gui.topRight.addChild(targetTxt);
// Create arrows remaining display
arrowsTxt = new Text2('Arrows: ' + maxArrows, {
size: 60,
fill: 0xFFFFFF
});
arrowsTxt.anchor.set(0, 0);
arrowsTxt.y = 120;
LK.gui.topLeft.addChild(arrowsTxt);
// Initialize clouds background
createClouds();
// Initialize ground vegetation
createGroundVegetation();
// Play background music
LK.playMusic('Fondo');
// Initialize first level
spawnFruits();
}
// Declare game variables in global scope
var arrows = [];
var fruits = [];
var platforms = [];
var bombs = [];
var arrowBonuses = [];
var stars = [];
var clouds = [];
var groundVegetation = [];
var maxArrows = 5;
var targetScore = 200;
var archer = null;
var scoreTxt = null;
var targetTxt = null;
var arrowsTxt = null;
// Start with menu scene
showMenuScene();
// Mouse controls
var mouseX = 0;
var mouseY = 0;
var currentPower = 12; // Default power
game.move = function (x, y, obj) {
if (!gameStarted || !archer) return;
mouseX = x;
mouseY = y;
// Update archer aiming
archer.aimAt(x, y);
// Calculate power based on distance from archer position
var dx = x - 300;
var dy = y - 1366;
var distance = Math.sqrt(dx * dx + dy * dy);
// Scale power based on distance (minimum 12, maximum 28)
currentPower = Math.min(28, Math.max(12, distance / 25));
};
game.down = function (x, y, obj) {
if (!gameStarted || !archer) return;
mouseX = x;
mouseY = y;
// Update archer aiming
archer.aimAt(x, y);
// Calculate power based on distance from archer position
var dx = x - 300;
var dy = y - 1366;
var distance = Math.sqrt(dx * dx + dy * dy);
// Scale power based on distance from archer position (minimum 12, maximum 28)
currentPower = Math.min(28, Math.max(12, distance / 25));
// Use archer's shoot method
archer.shoot();
// Update arrows remaining display
if (arrowsTxt) {
arrowsTxt.setText('Arrows: ' + (maxArrows - arrows.length));
}
};
game.update = function () {
if (!gameStarted) return;
// Check arrow-fruit collisions
for (var i = arrows.length - 1; i >= 0; i--) {
var arrow = arrows[i];
if (!arrow.active) continue;
// Check bomb collisions first
for (var j = 0; j < bombs.length; j++) {
var bomb = bombs[j];
if (!bomb.hit) {
// Use distance-based collision for larger bomb sprites
var dx = arrow.x - bomb.x;
var dy = arrow.y - bomb.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 80) {
arrow.active = false;
bomb.getHit();
arrow.destroy();
arrows.splice(i, 1);
return; // Exit immediately as game will be over
}
}
}
// Check arrow bonus collisions
for (var j = 0; j < arrowBonuses.length; j++) {
var arrowBonus = arrowBonuses[j];
if (!arrowBonus.hit) {
// Use distance-based collision for larger arrow bonus sprites
var dx = arrow.x - arrowBonus.x;
var dy = arrow.y - arrowBonus.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 70) {
arrow.active = false;
arrowBonus.getHit();
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
}
// Check star collisions with smaller hit area (harder to hit)
for (var j = 0; j < stars.length; j++) {
var star = stars[j];
if (!star.hit) {
// Use distance-based collision with smaller radius for stars (harder to hit)
var dx = arrow.x - star.x;
var dy = arrow.y - star.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Collision radius for star sprites (40 pixels)
if (distance < 40) {
arrow.active = false;
star.getHit();
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
}
for (var j = 0; j < fruits.length; j++) {
var fruit = fruits[j];
if (!fruit.hit) {
// Calculate distance between arrow and fruit center for larger collision
var dx = arrow.x - fruit.x;
var dy = arrow.y - fruit.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Use collision radius for fruits (80 pixels)
if (distance < 80) {
arrow.active = false;
fruit.getHit();
arrow.destroy();
arrows.splice(i, 1);
break;
}
}
}
}
// Check if out of arrows and all arrows have stopped moving
if (arrows.length >= 5 && fruits.length > 0) {
var allArrowsStopped = true;
for (var i = 0; i < arrows.length; i++) {
if (arrows[i].active && (Math.abs(arrows[i].velocityX) > 0.5 || Math.abs(arrows[i].velocityY) > 0.5)) {
allArrowsStopped = false;
break;
}
}
if (allArrowsStopped) {
// Game over if all 5 arrows used and fruits still remain
if (fruits.length > 0) {
LK.showGameOver();
}
}
}
// Update clouds movement
for (var i = 0; i < clouds.length; i++) {
var cloud = clouds[i];
cloud.x += cloud.speed;
// Reset cloud position when it goes off screen
if (cloud.x > 2200) {
cloud.x = -200;
cloud.y = 300 + Math.random() * 600; // Match new cloud positioning
}
}
// Update arrows remaining display - always show remaining out of 5 plus bonus
if (!window.bonusArrows) window.bonusArrows = 0;
var remainingArrows = Math.max(0, 5 - arrows.length);
var totalAvailable = remainingArrows + window.bonusArrows;
arrowsTxt.setText('Arrows: ' + totalAvailable);
};
Estrella animada. In-Game asset. 2d. High contrast. No shadows
Estrella. In-Game asset. 2d. High contrast. No shadows
Nube. In-Game asset. 2d. High contrast. No shadows
Bush. In-Game asset. 2d. High contrast. No shadows
tree. In-Game asset. 2d. High contrast. No shadows
Quita los arboles y las nubes
Boton de start. In-Game asset. 2d. High contrast. No shadows
Fondo para un juego de fruit archer. In-Game asset. 2d. High contrast. No shadows
Fruit Archer titulo. In-Game asset. 2d. High contrast. No shadows