User prompt
ahora quisiera que los objetos que no podemos tocar aparezcan cada vez mas si seguimos pasando de nivel, maximo 5 objetos
User prompt
Que las flechas se borren de la infraestructura que se choco solo cuando hallamos pasado el nivel
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'active')' in or related to this line: 'if (!arrow.active) {' Line Number: 381
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'active')' in or related to this line: 'if (!arrow.active) {' Line Number: 381
User prompt
Pero al atinarle a las frutas, las flechas que se quedan en la infrastructura deberian borrarse
User prompt
Elimina la linea roja
User prompt
Quiero que esa linea roja nos indique hacia donde ira la flecha y que forma tomara ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Que esa linea tambien se doble, que se mueva, agranda o doble dependiendo de que forma tendra la flecha cuando se dispara ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Pero que lo que no podemos tocar tenga su propia plataforma
User prompt
Que sean 5 flechas y la persona este en el medio al lado izquierdo
User prompt
Ahora quisiera que pongas otro objeto que si la flecha le da, pierde el nivel
User prompt
Un poco mas de fuerza y que haya como una linea indicandonos la direccion que hara la flecha
User prompt
Un poco menos de fuerza para la flecha
User prompt
Quiero que la felcha no traspase la infraestructura donde esta la fruta
User prompt
mas potencia al disparo de la flecha y que las infraestructuras no choquen entre si
User prompt
Que el personaje y la infrestructura tengan la distancia adecuada para que la felcha les pueda dar
User prompt
Quiero que si el mouse esta mas lejos o mas cerca del personaje sea la potencia del disparo de la felcha
Code edit (1 edits merged)
Please save this source code
User prompt
Fruit Archer - Bow and Arrow Shooting Game
Initial prompt
Quisiera que una persona con arco y flecha este al lado izquierdo y que con el mouse podamos decidir la direccion donde disparara la felcha hacia infraestructuras con frutas donde la flecha le debe dar a las frutas.
/**** * 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