User prompt
Let boss 3 require 45 slashes to die
User prompt
hide leaderboard button
User prompt
make boss 4 require 50 slashes to be killed
User prompt
make boss 5 require 65 slashes to be killed
User prompt
make boss 5 require 65 slashes to be killed
User prompt
- reduce the delay between spawning fruits in level 5
User prompt
- reduce the delay between spawning fruits in level 4
User prompt
- add 4 more lifefruits to level 4 - add 6 more lifefruits to level 5
User prompt
- Flash level 5 boss 90% into level 5 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
- Flash level 4 boss 90% into level 4
User prompt
Flash level 4 boss 90% into level 4
User prompt
Modify boss activation for level 5 to appear after 50 slashes instead of
User prompt
Modify boss activation for level 4 to appear after 50 slashes instead of 5
User prompt
Modify boss activation for level 4 to appear after 40 slashes instead of 5
User prompt
Change the mazimum number of lifefruits in level 5 from to 14
User prompt
Reduce maximum number of lifefruits in level 3 from to 10
User prompt
Increase by 3 the maximum number of lifefruits in level 2
User prompt
Reduce by 3 the maximum number of lifefruits in level 3
User prompt
Make combo text visible for 3s
User prompt
Reduce by 3 the maximum number of lifefruits in level 2
User prompt
Reduce by 3 the number of lifefruits in level 2
User prompt
Make boss 1 die after 21 hits
User prompt
Make boss 1 die after 15 hits
User prompt
Let boss 3 die after 50 hits. Let boss 4 die after 45 hits
User prompt
Loading Let the combo text be visible for 2s
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Blade = Container.expand(function () { var self = Container.call(this); self.active = false; self.points = []; self.maxPoints = 40; // Increased from 30 to 40 to track more points for even smoother detection self.lastUpdateTime = 0; // Track time between updates for velocity calculation self.trail = []; for (var i = 0; i < self.maxPoints; i++) { var trailPart = self.attachAsset('blade', { anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.trail.push(trailPart); } self.update = function () { // Update trail visuals for (var i = 0; i < self.trail.length; i++) { if (i < self.points.length) { var point = self.points[i]; var trailPart = self.trail[i]; trailPart.x = point.x; trailPart.y = point.y; trailPart.alpha = 0; // Keep blade invisible if (i > 0) { var prevPoint = self.points[i - 1]; var angle = Math.atan2(point.y - prevPoint.y, point.x - prevPoint.x); trailPart.rotation = angle; } } else { self.trail[i].alpha = 0; } } }; self.addPoint = function (x, y) { var currentTime = Date.now(); var velocity = 0; var angle = 0; // Calculate velocity and angle if we have previous points if (self.points.length > 0) { var prevPoint = self.points[0]; var dx = x - prevPoint.x; var dy = y - prevPoint.y; var timeDiff = currentTime - self.lastUpdateTime; // Prevent division by zero if (timeDiff > 0) { var distance = Math.sqrt(dx * dx + dy * dy); velocity = distance / timeDiff * 1000; // pixels per second } angle = Math.atan2(dy, dx); } self.lastUpdateTime = currentTime; self.points.unshift({ x: x, y: y, time: currentTime, velocity: velocity, angle: angle }); if (self.points.length > self.maxPoints) { self.points.pop(); } }; self.reset = function () { self.points = []; self.active = false; for (var i = 0; i < self.trail.length; i++) { self.trail[i].alpha = 0; } }; return self; }); var BlitzPowerup = Container.expand(function () { var self = Container.call(this); self.type = 'blitz'; self.sliced = false; self.width = 300; self.height = 300; self.points = 0; self.baseSpeed = 1.5; // Create a red lightning bolt-shaped fruit for blitz var blitzGraphics = self.attachAsset('blitz', { anchorX: 0.5, anchorY: 0.5 }); // No color tint needed as we're using the blitz image asset // No shape needed as we're using the blitz image asset self.vx = 0; self.vy = 0; self.gravity = 0.01; self.rotationSpeed = (Math.random() - 0.5) * 0.015; self.init = function (x, y, direction) { self.x = x; self.y = y; var angle = direction * (Math.PI / 4) + Math.random() * Math.PI / 8 - Math.PI / 16; var speed = self.baseSpeed + Math.random() * 0.8; self.vx = Math.cos(angle) * speed * 1.15; self.vy = -Math.sin(angle) * speed - 8; }; self.slice = function () { if (self.sliced) { return; } self.sliced = true; // Play Blitzkrieg sound LK.getSound('Blitzkrieg').play(); // Display "Blitzkrieg!" text in the middle of screen with large text that blinks red and yellow var blitzText = new Text2('BLITZKRIEG!', { size: 180, // Larger size fill: 0xFF0000, // Start with red weight: 'bold' }); blitzText.anchor.set(0.5, 0.5); blitzText.x = GAME_WIDTH / 2; blitzText.y = GAME_HEIGHT / 2; game.addChild(blitzText); // Make the text shake and blink red/yellow for 1 second var shakeCount = 0; var maxShakes = 20; // More shakes for more intensity var shakeIntensity = 25; // Higher intensity shake var originalX = blitzText.x; var originalY = blitzText.y; var colorToggle = false; // Shake and blink for 1 second (50ms * 20 = 1000ms) var shakeInterval = LK.setInterval(function () { if (shakeCount >= maxShakes) { LK.clearInterval(shakeInterval); blitzText.x = originalX; blitzText.y = originalY; // After 1 second of shaking, play blitzsquash LK.getSound('blitzsquash').play(); // Then continue with the rest of the effect // Flash effect tween(blitzGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // No need to reset tint as we're not using tint } }); // Fade out the text tween(blitzText, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { blitzText.destroy(); } }); // Convert all fruits to explosion assets first, then after 1 second, destroy them for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; if (fruit.type !== 'lifefruit' && fruit.type !== 'bullettime' && fruit.type !== 'blitz') { // Skip if already converted if (fruit.converted) { continue; } fruit.converted = true; // Get the current fruit graphic var fruitGraphics = fruit.children[0]; // Replace with explosion asset var explosionGraphic = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5 }); explosionGraphic.x = fruitGraphics.x; explosionGraphic.y = fruitGraphics.y; explosionGraphic.scale.set(fruitGraphics.scale.x, fruitGraphics.scale.y); fruit.removeChild(fruitGraphics); fruit.addChild(explosionGraphic); // Add glow effect tween(explosionGraphic, { scaleX: explosionGraphic.scale.x * 1.2, scaleY: explosionGraphic.scale.y * 1.2 }, { duration: 500, easing: tween.easeOut }); } } // Shake the screen effect var gameOriginalX = game.x; var gameOriginalY = game.y; var gameShakeCount = 0; var gameMaxShakes = 10; var gameShakeIntensity = 20; var gameShakeInterval = LK.setInterval(function () { if (gameShakeCount >= gameMaxShakes) { LK.clearInterval(gameShakeInterval); game.x = gameOriginalX; game.y = gameOriginalY; // After 1 second and shake is done, destroy all fruits LK.setTimeout(function () { // Destroy all fruits on screen (including bombs) for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; if (fruit.type !== 'lifefruit' && fruit.type !== 'bullettime' && fruit.type !== 'blitz') { // Add points for each destroyed fruit if (fruit.type !== 'bomb') { LK.setScore(LK.getScore() + fruit.points); } // Animate explosion effect var explosionGraphic = fruit.children[0]; tween(explosionGraphic, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut }); // Remove fruit fruit.destroy(); fruits.splice(i, 1); } } // Update score display scoreTxt.setText(LK.getScore()); }, 1000); // Wait 1 second before clearing return; } // Apply random shake offset game.x = gameOriginalX + (Math.random() - 0.5) * gameShakeIntensity; game.y = gameOriginalY + (Math.random() - 0.5) * gameShakeIntensity; gameShakeCount++; }, 50); return; } // Toggle text color between red and yellow for blinking effect colorToggle = !colorToggle; // Direct property access for Text2 objects instead of using style blitzText.fill = colorToggle ? 0xFFFF00 : 0xFF0000; // Apply random shake offset with higher intensity blitzText.x = originalX + (Math.random() - 0.5) * shakeIntensity * 2; blitzText.y = originalY + (Math.random() - 0.5) * shakeIntensity * 1.5; shakeCount++; }, 50); // Slower interval for more visible blinks }; self.down = function (x, y, obj) { if (!self.sliced) { self.slice(); // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; } }; self.update = function () { // Don't update movement if gameActive is false (leaderboard is visible) if (!gameActive) { return; } if (!self.sliced) { // Update whole fruit self.vy += self.gravity; self.x += self.vx; self.y += self.vy; self.rotation += self.rotationSpeed; // Bounce off walls if not sliced if (self.x < self.width / 2 && self.vx < 0) { self.vx = -self.vx * 0.8; self.x = self.width / 2; } if (self.x > GAME_WIDTH - self.width / 2 && self.vx > 0) { self.vx = -self.vx * 0.8; self.x = GAME_WIDTH - self.width / 2; } } }; self.isOffScreen = function () { return self.y > 2732 + self.height || self.x > GAME_WIDTH + self.width; }; return self; }); var Boss = Container.expand(function (level) { var self = Container.call(this); // Boss properties self.level = level || 1; self.health = 5 + self.level; // Health increases with level self.maxHealth = self.health; self.width = 800; self.height = 800; self.active = false; self.attackTimer = 0; self.attackInterval = self.level === 5 ? 950 : self.level === 4 ? 600 : self.level === 1 ? 5000 : self.level === 2 ? 2000 : 3000 - self.level * 250; // Level 5 boss spawn speed at 950ms (increased from 833ms), Level 4 boss at 600ms, Level 1 boss at 5000ms (increased from 3000ms), Level 2 boss at 2000ms // Ensure level is within valid range (1-5) if (self.level < 1) { self.level = 1; } if (self.level > 5) { self.level = 5; } // Determine boss type based on level var bossType = BOSS_FRUIT_TYPES[self.level - 1] || 'strawberry'; // Create boss visual var bossGraphic = self.attachAsset(bossType, { anchorX: 0.5, anchorY: 0.5, scaleX: self.level === 1 ? 1.95 : self.level === 2 ? 2.8 : self.level === 5 ? 3.5 : 4, // Make level 1 boss smaller by 35%, level 2 boss smaller by 30%, level 5 boss 30% smaller (from 5 to 3.5) scaleY: self.level === 1 ? 1.95 : self.level === 2 ? 2.8 : self.level === 5 ? 3.5 : 4 // Make level 1 boss smaller by 35%, level 2 boss smaller by 30%, level 5 boss 30% smaller (from 5 to 3.5) }); // Health bar background var healthBarBg = new Container(); healthBarBg.x = -200; healthBarBg.y = -450; var healthBarBack = LK.getAsset('blade', { anchorX: 0, anchorY: 0.5, width: 400, height: 30 }); healthBarBack.tint = 0x333333; // Health bar fill var healthBarFill = LK.getAsset('blade', { anchorX: 0, anchorY: 0.5, width: 400, height: 30 }); healthBarFill.tint = 0xFF0000; healthBarBg.addChild(healthBarBack); healthBarBg.addChild(healthBarFill); self.addChild(healthBarBg); self.healthBar = healthBarFill; // Boss movement self.vx = self.level === 5 ? 8 : 2 + self.level * 0.5; // Level 5 boss moves much faster self.targetX = GAME_WIDTH / 2; self.activate = function () { self.active = true; self.health = self.maxHealth; self.x = GAME_WIDTH / 2; // Position boss below the top boundary (320px) with enough space to not overlap // Boss dimensions depend on the asset scale (4x) and boss type // Account for boss height and health bar to ensure it appears fully below the boundary // Position bosses at different heights based on level to avoid overlapping the top boundary if (self.level === 2) { self.y = 320 + 570; // Level 2 boss: 60px lower } else if (self.level === 3) { self.y = 320 + 690; // Level 3 boss: 170px lower (moved additional 30px lower) } else if (self.level === 4) { self.y = 320 + 750; // Level 4 boss: 200px lower } else if (self.level === 5) { // For level 5 boss, require 65 slashes to defeat self.health -= self.maxHealth / 65; self.y = 320 + 900; // Level 5 boss: 300px lower (moved from 600 to 900) } else { self.y = 320 + 570; // Default position for level 1 boss } self.updateHealthBar(); }; self.updateHealthBar = function () { self.healthBar.width = self.health / self.maxHealth * 400; }; self.hit = function () { if (!self.active) { return; } if (self.lastHitTime && Date.now() - self.lastHitTime < 100) { return false; } self.lastHitTime = Date.now(); // For level 1 boss, require 15 slashes to defeat if (self.level === 1) { // Decrement by 1/21 of health for level 1 boss self.health -= self.maxHealth / 21; } else if (self.level === 2) { // For level 2 boss, require 35 slashes to defeat self.health -= self.maxHealth / 35; } else if (self.level === 3) { // For level 3 boss, require 45 slashes to defeat self.health -= self.maxHealth / 45; } else if (self.level === 4) { // For level 4 boss, require 50 slashes to defeat self.health -= self.maxHealth / 50; } else if (self.level === 5) { // For level 5 boss, require 65 slashes to defeat self.health -= self.maxHealth / 65; } else { // Normal behavior for other bosses self.health--; } self.updateHealthBar(); // Play owww sound when boss is hit LK.getSound('owww').play(); // Flash the boss red bossGraphic.tint = 0xFF0000; LK.setTimeout(function () { bossGraphic.tint = 0xFFFFFF; }, 200); // Check if boss is defeated if (self.health <= 0) { self.defeat(); return true; } return false; }; self.defeat = function () { self.active = false; // Make the health bar transparent healthBarBg.alpha = 0; // Create explosion effect bossGraphic.tint = 0xFFFFFF; tween(bossGraphic, { alpha: 0, scaleX: 3, scaleY: 3, rotation: Math.PI * 2 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Add bonus points (increased for higher level bosses) var bonus = 25 * self.level; LK.setScore(LK.getScore() + bonus); scoreTxt.setText(LK.getScore()); LK.effects.flashScreen(0x00FF00, 500); } }); // Play explosion sound LK.getSound('explosion').play(); // Play nooo sound when boss is killed LK.getSound('nooo').play(); }; self.throwFruit = function () { if (!self.active) { return; } // Launch multiple bombs at the player var bombCount = self.level === 5 ? Math.floor(Math.random() * 3) + 2 : // Level 5: 2-4 bombs (reduced from 3-6) self.level === 4 ? Math.floor(Math.random() * 2) + 1 : // Level 4: 1-2 bombs (reduced from 1-4) Math.floor(Math.random() * self.level) + 1; // Other levels: 1-level bombs // For level 5, also spawn random fruits alongside bombs // Also spawn random fruits with level 1 and level 2 bosses var fruitCount = self.level === 5 || self.level === 1 || self.level === 2 ? Math.floor(Math.random() * 2) : 0; // Reduced from 1-2 to 0-1 // Spawn bombs for (var i = 0; i < bombCount; i++) { var fruit = new Fruit('bomb'); // Generate random position on the boss perimeter var spawnAngle = Math.random() * Math.PI * 2; // Full 360 degrees var spawnRadius = self.width / 3; // Radius to spawn from (edge of boss) // Position the bomb on the perimeter fruit.x = self.x + Math.cos(spawnAngle) * spawnRadius; fruit.y = self.y + Math.sin(spawnAngle) * spawnRadius; // Aim outward from the spawn point with spread var angle = spawnAngle + Math.PI + (Math.random() - 0.5); // Outward direction with randomness var speed = self.level === 5 ? 5 + Math.random() * 3 : // Level 5: Much faster bombs 3 + Math.random() * 2 + self.level * 0.5; // Other levels: Normal speed fruit.vx = Math.cos(angle) * speed; fruit.vy = Math.sin(angle) * speed; game.addChild(fruit); fruits.push(fruit); } // For level 5, level 1, level 2, or level 3, also spawn random fruits if (self.level === 5 || self.level === 1 || self.level === 2 || self.level === 3) { // Level 2 boss spawns fewer fruits (1-2 instead of 2-3) var fruitCount = self.level === 2 ? Math.floor(Math.random() * 2) + 1 : self.level === 3 ? Math.floor(Math.random() * 3) + 2 : // Level 3 boss: 2-4 fruits self.level === 5 || self.level === 1 ? Math.floor(Math.random() * 2) : 0; for (var j = 0; j < fruitCount; j++) { // Select a random fruit type // For level 1, only use level 1 fruits (strawberry) // For level 2, use level 2 fruits (strawberry, kiwi) // For level 3, use level 3 fruits (strawberry, kiwi, orange) // For level 4, use level 4 fruits (strawberry, kiwi, orange, apple) var availableFruits; if (self.level === 1) { availableFruits = ['strawberry']; // Level 1 fruits } else if (self.level === 2) { availableFruits = ['strawberry', 'kiwi']; // Level 2 fruits } else if (self.level === 3) { availableFruits = ['strawberry', 'kiwi', 'orange']; // Level 3 fruits } else if (self.level === 4) { availableFruits = ['strawberry', 'kiwi', 'orange', 'apple']; // Level 4 fruits // Spawn more fruits for level 4 boss fruitCount = Math.floor(Math.random() * 6) + 10; // Level 4 boss: 10-15 fruits (increased from 6-10) // Ensure we spawn level 4 fruits // Make the boss spawn each fruit type at least once, then random selections var guaranteedFruits = ['strawberry', 'kiwi', 'orange', 'apple']; for (var j = 0; j < fruitCount; j++) { var fruitType; if (j < guaranteedFruits.length) { // Guarantee each fruit type appears at least once fruitType = guaranteedFruits[j]; } else { // Then pick random fruits fruitType = availableFruits[Math.floor(Math.random() * availableFruits.length)]; } var randomFruit = new Fruit(fruitType); // Generate random position on the boss perimeter var fruitAngle = Math.random() * Math.PI * 2; var fruitRadius = self.width / 3; // Position the fruit on the perimeter randomFruit.x = self.x + Math.cos(fruitAngle) * fruitRadius; randomFruit.y = self.y + Math.sin(fruitAngle) * fruitRadius; // Aim outward with different trajectory var fruitDirection = fruitAngle + Math.PI + (Math.random() - 0.5) * 0.5; var fruitSpeed = 4 + Math.random() * 2; randomFruit.vx = Math.cos(fruitDirection) * fruitSpeed; randomFruit.vy = Math.sin(fruitDirection) * fruitSpeed; game.addChild(randomFruit); fruits.push(randomFruit); } return; // Skip the regular fruit spawning below } else if (self.level === 5) { availableFruits = ['strawberry', 'kiwi', 'orange', 'apple', 'watermelon']; // Level 5 fruits // Spawn more fruits for level 5 boss fruitCount = Math.floor(Math.random() * 3) + 4; // Level 5 boss: 4-6 fruits // Ensure we spawn level 5 fruits for (var j = 0; j < fruitCount; j++) { var fruitType = availableFruits[Math.floor(Math.random() * availableFruits.length)]; var randomFruit = new Fruit(fruitType); // Generate random position on the boss perimeter var fruitAngle = Math.random() * Math.PI * 2; var fruitRadius = self.width / 3; // Position the fruit on the perimeter randomFruit.x = self.x + Math.cos(fruitAngle) * fruitRadius; randomFruit.y = self.y + Math.sin(fruitAngle) * fruitRadius; // Aim outward with different trajectory var fruitDirection = fruitAngle + Math.PI + (Math.random() - 0.5) * 0.5; var fruitSpeed = 4 + Math.random() * 2; randomFruit.vx = Math.cos(fruitDirection) * fruitSpeed; randomFruit.vy = Math.sin(fruitDirection) * fruitSpeed; game.addChild(randomFruit); fruits.push(randomFruit); } return; // Skip the regular fruit spawning below } else { availableFruits = ['strawberry', 'kiwi', 'orange', 'apple', 'watermelon']; // Level 5 fruits } var fruitType = availableFruits[Math.floor(Math.random() * availableFruits.length)]; var randomFruit = new Fruit(fruitType); // Generate random position on the boss perimeter var fruitAngle = Math.random() * Math.PI * 2; var fruitRadius = self.width / 3; // Position the fruit on the perimeter randomFruit.x = self.x + Math.cos(fruitAngle) * fruitRadius; randomFruit.y = self.y + Math.sin(fruitAngle) * fruitRadius; // Aim outward with different trajectory var fruitDirection = fruitAngle + Math.PI + (Math.random() - 0.5) * 0.5; var fruitSpeed = 4 + Math.random() * 2; randomFruit.vx = Math.cos(fruitDirection) * fruitSpeed; randomFruit.vy = Math.sin(fruitDirection) * fruitSpeed; game.addChild(randomFruit); fruits.push(randomFruit); } } }; self.update = function (delta) { if (!self.active) { return; } // Apply bullet time effect if active (slow down by 75%) var speedMultiplier = bulletTimeActive ? 0.25 : 1; // Move boss back and forth if (Math.abs(self.x - self.targetX) < 10) { self.targetX = Math.random() * (GAME_WIDTH - 400) + 200; } var dirX = self.targetX - self.x; self.x += dirX / Math.abs(dirX) * self.vx * speedMultiplier; // Attack on timer var currentTime = Date.now(); if (currentTime >= self.attackTimer) { self.throwFruit(); // Extend attack interval during bullet time // For level 1 boss, spawn a fruit every 1 second (reduced from 2 seconds) var interval = self.level === 1 ? 1000 : self.attackInterval; self.attackTimer = currentTime + interval * (bulletTimeActive ? 4 : 1); } }; self.down = function (x, y, obj) { // Allow clicking on boss to damage it if (self.active) { self.hit(); } }; return self; }); var BulletTimePowerup = Container.expand(function () { var self = Container.call(this); self.type = 'bullettime'; self.sliced = false; self.width = 300; self.height = 300; self.points = 0; self.baseSpeed = 1.5; // Create a blue hourglass-shaped fruit for bullet time var bulletTimeGraphics = self.attachAsset('bullettime', { anchorX: 0.5, anchorY: 0.5 }); // No color tint needed as we're using the bullettime image asset // No shapes needed as we're using the bullettime image asset self.vx = 0; self.vy = 0; self.gravity = 0.01; self.rotationSpeed = (Math.random() - 0.5) * 0.015; self.init = function (x, y, direction) { self.x = x; self.y = y; var angle = direction * (Math.PI / 4) + Math.random() * Math.PI / 8 - Math.PI / 16; var speed = self.baseSpeed + Math.random() * 0.8; self.vx = Math.cos(angle) * speed * 1.15; self.vy = -Math.sin(angle) * speed - 8; }; self.slice = function () { if (self.sliced) { return; } self.sliced = true; // Activate bullet time effect activateBulletTime(); // Create bullet time text display instead of Blitzkrieg text var bulletTimeText = new Text2('BULLET TIME!', { size: 120, fill: 0x00BFFF, // Blue color for bullet time weight: 'bold' }); bulletTimeText.anchor.set(0.5, 0.5); bulletTimeText.x = GAME_WIDTH / 2; bulletTimeText.y = GAME_HEIGHT / 2; game.addChild(bulletTimeText); // Play bullet time sound LK.getSound('bullet').play(); // Flash effect tween(bulletTimeGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // No need to reset tint as we're not using tint } }); // Animate text to fade out tween(bulletTimeText, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 1000, easing: tween.elasticOut, onFinish: function onFinish() { bulletTimeText.destroy(); } }); }; self.down = function (x, y, obj) { if (!self.sliced) { self.slice(); // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; } }; self.update = function () { // Don't update movement if gameActive is false (leaderboard is visible) if (!gameActive) { return; } if (!self.sliced) { // Update whole fruit self.vy += self.gravity; self.x += self.vx; self.y += self.vy; self.rotation += self.rotationSpeed; // Bounce off walls if not sliced if (self.x < self.width / 2 && self.vx < 0) { self.vx = -self.vx * 0.8; self.x = self.width / 2; } if (self.x > GAME_WIDTH - self.width / 2 && self.vx > 0) { self.vx = -self.vx * 0.8; self.x = GAME_WIDTH - self.width / 2; } } }; self.isOffScreen = function () { return self.y > 2732 + self.height || self.x > GAME_WIDTH + self.width; }; return self; }); var Fruit = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'apple'; self.sliced = false; self.width = 0; self.height = 0; self.points = 0; self.baseSpeed = 0; var fruitGraphics; switch (self.type) { case 'watermelon': self.width = 200; self.height = 200; self.points = 5; self.baseSpeed = 1.6; // Faster base speed break; case 'apple': self.width = 240; self.height = 240; self.points = 4; self.baseSpeed = 1.7; // Faster base speed break; case 'orange': self.width = 280; self.height = 280; self.points = 3; self.baseSpeed = 1.8; // Faster base speed break; case 'kiwi': self.width = 320; self.height = 320; self.points = 2; self.baseSpeed = 2.0; // Faster base speed break; case 'passion_fruit': self.width = 360; self.height = 360; self.points = 6; self.baseSpeed = 2.3; // Even faster base speed break; case 'strawberry': self.width = 480; self.height = 480; self.points = 1; self.baseSpeed = 2.2; // Faster base speed break; case 'bomb': self.width = 320; self.height = 320; self.points = -10; self.baseSpeed = 1.7; // Faster base speed break; } fruitGraphics = self.attachAsset(self.type, { anchorX: 0.5, anchorY: 0.5 }); self.vx = 0; self.vy = 0; self.gravity = 0.01; // Reduced gravity to make fruits go higher self.rotationSpeed = (Math.random() - 0.5) * 0.018; // Slightly faster rotation self.init = function (x, y, direction) { self.x = x; self.y = y; var angle = direction * (Math.PI / 4) + Math.random() * Math.PI / 8 - Math.PI / 16; var currentLevel = Math.floor(LK.getScore() / 50) + 1; var levelMultiplier = 1 + (currentLevel - 1) * 0.1; // 10% increase per level // Use constant base speed without random factor to keep speed consistent within level var speed = self.baseSpeed * levelMultiplier; // Apply 30% speed reduction for level 1 if (currentLevel === 1) { speed *= 0.7; // 30% slower for level 1 } self.vx = Math.cos(angle) * speed * 1.15; // Slightly faster horizontal movement self.vy = -Math.sin(angle) * speed - 8; // Higher initial upward velocity }; self.slice = function () { if (self.sliced) { return; } self.sliced = true; // Create explosive flash effect fruitGraphics.tint = 0xFFFFFF; fruitGraphics.alpha = 1; // Animate explosive flash effect with scaling tween(fruitGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onUpdate: function onUpdate(progress) { // Create a pulsing effect with rotation during explosion fruitGraphics.rotation += 0.1; }, onFinish: function onFinish() { // Clean up after animation fruitGraphics.tint = 0xFFFFFF; fruitGraphics.scaleX = 1; fruitGraphics.scaleY = 1; } }); // Play slice and squash sounds LK.getSound('slice').play(); LK.getSound('squash').play(); return self.points; }; self.down = function (x, y, obj) { if (!self.sliced) { // Slice the fruit when clicked var points = self.slice(); // Check if it's a bomb if (self.type === 'bomb') { // Lose a life when clicking a bomb LK.getSound('explosion').play(); LK.effects.flashScreen(0xFF0000, 500); playerLives--; // Reduce lives by 1 updateLivesDisplay(); // Update hearts display // Make bomb disappear self.sliced = true; // Create explosive flash effect for bomb fruitGraphics.tint = 0xFFFFFF; // Animate bomb disappearing tween(fruitGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Remove bomb from game self.destroy(); // Find and remove from fruits array var index = fruits.indexOf(self); if (index > -1) { fruits.splice(index, 1); } } }); // If no lives left, game over if (playerLives <= 0) { // Update high score if current score is higher if (LK.getScore() > highScore) { highScore = LK.getScore(); storage.highScore = highScore; } // Add score to global leaderboard addScoreToLeaderboard(LK.getScore()); // Pass the current score and high score to game over display LK.showGameOver({ score: LK.getScore(), highScore: highScore }); } return; } // Add points to score LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore()); // Increment sliced fruits counter for current level slicedFruitsInLevel++; // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; } }; self.update = function () { // Don't update movement if gameActive is false (leaderboard is visible) if (!gameActive) { return; } if (!self.sliced) { // Update whole fruit // Apply bullet time effect if active (slow down by 75%) var speedMultiplier = bulletTimeActive ? 0.25 : 1; self.vy += self.gravity * speedMultiplier; self.x += self.vx * speedMultiplier; self.y += self.vy * speedMultiplier; self.rotation += self.rotationSpeed * speedMultiplier; // Bounce off walls if not sliced if (self.x < self.width / 2 && self.vx < 0) { self.vx = -self.vx * 0.8; // Bounce with some energy loss self.x = self.width / 2; } if (self.x > GAME_WIDTH - self.width / 2 && self.vx > 0) { self.vx = -self.vx * 0.8; // Bounce with some energy loss self.x = GAME_WIDTH - self.width / 2; } } else { // No update for sliced fruit since we're just flashing and fading them } }; self.isOffScreen = function () { return self.y > 2732 + self.height || self.x > GAME_WIDTH + self.width; }; return self; }); var Heart = Container.expand(function () { var self = Container.call(this); // Create a red heart shape (using blade asset tinted red) var heartShape = self.attachAsset('blade', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60 }); heartShape.tint = 0xFF0000; return self; }); var Leaderboard = Container.expand(function () { var self = Container.call(this); self.visible = false; // Background panel var panel = self.attachAsset('blade', { anchorX: 0.5, anchorY: 0.5, width: 1500, height: 1800 }); panel.tint = 0x111111; // Very dark color (almost black) for better contrast panel.alpha = 0.9; // Slightly transparent for better visibility // Title var title = new Text2('GLOBAL LEADERBOARD', { size: 100, fill: 0xFFFFFF }); title.anchor.set(0.5, 0); title.y = -800; self.addChild(title); // Scores container var scoresContainer = new Container(); scoresContainer.y = -650; self.addChild(scoresContainer); self.scoresContainer = scoresContainer; // Close button var closeBtn = self.attachAsset('blade', { anchorX: 0.5, anchorY: 0.5, width: 120, height: 120 }); closeBtn.tint = 0xFF0000; closeBtn.x = 700; closeBtn.y = -800; // X symbol on close button var closeX1 = self.attachAsset('blade', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 12 }); closeX1.tint = 0xFFFFFF; closeX1.rotation = Math.PI / 4; closeX1.x = 700; closeX1.y = -800; var closeX2 = self.attachAsset('blade', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 12 }); closeX2.tint = 0xFFFFFF; closeX2.rotation = -Math.PI / 4; closeX2.x = 700; closeX2.y = -800; // Close button interaction closeBtn.interactive = true; closeBtn.down = function (x, y, obj) { self.hide(); }; // Show leaderboard self.show = function (scores) { self.visible = true; self.updateScores(scores); // Pause the game when leaderboard is visible gameActive = false; // Make game area 50% transparent game.alpha = 0.5; // Make leaderboard panel fully opaque (0% transparent) panel.alpha = 1.0; // Bring leaderboard to front layer if (self.parent) { var parent = self.parent; parent.removeChild(self); parent.addChild(self); } }; // Hide leaderboard self.hide = function () { self.visible = false; // Resume the game when leaderboard is hidden gameActive = true; // Restore game area opacity game.alpha = 1.0; }; // Update scores display self.updateScores = function (scores) { // Clear existing score entries while (scoresContainer.children.length > 0) { scoresContainer.removeChildAt(0); } // Add score entries for (var i = 0; i < scores.length; i++) { var entry = scores[i]; var yPos = i * 120; // Rank var rank = new Text2(i + 1 + '.', { size: 80, fill: 0xFFFFFF }); rank.anchor.set(1, 0); rank.x = -600; rank.y = yPos; scoresContainer.addChild(rank); // Username var username = entry.username || "Player"; var usernameText = new Text2(username, { size: 60, fill: 0xFFFFFF }); usernameText.anchor.set(0, 0); usernameText.x = -500; usernameText.y = yPos; scoresContainer.addChild(usernameText); // Score value var scoreText = new Text2(entry.score.toString(), { size: 80, fill: 0xFFF200 // Brighter yellow color for better visibility }); scoreText.anchor.set(0, 0); scoreText.x = 0; scoreText.y = yPos; scoresContainer.addChild(scoreText); } // Add message if no scores if (scores.length === 0) { var noScores = new Text2("No scores yet!", { size: 80, fill: 0x00FFFF //{3Y} // Bright cyan color for better visibility }); noScores.anchor.set(0.5, 0); noScores.y = 100; scoresContainer.addChild(noScores); } }; return self; }); var LeaderboardButton = Container.expand(function () { var self = Container.call(this); // Create button background var buttonBg = self.attachAsset('blade', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 120 }); buttonBg.tint = 0x3498db; // Create button text that says LEADERBOARD var buttonText = new Text2('LEADERBOARD', { size: 60, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); self.addChild(buttonText); // Button interaction self.down = function (x, y, obj) { // Scale down effect on press tween(self, { scaleX: 0.9, scaleY: 0.9 }, { duration: 100 }); // Toggle leaderboard visibility if (leaderboard && leaderboard.visible) { leaderboard.hide(); } else { showLeaderboard(); } }; self.up = function (x, y, obj) { // Scale back to normal on release tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100 }); }; return self; }); var LifeFruit = Container.expand(function () { var self = Container.call(this); self.type = 'lifefruit'; self.sliced = false; self.width = 320; self.height = 320; self.points = 0; self.baseSpeed = 1.5; // Create a white fruit with heart shape for extra life var lifeGraphics = self.attachAsset('lifefruit_img', { anchorX: 0.5, anchorY: 0.5 }); // No color tint needed as we're using the lifefruit_img asset // No heart shape needed as we're using the lifefruit_img asset self.vx = 0; self.vy = 0; self.gravity = 0.01; self.rotationSpeed = (Math.random() - 0.5) * 0.015; self.init = function (x, y, direction) { self.x = x; self.y = y; var angle = direction * (Math.PI / 4) + Math.random() * Math.PI / 8 - Math.PI / 16; var speed = self.baseSpeed + Math.random() * 0.8; self.vx = Math.cos(angle) * speed * 1.15; self.vy = -Math.sin(angle) * speed - 8; }; self.slice = function () { if (self.sliced) { return; } self.sliced = true; // Add an extra life but cap at 12 maximum lives playerLives = Math.min(12, playerLives + 1); // Cap lives at 12 updateLivesDisplay(); // Flash effect tween(lifeGraphics, { alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // No need to reset tint as we're not using tint } }); // Play fruit squash sound before lifesquash sound LK.getSound('squash').play(); LK.getSound('lifesquash').play(); }; self.down = function (x, y, obj) { if (!self.sliced) { self.slice(); // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; } }; self.update = function () { // Don't update movement if gameActive is false (leaderboard is visible) if (!gameActive) { return; } if (!self.sliced) { // Update whole fruit // Apply bullet time effect if active (slow down by 75%) var speedMultiplier = bulletTimeActive ? 0.25 : 1; self.vy += self.gravity * speedMultiplier; self.x += self.vx * speedMultiplier; self.y += self.vy * speedMultiplier; self.rotation += self.rotationSpeed * speedMultiplier; // Bounce off walls if not sliced if (self.x < self.width / 2 && self.vx < 0) { self.vx = -self.vx * 0.8; self.x = self.width / 2; } if (self.x > GAME_WIDTH - self.width / 2 && self.vx > 0) { self.vx = -self.vx * 0.8; self.x = GAME_WIDTH - self.width / 2; } } }; self.isOffScreen = function () { return self.y > 2732 + self.height || self.x > GAME_WIDTH + self.width; }; return self; }); var PlayAreaBoundary = Container.expand(function (isTop) { var self = Container.call(this); var line = self.attachAsset('blade', { anchorX: 0, anchorY: 0.5, width: GAME_WIDTH, height: 10 }); line.tint = 0xFFFFFF; line.alpha = 0.7; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x3498DB }); /**** * Game Code ****/ // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var SPAWN_INTERVAL_MIN = 1000; // Decreased spawn interval minimum for more frequent spawning var SPAWN_INTERVAL_MAX = 2000; // Decreased spawn interval maximum for more frequent spawning var SPAWN_COUNT_MIN = 1; var SPAWN_COUNT_MAX = 2; // Increased to spawn more fruits at once var FRUIT_TYPES = ['watermelon', 'apple', 'orange', 'kiwi', 'strawberry', 'passion_fruit']; var BOSS_FRUIT_TYPES = ['strawberry', 'kiwi', 'orange', 'apple', 'passion_fruit']; // Boss type matches newest fruit type introduced in each level var LEVEL_THRESHOLDS = [5, 50, 150, 300, 450]; // Score thresholds for each level boss var LEVEL_FRUITS = [['strawberry'], // Level 1 fruits ['strawberry', 'kiwi'], // Level 2 fruits ['strawberry', 'kiwi', 'orange'], // Level 3 fruits ['strawberry', 'kiwi', 'orange', 'apple'], // Level 4 fruits ['strawberry', 'kiwi', 'orange', 'apple', 'watermelon'] // Level 5 fruits ]; var BOMB_PROBABILITY = 0.2; // Increased bomb probability // Game variables var fruits = []; var blade = null; // Boss-related variables var boss = null; var bossActivated = false; var slicedFruitsInLevel = 0; // Track how many fruits sliced in current level for boss activation var bossFlashed = false; // Track if boss has been flashed in current level // Bullet time variables var bulletTimeActive = false; var bulletTimeEndTime = 0; var bulletTimeBar; // Progress bar for bullet time var bulletTimeDuration = 10000; // 10 seconds // Level tracking var currentLevel = 1; var nextLevelNotified = false; var showingLevelText = false; var levelTextTimer = 0; var levelText; // Level notification text var levelDisplay; // Text to display current level // Spawn timers var lastSpawnTime = 0; var nextSpawnTime = 0; // Combo system var comboCount = 0; var comboTimer = 0; var comboTimeout = 1000; // ms to reset combo var comboTxt; // Text to display combo // Player state var gameActive = true; var playerLives = 3; // Player starts with 3 lives var heartsDisplay; // Container for heart icons var missedFruits = 0; // Counter for missed fruits var missedText; // Text to display missed count var lifeFruitSpawned = 0; // Track how many life fruits we've spawned // Score-related var scoreTxt; // Text to display score var highScore = storage.highScore || 0; // Get high score from storage or default to 0 var highScoreText; // Text to display high score // Leaderboard var leaderboardButton; // Button to open leaderboard var leaderboard; // Leaderboard UI component // Parse leaderboard scores from storage or use empty array if none exists var leaderboardScores = []; if (storage.leaderboardScores) { var scoresArray = storage.leaderboardScores.split(';'); for (var i = 0; i < scoresArray.length; i++) { if (scoresArray[i]) { var parts = scoresArray[i].split(','); var scoreObj = { score: parseInt(parts[0]) }; if (parts.length > 1) { scoreObj.username = parts[1]; } leaderboardScores.push(scoreObj); } } } // Global leaderboard data var playerName = storage.playerName || "Player"; // Get saved player name or use default // UI elements var scoreTxt; var comboTxt; function setupGame() { // Prompt for player name if not already stored if (!storage.playerName) { var defaultName = "Player"; // Skip popup for now and just use default name // LK.showPopup is not available in the current version storage.playerName = defaultName; playerName = defaultName; } // Create blade blade = game.addChild(new Blade()); // Create boss reference but don't create the actual boss instance yet // We'll create it only when needed for the boss fight boss = null; // Set up score display scoreTxt = new Text2('0', { size: 100, fill: 0xFFFFFF, weight: 'bold' // Make text bold }); scoreTxt.anchor.set(0.5, 0); scoreTxt.y = 30; LK.gui.top.addChild(scoreTxt); // Set up combo display comboTxt = new Text2('', { size: 60, fill: 0xFFFF00 }); comboTxt.anchor.set(0.5, 0); comboTxt.y = 140; comboTxt.alpha = 0; LK.gui.top.addChild(comboTxt); // Set up level text display levelText = new Text2('Level 1', { size: 120, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.x = GAME_WIDTH / 2; levelText.y = GAME_HEIGHT / 2; levelText.alpha = 0; game.addChild(levelText); // Add level display to top right levelDisplay = new Text2('Level: 1', { size: 100, fill: 0xFFFFFF, weight: 'bold' // Make text bold }); levelDisplay.anchor.set(1, 0); levelDisplay.x = GAME_WIDTH - 50; // Right justified 50px from right wall levelDisplay.y = 100; game.addChild(levelDisplay); // Initialize missed counter missedFruits = 0; // Initialize the missed counter display through the dedicated function updateMissedCounter(); // Set up high score display highScoreText = new Text2('Your High Score: ' + highScore, { size: 60, fill: 0xFFFFFF }); highScoreText.anchor.set(0, 1); highScoreText.x = 50; highScoreText.y = -30; // Position at the bottom, moved 20 pixels below LK.gui.bottomLeft.addChild(highScoreText); // Create bullet time bar (initially hidden) bulletTimeBar = new Container(); bulletTimeBar.x = GAME_WIDTH / 2; bulletTimeBar.y = 280; // Moved 80px down from the top bulletTimeBar.visible = false; game.addChild(bulletTimeBar); // Bullet time bar background var bulletTimeBarBg = LK.getAsset('blade', { anchorX: 0.5, anchorY: 0.5, width: 1000, height: 30 }); bulletTimeBarBg.tint = 0x333333; // Bullet time bar fill var bulletTimeBarFill = LK.getAsset('blade', { anchorX: 0, anchorY: 0.5, width: 1000, height: 30 }); bulletTimeBarFill.tint = 0x00BFFF; // Match bullet time powerup color bulletTimeBarFill.x = -500; // Center the bar // Bullet time text var bulletTimeText = new Text2('BULLET TIME', { size: 60, fill: 0xFFFFFF }); bulletTimeText.anchor.set(0.5, 0.5); bulletTimeText.y = -60; // Add elements to bullet time bar bulletTimeBar.addChild(bulletTimeBarBg); bulletTimeBar.addChild(bulletTimeBarFill); bulletTimeBar.addChild(bulletTimeText); bulletTimeBar.barFill = bulletTimeBarFill; // Store reference to the fill for updates // Create hearts display for lives heartsDisplay = new Container(); heartsDisplay.x = GAME_WIDTH - 100; // Right justified 100px from right wall heartsDisplay.y = 250; // 250px from top game.addChild(heartsDisplay); // Add initial hearts (3 lives) updateLivesDisplay(); // Leaderboard button is hidden // Create and position leaderboard UI leaderboard = new Leaderboard(); leaderboard.x = GAME_WIDTH / 2; leaderboard.y = GAME_HEIGHT / 2; leaderboard.visible = false; game.addChild(leaderboard); // Set initial spawn time nextSpawnTime = Date.now() + Math.random() * (SPAWN_INTERVAL_MAX - SPAWN_INTERVAL_MIN) + SPAWN_INTERVAL_MIN; // Special level 1 powerup spawning removed // Play background music LK.playMusic('gameMusic'); // Reset all game state currentLevel = 1; bossActivated = false; nextLevelNotified = false; LK.setScore(0); missedFruits = 0; playerLives = 3; // Reset lives to 3 lifeFruitSpawned = 0; // Reset life fruit spawn counter slicedFruitsInLevel = 0; // Reset sliced fruits counter comboCount = 0; // Reset combo counter comboTimer = 0; // Reset combo timer // Add play area boundary lines var topBoundary = new PlayAreaBoundary(true); topBoundary.y = 320; // Position 320px from the top game.addChild(topBoundary); var bottomBoundary = new PlayAreaBoundary(false); bottomBoundary.y = GAME_HEIGHT - 200; // Position 200px from the bottom game.addChild(bottomBoundary); // Show level 1 text at start showLevelText(1); } // Function to display level text and reset level-specific state function showLevelText(level) { // Set level text content levelText.setText('Level ' + level); levelText.alpha = 0; showingLevelText = true; // Reset level-specific counters missedFruits = 0; updateMissedCounter(); slicedFruitsInLevel = 0; // Reset boss state for new level bossActivated = false; bossFlashed = false; // Reset boss flash state for new level // Reset bullet time bulletTimeActive = false; bulletTimeBar.visible = false; // Update level display in top right levelDisplay.setText('Level: ' + level); // Clear all remaining fruits on screen for (var i = fruits.length - 1; i >= 0; i--) { fruits[i].destroy(); fruits.splice(i, 1); } // Animate the level text tween(levelText, { alpha: 1 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Hold the text visible for a moment levelTextTimer = Date.now() + 2000; } }); } function spawnFruits() { var count = currentLevel === 1 ? Math.ceil((Math.floor(Math.random() * (SPAWN_COUNT_MAX - SPAWN_COUNT_MIN + 1)) + SPAWN_COUNT_MIN) / 2) : // 50% reduction for level 1 Math.floor(Math.random() * (SPAWN_COUNT_MAX - SPAWN_COUNT_MIN + 1)) + SPAWN_COUNT_MIN; // Get appropriate fruit types for current level var availableFruits = LEVEL_FRUITS[currentLevel - 1] || ['strawberry']; // Check if we should spawn a life fruit based on the current level var spawnLifeFruit = false; var spawnBulletTime = false; var spawnBlitz = false; var maxLifeFruits = currentLevel; if (currentLevel === 4) { maxLifeFruits = 21; // Level 4 gets 21 life fruits (increased from 17) } else if (currentLevel === 5) { maxLifeFruits = 20; // Level 5 gets 20 life fruits (increased from 14) } // Level 4 gets 21 life fruits if (currentLevel === 1) { maxLifeFruits = 5; // Level 1 gets 5 life fruits (unchanged) } else if (currentLevel === 2) { maxLifeFruits = 12; // Level 2 gets 12 life fruits (increased from 9) } else if (currentLevel === 3) { maxLifeFruits = 10; // Level 3 gets 10 life fruits (reduced from 17) } // Increase spawn chances for all levels if (lifeFruitSpawned < maxLifeFruits && Math.random() < (currentLevel === 1 ? 0.2 : currentLevel === 2 ? 0.25 : currentLevel === 3 ? 0.3 : currentLevel === 4 ? 0.25 : currentLevel === 5 ? 0.3 : 0.1)) { // 30% chance to spawn a life fruit in level 5 and level 3, 25% chance in level 2 and level 4, 20% chance in level 1 spawnLifeFruit = true; lifeFruitSpawned++; } else if (!bulletTimeActive && Math.random() < (currentLevel === 3 ? 0.12 : currentLevel === 4 ? 0.15 : currentLevel === 5 ? 0.2 : 0.05)) { // 20% chance to spawn bullet time powerup in level 5, 15% in level 4, 12% in level 3, 5% in other levels spawnBulletTime = true; } else if (Math.random() < (currentLevel === 1 ? 0.12 : currentLevel === 3 ? 0.12 : currentLevel === 4 ? 0.15 : currentLevel === 5 ? 0.2 : 0.05)) { // 20% chance to spawn blitz powerup in level 5, 15% in level 4, 12% in level 1 and level 3, 5% in other levels spawnBlitz = true; } // Loop through and create fruits for (var i = 0; i < count; i++) { var fruit; // Create life fruit if it's time if (i === 0 && spawnLifeFruit) { fruit = new LifeFruit(); } else if (i === 0 && spawnBulletTime) { fruit = new BulletTimePowerup(); } else if (i === 0 && spawnBlitz) { fruit = new BlitzPowerup(); } else { var isBomb = Math.random() < BOMB_PROBABILITY; var type = isBomb ? 'bomb' : availableFruits[Math.floor(Math.random() * availableFruits.length)]; fruit = new Fruit(type); } // Determine flight pattern var pattern = Math.random(); if (pattern < 0.5) { // Horizontal flight from left to right var x = -fruit.width; var y = Math.random() * (GAME_HEIGHT / 3) + 300; // Higher position range fruit.x = x; fruit.y = y; fruit.vx = fruit.baseSpeed + Math.random() * 2.5; // Slightly faster fruit.vy = -Math.random() * 2; // Add slight upward movement } else { // Vertical drop var x = Math.random() * (GAME_WIDTH - 200) + 100; var direction = Math.random(); // Random direction between 0 and 1 fruit.init(x, GAME_HEIGHT, direction); } game.addChild(fruit); fruits.push(fruit); } // Schedule next spawn with appropriate delay based on level // Level 1, 2, & 3: 30% shorter delay // Level 4+: normal delay var delayMultiplier = 1; if (currentLevel === 1) { delayMultiplier = 0.6; // 40% shorter delay for level 1 (decreased from 0.8 to 0.6) } else if (currentLevel === 2 || currentLevel === 3) { delayMultiplier = 0.7; // 30% shorter delay for level 2 and 3 } else if (currentLevel === 4) { delayMultiplier = 0.55; // 45% shorter delay for level 4 } else if (currentLevel === 5) { delayMultiplier = 0.5; // 50% shorter delay for level 5 } nextSpawnTime = Date.now() + delayMultiplier * (Math.random() * (SPAWN_INTERVAL_MAX - SPAWN_INTERVAL_MIN) + SPAWN_INTERVAL_MIN); } function updateCombo() { if (comboCount > 1) { comboTxt.setText('COMBO x' + comboCount + '!'); comboTxt.alpha = 1; // Add combo bonus points LK.setScore(LK.getScore() + comboCount * 2); // Play combo sound LK.getSound('combo').play(); // Animate combo text tween(comboTxt, { alpha: 0 }, { duration: 3000, easing: tween.easeOut }); } comboCount = 0; } function handleBladeCollisions() { // Return early if the blade isn't active or doesn't have enough points to form a line if (!blade.active || blade.points.length < 2) { return; } // Process multiple segments for more accurate detection // Increase segments to check for more responsive slashing, especially for level 1 boss var segmentsToCheck = Math.min(15, blade.points.length - 1); // Check multiple segments of the blade for better sensitivity var hitFruits = {}; var hitBoss = false; // First check for boss collision across all blade segments if (boss && boss.active) { for (var j = 0; j < segmentsToCheck; j++) { var startPoint = blade.points[j]; var endPoint = blade.points[j + 1]; // Special case for level 1 boss - add velocity check for quicker response if (boss.level === 1) { // Calculate slice velocity for level 1 boss (faster slices are more effective) var sliceVelocity = Math.sqrt(Math.pow(endPoint.x - startPoint.x, 2) + Math.pow(endPoint.y - startPoint.y, 2)); // For level 1 boss, fast slices are more effective var adjustedWidth = sliceVelocity > 10 ? boss.width / 1.8 : boss.width / 2.2; if (lineIntersectsCircle(startPoint.x, startPoint.y, endPoint.x, endPoint.y, boss.x, boss.y, adjustedWidth)) { // Hit the boss var defeated = boss.hit(); // Play slice and squash sounds LK.getSound('slice').play(); LK.getSound('squash').play(); // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; hitBoss = true; break; } } // Check for other boss levels else if (lineIntersectsCircle(startPoint.x, startPoint.y, endPoint.x, endPoint.y, boss.x, boss.y, boss.width / 2.5)) { // Hit the boss var defeated = boss.hit(); // Play slice and squash sounds LK.getSound('slice').play(); LK.getSound('squash').play(); // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; hitBoss = true; break; } } } // Then check for fruit collisions for (var j = 0; j < segmentsToCheck; j++) { var startPoint = blade.points[j]; var endPoint = blade.points[j + 1]; // Check collisions with fruits for (var i = 0; i < fruits.length; i++) { var fruit = fruits[i]; if (!fruit.sliced && !hitFruits[i] && lineIntersectsCircle(startPoint.x, startPoint.y, endPoint.x, endPoint.y, fruit.x, fruit.y, fruit.width / 2)) { hitFruits[i] = true; if (fruit.type === 'bomb') { // Lose a life when hitting a bomb LK.getSound('explosion').play(); LK.effects.flashScreen(0xFF0000, 500); playerLives--; // Reduce lives by 1 updateLivesDisplay(); // Update hearts display // If no lives left, game over if (playerLives <= 0) { // Update high score if current score is higher if (LK.getScore() > highScore) { highScore = LK.getScore(); storage.highScore = highScore; } // Add score to global leaderboard addScoreToLeaderboard(LK.getScore()); // Pass the current score and high score to game over display LK.showGameOver({ score: LK.getScore(), highScore: highScore }); } break; // Exit the inner loop since we hit a bomb } } // Handle life fruit else if (fruit.type === 'lifefruit' && !hitFruits[i] && lineIntersectsCircle(startPoint.x, startPoint.y, endPoint.x, endPoint.y, fruit.x, fruit.y, fruit.width / 2)) { hitFruits[i] = true; // Slice the life fruit and gain an extra life fruit.slice(); // Play a special sound LK.getSound('combo').play(); // Flash green for the extra life LK.effects.flashScreen(0x00FF00, 300); // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; } else if (!fruit.sliced && !hitFruits[i] && lineIntersectsCircle(startPoint.x, startPoint.y, endPoint.x, endPoint.y, fruit.x, fruit.y, fruit.width / 2)) { hitFruits[i] = true; // Slice regular fruit var points = fruit.slice(); LK.setScore(LK.getScore() + points); scoreTxt.setText(LK.getScore()); // Increment sliced fruits counter for current level slicedFruitsInLevel++; // Play slice sound LK.getSound('slice').play(); // Update combo comboCount++; comboTimer = Date.now() + comboTimeout; } } } } function lineIntersectsCircle(x1, y1, x2, y2, cx, cy, r) { // Check if we're dealing with the boss var isBoss = boss && cx === boss.x && cy === boss.y; var isLevel1Boss = isBoss && boss.level === 1; // Increase detection radius for bosses, especially level 1 boss // Make all bosses more sensitive to slashes when checking through fruits r = isLevel1Boss ? r * 7.0 : isBoss ? r * 5.0 : r * 1.5; // Find the closest point on the line segment to the circle center var dx = x2 - x1; var dy = y2 - y1; var len = Math.sqrt(dx * dx + dy * dy); // Normalize direction vector dx /= len; dy /= len; // Vector from line start to circle center var vx = cx - x1; var vy = cy - y1; // Project this vector onto the line direction var projection = vx * dx + vy * dy; // Clamp projection to line segment - bosses have much more tolerance var clampMax = isBoss ? len * 4.0 : len; projection = Math.max(0, Math.min(clampMax, projection)); // Find the closest point on the line segment var closestX = x1 + projection * dx; var closestY = y1 + projection * dy; // Check if this point is within the circle var distanceSquared = (cx - closestX) * (cx - closestX) + (cy - closestY) * (cy - closestY); // For bosses, further increase hit detection by being more lenient with distance check if (isBoss) { // For level 1 boss, be even more lenient to make it more sensitive through fruits if (isLevel1Boss) { return distanceSquared <= r * r * 4.5; } // For other bosses, also increase sensitivity but not as much return distanceSquared <= r * r * 3.5; } // Normal fruit collision detection return distanceSquared <= r * r; } // Show the leaderboard UI function showLeaderboard() { if (leaderboard) { leaderboard.show(leaderboardScores); } } // Update the hearts display based on current lives function updateLivesDisplay() { // Clear existing hearts while (heartsDisplay.children.length > 0) { heartsDisplay.removeChildAt(0); } // Add new hearts based on current lives for (var i = 0; i < playerLives; i++) { var heart = new Heart(); heart.x = -i * 80; // Space hearts horizontally from right to left heartsDisplay.addChild(heart); } // Update the missed counter updateMissedCounter(); } // Dedicated function to update missed fruits counter and its color function updateMissedCounter() { // Determine color based on missed count var missedColor; if (missedFruits <= 3) { missedColor = 0x00FF00; // Green for 0-3 misses } else if (missedFruits <= 6) { missedColor = 0xFFA500; // Orange for 4-6 misses } else { missedColor = 0xFF0000; // Red for 7-10 misses } // Remove old text from parent if it exists if (missedText && missedText.parent) { missedText.parent.removeChild(missedText); } // Create new Text2 with the appropriate color missedText = new Text2('Missed: ' + missedFruits + '/10', { size: 100, fill: missedColor, // Apply the color based on missed count weight: 'bold' // Make text bold }); missedText.anchor.set(1, 0); missedText.x = GAME_WIDTH - 50; missedText.y = 10; game.addChild(missedText); } // Activate bullet time effect function activateBulletTime() { bulletTimeActive = true; bulletTimeEndTime = Date.now() + bulletTimeDuration; bulletTimeBar.visible = true; // Flash screen blue to indicate bullet time activation LK.effects.flashScreen(0x00BFFF, 500); // Play a sound LK.getSound('combo').play(); } // Update bullet time progress bar function updateBulletTime() { if (!bulletTimeActive) { return; } var currentTime = Date.now(); var remaining = bulletTimeEndTime - currentTime; if (remaining <= 0) { // Bullet time has ended bulletTimeActive = false; bulletTimeBar.visible = false; return; } // Update progress bar var progress = remaining / bulletTimeDuration; bulletTimeBar.barFill.width = 1000 * progress; } // Create a fun victory dance with fruit bouncing animation function startVictoryDance() { // Create victory message var victoryText = new Text2('VICTORY!', { size: 200, fill: 0xFFD700, // Gold color weight: 'bold' }); victoryText.anchor.set(0.5, 0.5); victoryText.x = GAME_WIDTH / 2; victoryText.y = GAME_HEIGHT / 3; victoryText.alpha = 0; game.addChild(victoryText); // Create celebration fruits var celebrationFruits = []; var fruitTypes = ['strawberry', 'kiwi', 'orange', 'apple', 'watermelon', 'passion_fruit']; // Spawn celebration fruits for (var i = 0; i < 20; i++) { var fruitType = fruitTypes[Math.floor(Math.random() * fruitTypes.length)]; var celebrationFruit = new Fruit(fruitType); celebrationFruit.x = Math.random() * GAME_WIDTH; celebrationFruit.y = GAME_HEIGHT + 100 + Math.random() * 500; celebrationFruit.vx = (Math.random() - 0.5) * 10; celebrationFruit.vy = -10 - Math.random() * 5; celebrationFruit.rotationSpeed = (Math.random() - 0.5) * 0.1; game.addChild(celebrationFruit); celebrationFruits.push(celebrationFruit); } // Animate victory text tween(victoryText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 1000, easing: tween.elasticOut }); // Start celebration animation sequence var danceTimer = 0; var danceInterval = LK.setInterval(function () { danceTimer += 100; // Update fruits with bouncy motion for (var i = 0; i < celebrationFruits.length; i++) { var fruit = celebrationFruits[i]; // Apply gravity fruit.vy += 0.2; // Update position fruit.x += fruit.vx; fruit.y += fruit.vy; fruit.rotation += fruit.rotationSpeed; // Bounce off bottom if (fruit.y > GAME_HEIGHT - 150 && fruit.vy > 0) { fruit.vy = -fruit.vy * 0.8; // Play squash sound at random times if (Math.random() < 0.2) { LK.getSound('squash').play(); } } // Bounce off sides if (fruit.x < 100 && fruit.vx < 0 || fruit.x > GAME_WIDTH - 100 && fruit.vx > 0) { fruit.vx = -fruit.vx * 0.8; } // Wobble effect if (danceTimer % 500 < 250) { tween(fruit, { scaleX: 1.2, scaleY: 0.8 }, { duration: 250, easing: tween.easeOut }); } else { tween(fruit, { scaleX: 0.8, scaleY: 1.2 }, { duration: 250, easing: tween.easeOut }); } } // Make victory text pulse var scale = 1 + 0.1 * Math.sin(danceTimer / 200); victoryText.scale.set(scale); // End dance sequence after 10 seconds if (danceTimer >= 10000) { LK.clearInterval(danceInterval); // Clean up celebration fruits for (var i = 0; i < celebrationFruits.length; i++) { tween(celebrationFruits[i], { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 500, easing: tween.easeOut }); } // Fade out victory text tween(victoryText, { alpha: 0, scaleX: 3, scaleY: 3 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Show final game over screen if (LK.getScore() > highScore) { highScore = LK.getScore(); storage.highScore = highScore; } // Add score to global leaderboard addScoreToLeaderboard(LK.getScore()); // Show you win screen LK.showYouWin({ score: LK.getScore(), highScore: highScore }); } }); } }, 16); // Run at approximately 60fps } // Add a score to the leaderboard function addScoreToLeaderboard(score) { // Only add score if it's greater than 0 if (score <= 0) { return; } // Add new score to leaderboard with Upit username or 'Anon' var username = LK.getUptUsername ? LK.getUptUsername() : 'Anon'; leaderboardScores.push({ score: score, username: username }); // Sort leaderboard by score (highest first) leaderboardScores.sort(function (a, b) { return b.score - a.score; }); // Limit to top 10 scores if (leaderboardScores.length > 10) { leaderboardScores = leaderboardScores.slice(0, 10); } // Save to storage - convert to simple string format to avoid undefined JSON error var scoresString = ''; for (var i = 0; i < leaderboardScores.length; i++) { scoresString += leaderboardScores[i].score + "," + (leaderboardScores[i].username || "Player"); if (i < leaderboardScores.length - 1) { scoresString += ';'; } } storage.leaderboardScores = scoresString; } ; // Game update function game.update = function () { // Don't update game state if game is paused (leaderboard is shown) if (!gameActive) { return; } var currentTime = Date.now(); // Handle level text animation if (showingLevelText && currentTime > levelTextTimer && levelText.alpha > 0) { tween(levelText, { alpha: 0 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { showingLevelText = false; } }); } // Check if we should flash the boss at 80% into the level // For level 1: Flash at 16 fruits (80% of 20) // For level 2: Flash at 24 fruits (80% of 30) // For level 3: Flash at 32 fruits (80% of 40) // For level 4: Flash at 45 fruits (90% of 50) // For level 5: Flash at 45 fruits (90% of 50) if (currentLevel >= 1 && currentLevel <= 5 && (currentLevel === 1 && slicedFruitsInLevel >= 16 || currentLevel === 2 && slicedFruitsInLevel >= 24 || currentLevel === 3 && slicedFruitsInLevel >= 32 || currentLevel === 4 && slicedFruitsInLevel >= 45 || currentLevel === 5 && slicedFruitsInLevel >= 45) && !bossFlashed && !bossActivated) { var _doMultipleFlashes = function doMultipleFlashes(flashCount) { if (flashCount <= 0) { tempBoss.destroy(); return; } // Flash in tween(tempBoss, { alpha: 1 }, { duration: 250, // Half second total per flash (250ms in, 250ms out) easing: tween.easeOut, onFinish: function onFinish() { // Flash out tween(tempBoss, { alpha: 0 }, { duration: 250, // Half second total per flash (250ms in, 250ms out) easing: tween.easeIn, onFinish: function onFinish() { // Continue with next flash _doMultipleFlashes(flashCount - 1); } }); } }); }; // Start the sequence with 3 flashes bossFlashed = true; // Create a temporary boss image for flashing var tempBoss = new Boss(currentLevel); tempBoss.x = GAME_WIDTH / 2; tempBoss.y = GAME_HEIGHT / 2; tempBoss.alpha = 0; game.addChild(tempBoss); // Function to perform multiple boss flashes _doMultipleFlashes(3); } // Check if we should activate the boss fight based on fruit count // Level 1: After 20 sliced fruits // Level 2: After 30 sliced fruits // Level 3: After 40 sliced fruits // Other levels: After 5 sliced fruits consistently var shouldActivateBoss = false; if (currentLevel === 1) { shouldActivateBoss = slicedFruitsInLevel >= 20 && !bossActivated && !nextLevelNotified; } else if (currentLevel === 2) { shouldActivateBoss = slicedFruitsInLevel >= 30 && !bossActivated && !nextLevelNotified; } else if (currentLevel === 3) { shouldActivateBoss = slicedFruitsInLevel >= 40 && !bossActivated && !nextLevelNotified; } else if (currentLevel === 4) { shouldActivateBoss = slicedFruitsInLevel >= 50 && !bossActivated && !nextLevelNotified; } else if (currentLevel === 5) { shouldActivateBoss = slicedFruitsInLevel >= 50 && !bossActivated && !nextLevelNotified; } else { // Fallback for any future levels beyond 5 shouldActivateBoss = LK.getScore() >= LEVEL_THRESHOLDS[currentLevel - 1] && !bossActivated && !nextLevelNotified; } if (currentLevel <= 5 && shouldActivateBoss) { bossActivated = true; // Create a new boss for the current level if (boss) { boss.destroy(); } boss = game.addChild(new Boss(currentLevel)); boss.activate(); // Clear existing fruits during boss transition for (var i = fruits.length - 1; i >= 0; i--) { fruits[i].destroy(); fruits.splice(i, 1); } } // Only spawn normal fruits if we're not in a boss fight if (currentTime >= nextSpawnTime && (!bossActivated || !boss.active)) { spawnFruits(); } // Update all fruits for (var i = fruits.length - 1; i >= 0; i--) { var fruit = fruits[i]; fruit.update(); // Remove fruits that are off-screen if (fruit.isOffScreen()) { // Only count missed fruits that aren't bombs or already sliced if (!fruit.sliced && fruit.type !== 'bomb') { missedFruits++; // Update the missed counter with new value and color updateMissedCounter(); // If 10 fruits are missed, lose a life and reset miss counter if (missedFruits >= 10) { LK.effects.flashScreen(0xFF0000, 500); playerLives--; // Reduce lives by 1 updateLivesDisplay(); // Update hearts display missedFruits = 0; // Reset missed fruits counter updateMissedCounter(); // Update missed counter display // Only show game over if no lives left if (playerLives <= 0) { // Update high score if current score is higher if (LK.getScore() > highScore) { highScore = LK.getScore(); storage.highScore = highScore; } // Add score to global leaderboard addScoreToLeaderboard(LK.getScore()); // Pass the current score and high score to game over display LK.showGameOver({ score: LK.getScore(), highScore: highScore }); } } } fruit.destroy(); fruits.splice(i, 1); } } // Check for blade collisions handleBladeCollisions(); // Update blade blade.update(); // Update boss if active if (boss && boss.active) { boss.update(1 / 60); // Pass approximate delta time } // If boss was defeated, move to next level if (bossActivated && boss && !boss.active) { bossActivated = false; nextLevelNotified = false; // Move to next level if we haven't reached level 5 yet if (currentLevel < 5) { currentLevel++; // Show level notification showLevelText(currentLevel); } else if (currentLevel === 5) { // Player has beaten all levels LK.effects.flashScreen(0x00FF00, 800); // Start the victory dance sequence startVictoryDance(); } } // Check combo timer if (comboCount > 0 && Date.now() > comboTimer) { updateCombo(); } // Update bullet time effect if (bulletTimeActive) { updateBulletTime(); } }; // Handle touch/mouse events game.down = function (x, y, obj) { blade.active = true; blade.reset(); blade.addPoint(x, y); }; game.move = function (x, y, obj) { if (blade.active) { blade.addPoint(x, y); handleBladeCollisions(); } }; game.up = function (x, y, obj) { blade.active = false; }; // Start the game setupGame();
===================================================================
--- original.js
+++ change.js
@@ -384,9 +384,10 @@
} else if (self.level === 2) {
// For level 2 boss, require 35 slashes to defeat
self.health -= self.maxHealth / 35;
} else if (self.level === 3) {
- // For level 3 boss, require 50 slashes to defeat
+ // For level 3 boss, require 45 slashes to defeat
+ self.health -= self.maxHealth / 45;
} else if (self.level === 4) {
// For level 4 boss, require 50 slashes to defeat
self.health -= self.maxHealth / 50;
} else if (self.level === 5) {
red bomb. In-Game asset. 2d. High contrast. No shadows
Head of pepe meme. each face shaped as a marble shaped face made in blender 3D. In-Game asset. 2d. High contrast. No shadows
Head of doge meme. face shaped as a marble shaped face made in blender 3D. In-Game asset. 2d. High contrast. No shadows
Head of troll face meme. face shaped as a marble shaped face made in blender 3D. In-Game asset. 2d. High contrast. No shadows
Head of think smart guy meme. face shaped as a marble shaped face made in blender 3D. In-Game asset. 2d. High contrast. No shadows
Head of white y u no meme. face shaped as a marble shaped face made in blender 3D. In-Game asset. 2d. High contrast. No shadows
Explosion. In-Game asset. 2d. High contrast. No shadows
Clock. In-Game asset. 3d. High contrast. No shadows
Red Heart. In-Game asset. 3d. High contrast. No shadows
gattling gun. In-Game asset. 2d. High contrast. No shadows