User prompt
zincirde bazen siyah toplar oluyor bunu istemiyorum
User prompt
fire blast topu patlattıktan sonra zincir arasında boşluk kalıyor
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'trackPosition')' in or related to this line: 'ball.trackPosition -= gapSize;' Line Number: 970
User prompt
rotadaki noktalar eşit aralıkta olmalı
User prompt
deliğe girmeden önce zincirin arkası önüne çok yaklaşıyor. arasındaki mesafeyi korumalı
User prompt
toplar deliğe yaklaşınca birbirlerinin üzerine biniyorlar. bunu çözelim. toplar asla üstüste gelmemeli
User prompt
zincirdeki topların arasındaki mesafe vurulmadıkça ve fire ball gelmedikçe hiç değişmemeli oyun sonuna kadar.
User prompt
rota noktaları deliğe doğru birbirine yaklaştığı için, toplar deliğe yaklaştığında yani rota sonunda birbirine giriyor bunu istemiyorum.
User prompt
arka arkaya bir çok top attığımda oyun kasmaya başlıyor
User prompt
topları arka arkaya atınca yine fps düşüyor
User prompt
ses gelmediği gibi, her top attığımda fps düşüyor
User prompt
ses hala gelmiyor
User prompt
ses gelmiyor
User prompt
topu her fırlattığımda topun ucunda ateş efekti ve sesi istiyorum ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'trackPosition')' in or related to this line: 'ball.trackPosition -= gapSize;' Line Number: 842
User prompt
fire ball zincirde değdiği ilk topun sağında ve solunda 3'er topu yok etmeli ve blast yazısı sallanarak ekrana gelmeli belki ekran biraz sallansa (az efektli) güzel olur ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
fire ball topları yok ettikten sonra zincir birleşmeli. tıpkı aynı renk topları yok ettikten sonra birleştiği gibi ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
beyaz top zincire eklenmesin. sadece görevini yapıp kaybolsun. bunun yanında oyuna turuncu ateş topu ekleyelim o da tıpkı beyaz top gibi 10 topta bir restgele gelsin. ve değdiği topun sağından ve solundan 3 topu patlatsın. ekranda da blast patlama efekti olsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
atıcı bir bütün olarak obüs gibi gözüksün
User prompt
sıradaki top atıcının altında yazsın
User prompt
oyundan siyah topu kaldıralım. ve sadece atıcının topları yanıp sönerek büyüyüp küçülsün ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'scaleX' in null' in or related to this line: 'tween(freezeCountdownDisplay, {' Line Number: 663
User prompt
beyaz top zincire girince hiç bir şey olmuyor. dediğim gibi = ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
beyaz top zincirdeki toplara değince zinciri durdurmalı 2 saniye boyunca ve bu 2 saniye ekranda büyükçe yazmalı. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
bu özel toplar, vurucu(shooter) atılacak. her 10 topta bir rastgele beyaz top olsun.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Ball = Container.expand(function (color) { var self = Container.call(this); // Safety check to prevent black balls if (color === 'black') { color = 'red'; // Default to red if black is passed } self.color = color; self.ballColors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange']; self.isSpecial = false; self.specialType = null; // 'explosive', 'freeze', 'rapid', 'white_freeze', 'black_destroyer' var ballGraphics = self.attachAsset('ball_' + color, { anchorX: 0.5, anchorY: 0.5 }); // Check for special ball types based on color if (color === 'white') { self.isSpecial = true; self.specialType = 'white_freeze'; ballGraphics.alpha = 0.9; ballGraphics.scaleX = 1.15; ballGraphics.scaleY = 1.15; // Add pulsing effect for white balls var _pulseEffect = function pulseEffect() { tween(ballGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(ballGraphics, { scaleX: 1.15, scaleY: 1.15 }, { duration: 600, easing: tween.easeInOut, onFinish: _pulseEffect }); } }); }; _pulseEffect(); } else if (color === 'fire') { self.isSpecial = true; self.specialType = 'fire_blast'; ballGraphics.alpha = 0.9; ballGraphics.scaleX = 1.2; ballGraphics.scaleY = 1.2; ballGraphics.tint = 0xFF4500; // Add intense pulsing effect for fire balls var _pulseEffect = function pulseEffect() { tween(ballGraphics, { scaleX: 1.4, scaleY: 1.4, tint: 0xFF0000 }, { duration: 400, easing: tween.easeInOut, onFinish: function onFinish() { tween(ballGraphics, { scaleX: 1.2, scaleY: 1.2, tint: 0xFF4500 }, { duration: 400, easing: tween.easeInOut, onFinish: _pulseEffect }); } }); }; _pulseEffect(); } else { // 5% chance for special balls on regular colors if (Math.random() < 0.05) { // Start pulsing animation var _pulseEffect = function pulseEffect() { tween(ballGraphics, { scaleX: 1.3, scaleY: 1.3 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(ballGraphics, { scaleX: 1.15, scaleY: 1.15 }, { duration: 600, easing: tween.easeInOut, onFinish: _pulseEffect }); } }); }; self.isSpecial = true; var specials = ['explosive', 'freeze', 'rapid']; self.specialType = specials[Math.floor(Math.random() * specials.length)]; ballGraphics.alpha = 0.9; // Add glow effect for special balls ballGraphics.scaleX = 1.15; ballGraphics.scaleY = 1.15; // Add pulsing effect if (self.specialType === 'explosive') { ballGraphics.tint = 0xFF4500; } else if (self.specialType === 'freeze') { ballGraphics.tint = 0x00FFFF; } else if (self.specialType === 'rapid') { ballGraphics.tint = 0xFFFF00; } _pulseEffect(); } } self.trackPosition = 0; self.trackX = 0; self.trackY = 0; self.isMoving = false; return self; }); var ChainBall = Ball.expand(function (color) { var self = Ball.call(this, color); self.chainIndex = 0; self.targetX = 0; self.targetY = 0; self.update = function () { if (self.isMoving) { // Smooth movement towards target position var dx = self.targetX - self.x; var dy = self.targetY - self.y; self.x += dx * 0.3; self.y += dy * 0.3; if (Math.abs(dx) < 1 && Math.abs(dy) < 1) { self.isMoving = false; self.x = self.targetX; self.y = self.targetY; } } }; return self; }); var Shooter = Container.expand(function () { var self = Container.call(this); // Create howitzer base (larger and more prominent) var shooterBase = self.attachAsset('shooter', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.0, scaleY: 1.8 }); shooterBase.tint = 0x4a4a4a; // Darker military color // Add cannon barrel (longer and thicker for howitzer look) var barrel = self.attachAsset('track', { anchorX: 0, anchorY: 0.5, scaleX: 8, scaleY: 2.5, x: 0, y: 0 }); barrel.tint = 0x2a2a2a; // Very dark barrel color // Add muzzle brake at end of barrel var muzzleBrake = self.attachAsset('track', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 3.0, x: barrel.width * barrel.scaleX * 0.95, y: 0 }); muzzleBrake.tint = 0x1a1a1a; // Almost black for muzzle brake self.angle = 0; self.currentBall = null; self.nextBall = null; self.rapidFire = false; self.rapidFireTimer = 0; self.shootCooldown = 0; self.loadBall = function () { if (self.currentBall) { self.currentBall.destroy(); } self.currentBall = self.nextBall; if (self.currentBall) { self.currentBall.x = 0; self.currentBall.y = -40; self.addChild(self.currentBall); // Add pulsing and shining animation to current ball var _pulseCurrentBall = function pulseCurrentBall() { if (self.currentBall && self.currentBall.parent) { tween(self.currentBall, { scaleX: 1.2, scaleY: 1.2 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (self.currentBall && self.currentBall.parent) { tween(self.currentBall, { scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeInOut, onFinish: _pulseCurrentBall }); } } }); } }; _pulseCurrentBall(); // Add shining effect with alpha animation var _shineCurrentBall = function shineCurrentBall() { if (self.currentBall && self.currentBall.parent) { tween(self.currentBall, { alpha: 0.7 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { if (self.currentBall && self.currentBall.parent) { tween(self.currentBall, { alpha: 1.0 }, { duration: 600, easing: tween.easeInOut, onFinish: _shineCurrentBall }); } } }); } }; _shineCurrentBall(); } // Generate next ball var colors = ['red', 'blue', 'green', 'yellow', 'purple']; var randomColor; // Check if it's time for a special ball (every 10 shots) if (shotCounter > 0 && shotCounter % 10 === 0) { // Randomly choose between white freeze ball or fire blast ball if (Math.random() < 0.5) { randomColor = 'white'; // White freeze ball } else { randomColor = 'fire'; // Fire blast ball } } else { randomColor = colors[Math.floor(Math.random() * colors.length)]; } // Safety check to ensure we never create black balls if (randomColor === 'black' || !randomColor) { randomColor = colors[Math.floor(Math.random() * colors.length)]; // Default to valid colors } self.nextBall = new Ball(randomColor); }; self.aimAt = function (x, y) { var dx = x - self.x; var dy = y - self.y; self.angle = Math.atan2(dy, dx); self.rotation = self.angle; }; self.shoot = function () { if (self.shootCooldown > 0 || !self.currentBall) return null; var ball = self.currentBall; self.removeChild(ball); // Set ball velocity var speed = 8; ball.vx = Math.cos(self.angle) * speed; ball.vy = Math.sin(self.angle) * speed; ball.x = self.x; ball.y = self.y; // Increment shot counter shotCounter++; self.loadBall(); // Create optimized fire effect only if under limit and not in rapid fire if (activeFireEffects.length < maxFireEffects && !self.rapidFire) { var fireEffect = getFireEffect(); game.addChild(fireEffect); activeFireEffects.push(fireEffect); // Position fire effect at the end of the barrel var barrelEndX = self.x + Math.cos(self.angle) * 120; var barrelEndY = self.y + Math.sin(self.angle) * 120; fireEffect.x = barrelEndX; fireEffect.y = barrelEndY; fireEffect.rotation = self.angle; // Animate fire effect - expand and fade out quickly tween(fireEffect, { scaleX: 1.2, scaleY: 1.2, alpha: 0 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { returnFireEffect(fireEffect); } }); } // Set cooldown self.shootCooldown = self.rapidFire ? 10 : 20; // Play shoot sound immediately try { LK.getSound('shoot').play(); } catch (e) { console.log('Sound play error:', e); } return ball; }; self.update = function () { if (self.shootCooldown > 0) { self.shootCooldown--; } if (self.rapidFire) { self.rapidFireTimer--; if (self.rapidFireTimer <= 0) { self.rapidFire = false; // Remove glow effect tween.stop(self, { tint: true }); self.tint = 0xFFFFFF; } else { // Maintain glow effect if (self.tint === 0xFFFFFF) { tween(self, { tint: 0xFFFF88 }, { duration: 200, easing: tween.easeInOut }); } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x0a0a1a }); /**** * Game Code ****/ // Fire effect pool management function getFireEffect() { var fireEffect; if (fireEffectPool.length > 0) { fireEffect = fireEffectPool.pop(); fireEffect.alpha = 0.9; fireEffect.scaleX = 0.8; fireEffect.scaleY = 0.8; tween.stop(fireEffect); // Stop any ongoing animations } else { fireEffect = LK.getAsset('fire_effect', { anchorX: 0.5, anchorY: 0.5 }); } return fireEffect; } function returnFireEffect(fireEffect) { if (fireEffect && fireEffect.parent) { fireEffect.parent.removeChild(fireEffect); } var index = activeFireEffects.indexOf(fireEffect); if (index > -1) { activeFireEffects.splice(index, 1); } // Reset fire effect properties to prevent animation conflicts fireEffect.alpha = 0.9; fireEffect.scaleX = 0.8; fireEffect.scaleY = 0.8; fireEffect.rotation = 0; tween.stop(fireEffect); // Stop any ongoing animations if (fireEffectPool.length < maxPoolSize) { // Limit pool size to prevent memory buildup fireEffectPool.push(fireEffect); } } // Create dynamic space background with shooting stars function createSpaceBackground() { // Create complex starfield with realistic star colors and sizes (no colored squares) var starColors = [0xFFFFFF, 0xFFE4B5, 0xFFB347, 0x87CEEB, 0xF0F8FF, 0xFFF8DC]; var starSizes = [0.5, 0.8, 1.2, 1.8, 2.5, 3.2]; for (var layer = 0; layer < 5; layer++) { var numStars = layer === 0 ? 200 : layer === 1 ? 150 : layer === 2 ? 100 : layer === 3 ? 60 : 30; var baseAlpha = layer === 0 ? 0.2 : layer === 1 ? 0.4 : layer === 2 ? 0.6 : layer === 3 ? 0.8 : 1.0; for (var i = 0; i < numStars; i++) { var starSize = starSizes[Math.floor(Math.random() * starSizes.length)]; var star = game.addChild(LK.getAsset('track', { anchorX: 0.5, anchorY: 0.5, scaleX: starSize * 0.1, scaleY: starSize * 0.1 })); star.x = Math.random() * 2048; star.y = Math.random() * 2732; star.alpha = baseAlpha + Math.random() * 0.3; star.tint = starColors[Math.floor(Math.random() * starColors.length)]; // Add realistic twinkling based on star size and distance if (layer >= 2 && Math.random() < 0.4) { var twinkleDelay = Math.random() * 3000; LK.setTimeout(function () { var _twinkleEffect = function twinkleEffect() { if (star && star.parent) { tween(star, { alpha: star.alpha * 0.2 }, { duration: 1000 + Math.random() * 800, easing: tween.easeInOut, onFinish: function onFinish() { if (star && star.parent) { tween(star, { alpha: baseAlpha + Math.random() * 0.3 }, { duration: 1000 + Math.random() * 800, easing: tween.easeInOut, onFinish: _twinkleEffect }); } } }); } }; _twinkleEffect(); }, twinkleDelay); } } } // Create animated cosmic dust clouds with ellipse shapes only for (var i = 0; i < 12; i++) { var dust = game.addChild(LK.getAsset('hole', { anchorX: 0.5, anchorY: 0.5, scaleX: 2 + Math.random() * 3, scaleY: 1 + Math.random() * 2 })); dust.x = Math.random() * 2048; dust.y = Math.random() * 2732; dust.alpha = 0.03 + Math.random() * 0.05; dust.rotation = Math.random() * Math.PI * 2; // Realistic dust cloud colors var dustColors = [0x4B0082, 0x800080, 0x8B008B, 0x9400D3, 0x1E90FF, 0x00CED1, 0x483D8B]; dust.tint = dustColors[Math.floor(Math.random() * dustColors.length)]; // Add slow rotation animation var rotationSpeed = (Math.random() - 0.5) * 0.001; var dustRotation = dust.rotation; var _dustRotate = function dustRotate() { if (dust && dust.parent) { dustRotation += rotationSpeed; dust.rotation = dustRotation; LK.setTimeout(_dustRotate, 50); } }; _dustRotate(); } // Add some brighter nebula formations with ellipse shapes for (var i = 0; i < 6; i++) { var nebula = game.addChild(LK.getAsset('hole', { anchorX: 0.5, anchorY: 0.5, scaleX: 4 + Math.random() * 5, scaleY: 3 + Math.random() * 4 })); nebula.x = Math.random() * 2048; nebula.y = Math.random() * 2732; nebula.alpha = 0.08 + Math.random() * 0.12; nebula.rotation = Math.random() * Math.PI * 2; // More vibrant nebula colors var nebulaColors = [0xFF1493, 0x00FA9A, 0xFF4500, 0x9370DB, 0x00CED1, 0xFF69B4]; nebula.tint = nebulaColors[Math.floor(Math.random() * nebulaColors.length)]; // Add gentle pulsing effect var pulseDelay = Math.random() * 4000; LK.setTimeout(function () { var _nebulaEffect = function nebulaEffect() { if (nebula && nebula.parent) { tween(nebula, { alpha: nebula.alpha * 0.5, scaleX: nebula.scaleX * 1.1, scaleY: nebula.scaleY * 1.1 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: function onFinish() { if (nebula && nebula.parent) { tween(nebula, { alpha: 0.08 + Math.random() * 0.12, scaleX: 4 + Math.random() * 5, scaleY: 3 + Math.random() * 4 }, { duration: 2000 + Math.random() * 1000, easing: tween.easeInOut, onFinish: _nebulaEffect }); } } }); } }; _nebulaEffect(); }, pulseDelay); } } // Initialize space background createSpaceBackground(); // Create shooting star function function createShootingStar() { var shootingStar = game.addChild(LK.getAsset('track', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.1 })); // Random starting position from top or sides var startSide = Math.floor(Math.random() * 3); // 0 = top, 1 = left, 2 = right if (startSide === 0) { shootingStar.x = Math.random() * 2048; shootingStar.y = -50; } else if (startSide === 1) { shootingStar.x = -50; shootingStar.y = Math.random() * 1500; } else { shootingStar.x = 2098; shootingStar.y = Math.random() * 1500; } // Set shooting star properties shootingStar.alpha = 0.8; shootingStar.tint = 0xFFFFFF; shootingStar.rotation = Math.random() * Math.PI * 2; // Create trail effect with multiple small stars var trailStars = []; for (var i = 0; i < 5; i++) { var trailStar = game.addChild(LK.getAsset('track', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1 - i * 0.015, scaleY: 0.05 - i * 0.008 })); trailStar.x = shootingStar.x; trailStar.y = shootingStar.y; trailStar.alpha = 0.6 - i * 0.1; trailStar.tint = 0xFFFFFF; trailStar.rotation = shootingStar.rotation; trailStars.push(trailStar); } // Calculate movement direction var targetX = Math.random() * 2048; var targetY = 2732 + 200; var deltaX = targetX - shootingStar.x; var deltaY = targetY - shootingStar.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); var speed = 15 + Math.random() * 10; var vx = deltaX / distance * speed; var vy = deltaY / distance * speed; // Animate shooting star var _moveShootingStar = function moveShootingStar() { if (shootingStar && shootingStar.parent) { shootingStar.x += vx; shootingStar.y += vy; // Update trail positions for (var i = trailStars.length - 1; i > 0; i--) { if (trailStars[i] && trailStars[i].parent) { trailStars[i].x = trailStars[i - 1].x; trailStars[i].y = trailStars[i - 1].y; } } if (trailStars[0] && trailStars[0].parent) { trailStars[0].x = shootingStar.x; trailStars[0].y = shootingStar.y; } // Check if off screen if (shootingStar.x < -100 || shootingStar.x > 2148 || shootingStar.y > 2832) { shootingStar.destroy(); for (var i = 0; i < trailStars.length; i++) { if (trailStars[i] && trailStars[i].parent) { trailStars[i].destroy(); } } return; } LK.setTimeout(_moveShootingStar, 16); } }; _moveShootingStar(); } // Start shooting star timer (every 10 seconds) var shootingStarTimer = LK.setInterval(createShootingStar, 10000); // Game variables var chain = []; var flyingBalls = []; var shooter; var trackPoints = []; var chainSpeed = 0.0003; // Further decreased normal chain speed var normalChainSpeed = 0.0003; // Store normal speed var fastChainSpeed = 0.002; // Fast chain speed - slower than before var veryFastChainSpeed = 0.006; // Very fast chain speed - slower than before var speedLevel = 0; // 0 = normal, 1 = fast, 2 = very fast var chainFrozen = false; var freezeTimer = 0; var freezeCountdownDisplay = null; var level = 1; var gameWon = false; var gameLost = false; // Score multiplier system var scoreMultipliers = [1.0, 1.5, 2.0]; // normal, fast, very fast var pointsDisplays = []; // Array to track active point displays var shotCounter = 0; // Counter to track shots fired // Fire effect optimization var fireEffectPool = []; // Pool of reusable fire effects var activeFireEffects = []; // Currently active fire effects var maxFireEffects = 2; // Reduced maximum concurrent fire effects var maxPoolSize = 3; // Limit pool size to prevent memory buildup // Create vortex track path function createTrackPath() { trackPoints = []; var centerX = 1024; var centerY = 1366; var holeX = 1024; var holeY = 1200; // Hole position - closer to shooter var segments = 1500; // Longer track for better gameplay var targetSpacing = 7.0; // Target distance between consecutive points var currentDistance = 0; // Generate initial spiral path points (more densely packed) var tempPoints = []; var densityMultiplier = 3; // Create 3x more points initially for smoother curves for (var i = 0; i < segments * densityMultiplier; i++) { var progress = i / (segments * densityMultiplier); // Single spiral angle - creates one continuous swirl towards center var spiralAngle = progress * Math.PI * 6; // 3 full rotations for longer swirling path // Radius decreases linearly for smooth inward movement var baseRadius = 1000 * (1 - progress); // Linear decrease for steady approach to center // Add gentle wave motion for swirling effect var waveMotion = Math.sin(progress * Math.PI * 12) * (60 * (1 - progress)); // Gentle swirling motion var currentRadius = baseRadius + waveMotion; // Ensure minimum radius for playability at center currentRadius = Math.max(currentRadius, 40); // Calculate position on spiral path - interpolate towards hole position var spiralX = centerX + Math.cos(spiralAngle) * currentRadius; var spiralY = centerY + Math.sin(spiralAngle) * currentRadius; // Gradually move towards hole position in final segment var holeProgress = Math.max(0, (progress - 0.9) / 0.1); // Last 10% of track moves to hole var x = spiralX + (holeX - spiralX) * holeProgress; var y = spiralY + (holeY - spiralY) * holeProgress; // Ensure the path stays within screen bounds with padding x = Math.max(150, Math.min(1898, x)); y = Math.max(300, Math.min(2600, y)); tempPoints.push({ x: x, y: y }); } // Now resample the path to ensure equal spacing trackPoints.push(tempPoints[0]); // Add first point currentDistance = 0; for (var i = 1; i < tempPoints.length; i++) { var prevPoint = tempPoints[i - 1]; var currPoint = tempPoints[i]; var segmentLength = Math.sqrt(Math.pow(currPoint.x - prevPoint.x, 2) + Math.pow(currPoint.y - prevPoint.y, 2)); currentDistance += segmentLength; // Add point when we've traveled the target spacing distance if (currentDistance >= targetSpacing) { // Interpolate to get exact position at target spacing var excess = currentDistance - targetSpacing; var ratio = excess / segmentLength; var adjustedX = currPoint.x - (currPoint.x - prevPoint.x) * ratio; var adjustedY = currPoint.y - (currPoint.y - prevPoint.y) * ratio; trackPoints.push({ x: adjustedX, y: adjustedY }); currentDistance = excess; // Carry over the excess distance } } // Create visual track markers with vortex effect for (var i = 0; i < trackPoints.length; i += 6) { var trackMarker = game.addChild(LK.getAsset('track', { anchorX: 0.5, anchorY: 0.5 })); trackMarker.x = trackPoints[i].x; trackMarker.y = trackPoints[i].y; trackMarker.alpha = 0.3 + i / trackPoints.length * 0.3; // Fade in towards center // Scale markers smaller as they approach center for vortex effect var scaleProgress = i / trackPoints.length; trackMarker.scaleX = 0.5 + (1 - scaleProgress) * 0.5; trackMarker.scaleY = 0.5 + (1 - scaleProgress) * 0.5; } } // Create hole at end of track var hole = game.addChild(LK.getAsset('hole', { anchorX: 0.5, anchorY: 0.5 })); // Create shooter shooter = game.addChild(new Shooter()); shooter.x = 1024; // Center of screen horizontally (2048/2 = 1024) shooter.y = 1366; // Position near the center hole // Initialize first balls shooter.loadBall(); shooter.loadBall(); // Create initial chain function createChain() { chain = []; var chainLength = 15 + level * 5; var colors = ['red', 'blue', 'green', 'yellow', 'purple']; var ballSpacing = 50; // Space between balls - balls touch each other var trackSpacing = 5.0; // Increased track position spacing to prevent overlap // Ensure better color distribution by cycling through colors for (var i = 0; i < chainLength; i++) { // Use a mix of sequential and random distribution var colorIndex; if (i % 8 < 5) { // First 5 of every 8 balls use sequential colors colorIndex = i % colors.length; } else { // Last 3 of every 8 balls use random colors colorIndex = Math.floor(Math.random() * colors.length); } var color = colors[colorIndex]; // Safety check to ensure we never create black balls if (color === 'black' || !color) { color = colors[i % colors.length]; // Default to cycling through valid colors } var ball = game.addChild(new ChainBall(color)); ball.chainIndex = i; // Initialize track position with consistent spacing for vortex ball.trackPosition = i * 7.0; // Use consistent spacing to prevent overlap // Position ball immediately on track positionBallOnTrack(ball, ball.trackPosition); chain.push(ball); } } // Position ball on track function positionBallOnTrack(ball, trackPosition) { if (trackPosition >= trackPoints.length || trackPoints.length === 0) { if (trackPoints.length > 0) { ball.x = trackPoints[trackPoints.length - 1].x; ball.y = trackPoints[trackPoints.length - 1].y; } else { ball.x = 1024; ball.y = 1366; } return; } var pointIndex = Math.floor(Math.max(0, trackPosition)); var nextIndex = Math.min(pointIndex + 1, trackPoints.length - 1); var t = trackPosition - pointIndex; var point1 = trackPoints[pointIndex]; var point2 = trackPoints[nextIndex]; // Add safety check for undefined points if (!point1 || !point2) { ball.x = 1024; ball.y = 1366; ball.isMoving = false; return; } // Calculate position with anti-overlap adjustment near the hole var baseX = point1.x + (point2.x - point1.x) * t; var baseY = point1.y + (point2.y - point1.y) * t; // Apply spacing adjustment to prevent overlap near hole var progressToHole = trackPosition / trackPoints.length; if (progressToHole > 0.8) { // Near the hole (last 20% of track) // Add slight offset based on ball's chain position to prevent stacking var offsetAngle = ball.chainIndex * 0.3; // Small angular offset per ball var offsetRadius = 15 * (progressToHole - 0.8) * 5; // Increase offset near hole baseX += Math.cos(offsetAngle) * offsetRadius; baseY += Math.sin(offsetAngle) * offsetRadius; } ball.targetX = baseX; ball.targetY = baseY; ball.isMoving = true; } // Update chain positions function updateChain() { if (chainFrozen) { freezeTimer--; // Update freeze countdown display if (freezeCountdownDisplay) { var secondsLeft = Math.ceil(freezeTimer / 60); freezeCountdownDisplay.setText('FROZEN: ' + secondsLeft); // Add pulsing effect each second if (freezeTimer % 60 === 0) { tween(freezeCountdownDisplay, { scaleX: 1.3, scaleY: 1.3 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { if (freezeCountdownDisplay) { tween(freezeCountdownDisplay, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200, easing: tween.easeIn }); } } }); } } if (freezeTimer <= 0) { chainFrozen = false; // Remove countdown display if (freezeCountdownDisplay) { freezeCountdownDisplay.destroy(); freezeCountdownDisplay = null; } // Remove ice tint from all chain balls for (var i = 0; i < chain.length; i++) { var chainBall = chain[i]; tween(chainBall, { tint: 0xFFFFFF }, { duration: 300 }); } } return; } // Move all balls forward and maintain equal spacing for vortex var trackSpacing = 7.0; // Increased spacing to prevent overlap, especially near hole for (var i = 0; i < chain.length; i++) { var ball = chain[i]; // Move chain forward in vortex pattern ball.trackPosition += chainSpeed; // Check if reached hole (vortex center) if (ball.trackPosition >= trackPoints.length - 1) { gameLost = true; LK.showGameOver(); return; } } // Maintain equal spacing between balls for vortex movement with collision avoidance for (var i = 0; i < chain.length; i++) { var ball = chain[i]; // Ensure proper spacing from previous ball if (i > 0) { var previousBall = chain[i - 1]; var expectedPosition = previousBall.trackPosition + trackSpacing; // Enforce minimum spacing to prevent overlap ball.trackPosition = Math.max(ball.trackPosition, expectedPosition); } positionBallOnTrack(ball, ball.trackPosition); } // Additional collision avoidance pass to prevent balls from overlapping for (var i = 0; i < chain.length; i++) { var ball = chain[i]; for (var j = i + 1; j < chain.length; j++) { var otherBall = chain[j]; var dx = ball.x - otherBall.x; var dy = ball.y - otherBall.y; var distance = Math.sqrt(dx * dx + dy * dy); var minDistance = 50; // Minimum distance between ball centers if (distance < minDistance && distance > 0) { // Calculate separation needed var separation = minDistance - distance; var separationX = dx / distance * separation * 0.5; var separationY = dy / distance * separation * 0.5; // Move balls apart by adjusting their track positions var trackDelta = separation / trackSpacing; if (i > 0) { // Move current ball back ball.trackPosition -= trackDelta * 0.5; // Reposition on track positionBallOnTrack(ball, ball.trackPosition); } if (j < chain.length - 1) { // Move other ball forward otherBall.trackPosition += trackDelta * 0.5; // Reposition on track positionBallOnTrack(otherBall, otherBall.trackPosition); } } } } } // Check for matches function checkMatches() { var matches = []; var currentColor = null; var currentMatch = []; // First, ensure all balls are positioned correctly on the track for (var i = 0; i < chain.length; i++) { var ball = chain[i]; positionBallOnTrack(ball, ball.trackPosition); } // Check for consecutive balls of the same color for (var i = 0; i < chain.length; i++) { var ball = chain[i]; if (ball.color === currentColor) { currentMatch.push(ball); } else { if (currentMatch.length >= 3) { matches.push(currentMatch.slice()); } currentColor = ball.color; currentMatch = [ball]; } } // Check last group if (currentMatch.length >= 3) { matches.push(currentMatch.slice()); } // Remove matches for (var m = 0; m < matches.length; m++) { var match = matches[m]; var baseScore = match.length * 10; var multiplier = scoreMultipliers[speedLevel]; var score = Math.floor(baseScore * multiplier); // Calculate display position (center of the match) var displayX = 0; var displayY = 0; for (var b = 0; b < match.length; b++) { displayX += match[b].x; displayY += match[b].y; } displayX /= match.length; displayY /= match.length; // Display flashing points displayPoints(score, displayX, displayY); // Check for special balls for (var b = 0; b < match.length; b++) { var ball = match[b]; if (ball.isSpecial) { handleSpecialBall(ball); } } // Store the first index of the match to know where the gap starts var firstMatchIndex = chain.indexOf(match[0]); var trackSpacing = 7.0; // Increased spacing to prevent overlap var gapSize = match.length * trackSpacing; // Calculate gap size based on track spacing // Remove matched balls for (var b = 0; b < match.length; b++) { var ball = match[b]; var index = chain.indexOf(ball); if (index > -1) { chain.splice(index, 1); ball.destroy(); } } // Close the gap by moving all balls after the removed section forward for (var i = firstMatchIndex; i < chain.length; i++) { var ball = chain[i]; // Move the ball's track position forward to close the gap ball.trackPosition -= gapSize; } // Re-space all balls to maintain equal intervals after gap closing for (var i = 1; i < chain.length; i++) { var previousBall = chain[i - 1]; var currentBall = chain[i]; var expectedPosition = previousBall.trackPosition + trackSpacing; // Enforce minimum spacing to prevent overlap currentBall.trackPosition = Math.max(currentBall.trackPosition, expectedPosition); // Position the ball immediately to detect matches faster positionBallOnTrack(currentBall, currentBall.trackPosition); // Animate the ball to its new position smoothly tween(currentBall, { x: currentBall.targetX, y: currentBall.targetY }, { duration: 200, // Faster animation for quicker match detection easing: tween.easeOut }); } LK.setScore(LK.getScore() + score); LK.getSound('match').play(); // Pull chain back slightly when balls are removed chainSpeed = Math.max(0.5, chainSpeed - 0.1); } // Always check for new matches after gap closure, regardless of previous matches // This ensures balls disappear when they touch each other after gap closure if (matches.length > 0) { // After removing matches and closing gaps, check for new matches that may have formed // Use a timeout to allow animations to settle before checking LK.setTimeout(function () { checkMatches(); }, 50); // Reduced timeout for faster response } else { // Even if no matches were found initially, check again after any repositioning // This handles cases where balls of the same color touch after repositioning var hasRepositioned = false; for (var i = 1; i < chain.length; i++) { var prevBall = chain[i - 1]; var currBall = chain[i]; if (prevBall.color === currBall.color) { // Check if these balls are close enough to be considered touching var distance = Math.sqrt(Math.pow(currBall.x - prevBall.x, 2) + Math.pow(currBall.y - prevBall.y, 2)); if (distance < 60) { // Balls are touching hasRepositioned = true; break; } } } if (hasRepositioned) { LK.setTimeout(function () { checkMatches(); }, 50); } } // Check win condition if (chain.length === 0) { gameWon = true; level++; LK.showYouWin(); } } // Display flashing points function displayPoints(points, x, y) { var pointsText = new Text2('+' + points, { size: 80, fill: 0xFFFF00 }); pointsText.anchor.set(0.5, 0.5); pointsText.x = x; pointsText.y = y; pointsText.alpha = 1.0; pointsText.scaleX = 0.5; pointsText.scaleY = 0.5; game.addChild(pointsText); pointsDisplays.push(pointsText); // Animate the points display tween(pointsText, { scaleX: 1.2, scaleY: 1.2, y: y - 100 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { tween(pointsText, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { pointsText.destroy(); var index = pointsDisplays.indexOf(pointsText); if (index > -1) { pointsDisplays.splice(index, 1); } } }); } }); } // Handle special ball effects function handleSpecialBall(ball) { if (ball.specialType === 'explosive') { var _shakeEffect = function shakeEffect() { if (shakeTimer < shakeDuration) { game.x = originalX + (Math.random() - 0.5) * shakeIntensity; game.y = originalY + (Math.random() - 0.5) * shakeIntensity; shakeTimer += 16; LK.setTimeout(_shakeEffect, 16); } else { game.x = originalX; game.y = originalY; } }; // Remove nearby balls var explosionRadius = 3; var ballIndex = chain.indexOf(ball); var toRemove = []; for (var i = Math.max(0, ballIndex - explosionRadius); i < Math.min(chain.length, ballIndex + explosionRadius + 1); i++) { if (chain[i] !== ball) { toRemove.push(chain[i]); } } for (var i = 0; i < toRemove.length; i++) { var index = chain.indexOf(toRemove[i]); if (index > -1) { chain.splice(index, 1); toRemove[i].destroy(); } } LK.effects.flashScreen(0xff8000, 300); // Add screen shake effect var originalX = game.x; var originalY = game.y; var shakeIntensity = 10; var shakeDuration = 300; var shakeTimer = 0; _shakeEffect(); } else if (ball.specialType === 'freeze') { chainFrozen = true; freezeTimer = 180; // 3 seconds at 60fps LK.effects.flashScreen(0x00ffff, 500); // Add ice tint to all chain balls for (var i = 0; i < chain.length; i++) { var chainBall = chain[i]; tween(chainBall, { tint: 0x88DDFF }, { duration: 300 }); } } else if (ball.specialType === 'rapid') { shooter.rapidFire = true; shooter.rapidFireTimer = 300; // 5 seconds at 60fps LK.effects.flashScreen(0xffff00, 200); } else if (ball.specialType === 'white_freeze') { chainFrozen = true; freezeTimer = 120; // 2 seconds at 60fps LK.effects.flashScreen(0xffffff, 500); // Create freeze countdown display if (freezeCountdownDisplay) { freezeCountdownDisplay.destroy(); } freezeCountdownDisplay = new Text2('FROZEN: 2', { size: 150, fill: 0x00FFFF }); freezeCountdownDisplay.anchor.set(0.5, 0.5); LK.gui.center.addChild(freezeCountdownDisplay); // Add pulsing animation to the countdown display tween(freezeCountdownDisplay, { scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.easeInOut, onFinish: function onFinish() { tween(freezeCountdownDisplay, { scaleX: 1.0, scaleY: 1.0 }, { duration: 300, easing: tween.easeInOut }); } }); // Add ice tint to all chain balls for (var i = 0; i < chain.length; i++) { var chainBall = chain[i]; tween(chainBall, { tint: 0x88DDFF }, { duration: 300 }); } } else if (ball.specialType === 'fire_blast') { // Fire blast effect - explode 3 balls on each side var ballIndex = chain.indexOf(ball); var blastRadius = 3; var toRemove = []; // Get 3 balls to the left and 3 balls to the right for (var i = Math.max(0, ballIndex - blastRadius); i < Math.min(chain.length, ballIndex + blastRadius + 1); i++) { if (chain[i] !== ball) { toRemove.push(chain[i]); } } // Store the first index of the blast to know where the gap starts var firstBlastIndex = Math.max(0, ballIndex - blastRadius); var trackSpacing = 7.0; // Track spacing to prevent overlap var gapSize = toRemove.length * trackSpacing; // Calculate gap size based on track spacing // Remove blasted balls for (var i = 0; i < toRemove.length; i++) { var index = chain.indexOf(toRemove[i]); if (index > -1) { chain.splice(index, 1); toRemove[i].destroy(); } } // Close the gap by moving all balls after the removed section forward for (var i = firstBlastIndex; i < chain.length; i++) { var ball = chain[i]; // Add safety check for undefined ball and trackPosition if (ball && ball.trackPosition !== undefined) { // Move the ball's track position forward to close the gap ball.trackPosition -= gapSize; } } // Comprehensive re-spacing to ensure no gaps remain // Start from the beginning and ensure each ball has proper spacing for (var i = 0; i < chain.length; i++) { var ball = chain[i]; if (i === 0) { // First ball keeps its current position as reference continue; } var previousBall = chain[i - 1]; var expectedPosition = previousBall.trackPosition + trackSpacing; // Always enforce proper spacing, moving balls forward to close any gaps ball.trackPosition = expectedPosition; // Position the ball immediately to detect matches faster positionBallOnTrack(ball, ball.trackPosition); // Animate the ball to its new position smoothly tween(ball, { x: ball.targetX, y: ball.targetY }, { duration: 200, // Faster animation for quicker match detection easing: tween.easeOut }); } // Create blast explosion effect LK.effects.flashScreen(0xFF4500, 500); // Add screen shake effect var originalX = game.x; var originalY = game.y; var shakeIntensity = 15; var shakeDuration = 400; var shakeTimer = 0; var _shakeEffect = function shakeEffect() { if (shakeTimer < shakeDuration) { game.x = originalX + (Math.random() - 0.5) * shakeIntensity; game.y = originalY + (Math.random() - 0.5) * shakeIntensity; shakeTimer += 16; LK.setTimeout(_shakeEffect, 16); } else { game.x = originalX; game.y = originalY; } }; _shakeEffect(); // Create blast text effect with swinging animation var blastText = new Text2('BLAST!', { size: 120, fill: 0xFF4500 }); blastText.anchor.set(0.5, 0.5); blastText.x = ball.x; blastText.y = ball.y; blastText.scaleX = 0.5; blastText.scaleY = 0.5; blastText.rotation = -0.3; // Start with slight rotation game.addChild(blastText); // Animate blast text with swinging motion tween(blastText, { scaleX: 2.0, scaleY: 2.0, alpha: 0.8, rotation: 0.3 // Swing to the other side }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(blastText, { alpha: 0, scaleX: 1.5, scaleY: 1.5, rotation: -0.2 // Swing back slightly }, { duration: 400, easing: tween.easeIn, onFinish: function onFinish() { blastText.destroy(); } }); } }); // Check for new matches after gap closure LK.setTimeout(function () { checkMatches(); }, 250); // Allow time for animations to settle } LK.getSound('powerup').play(); } // Insert ball into chain function insertBallIntoChain(ball, insertIndex) { var chainBall = new ChainBall(ball.color); chainBall.isSpecial = ball.isSpecial; chainBall.specialType = ball.specialType; chainBall.x = ball.x; chainBall.y = ball.y; chainBall.scaleX = 0.1; chainBall.scaleY = 0.1; game.addChild(chainBall); // Animate ball insertion tween(chainBall, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); chain.splice(insertIndex, 0, chainBall); // Update chain indices for (var i = 0; i < chain.length; i++) { chain[i].chainIndex = i; } checkMatches(); } // Initialize game createTrackPath(); createChain(); // Position hole at the end of the track - near the shooter hole.x = 1024; hole.y = 1200; // Position hole closer to shooter // Score display var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Level display var levelTxt = new Text2('Level: 1', { size: 50, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); levelTxt.x = 120; levelTxt.y = 20; LK.gui.top.addChild(levelTxt); // Next ball preview var nextBallTxt = new Text2('Next:', { size: 40, fill: 0xFFFFFF }); nextBallTxt.anchor.set(1, 0); LK.gui.topRight.addChild(nextBallTxt); // Create accelerate button var accelerateBtn = new Text2('NORMAL', { size: 50, fill: 0xFFFFFF }); accelerateBtn.anchor.set(1, 1); accelerateBtn.x = -20; accelerateBtn.y = -20; LK.gui.bottomRight.addChild(accelerateBtn); // Button press handler accelerateBtn.down = function (x, y, obj) { speedLevel = (speedLevel + 1) % 3; // Cycle through 0, 1, 2 if (speedLevel === 0) { accelerateBtn.setText('NORMAL'); accelerateBtn.fill = 0xFFFFFF; } else if (speedLevel === 1) { accelerateBtn.setText('FAST'); accelerateBtn.fill = 0xFFFF00; } else { accelerateBtn.setText('VERY FAST'); accelerateBtn.fill = 0xFF4444; } }; // Game input game.down = function (x, y, obj) { shooter.aimAt(x, y); var ball = shooter.shoot(); if (ball) { flyingBalls.push(ball); game.addChild(ball); } }; // Main game loop game.update = function () { if (gameWon || gameLost) { // Clean up fire effects when game ends for (var i = 0; i < activeFireEffects.length; i++) { if (activeFireEffects[i] && activeFireEffects[i].parent) { tween.stop(activeFireEffects[i]); // Stop animations activeFireEffects[i].parent.removeChild(activeFireEffects[i]); } } activeFireEffects = []; // Clean up flying balls for (var i = 0; i < flyingBalls.length; i++) { flyingBalls[i].destroy(); } flyingBalls = []; return; } // Update chain updateChain(); // Update flying balls - limit processing if too many var maxFlyingBalls = 5; // Prevent too many balls in flight if (flyingBalls.length > maxFlyingBalls) { // Remove oldest balls if too many in flight var oldestBall = flyingBalls.shift(); oldestBall.destroy(); } for (var i = flyingBalls.length - 1; i >= 0; i--) { var ball = flyingBalls[i]; ball.x += ball.vx; ball.y += ball.vy; // Check collision with chain - optimized detection var inserted = false; var collisionIndex = -1; var ballRadius = 25; // Approximate ball radius // Only check balls within reasonable distance for (var j = 0; j < chain.length; j++) { var chainBall = chain[j]; // Quick distance check before expensive intersects call var dx = ball.x - chainBall.x; var dy = ball.y - chainBall.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < ballRadius * 2 && ball.intersects(chainBall)) { collisionIndex = j; break; } } // If collision detected, handle special balls or insert into chain if (collisionIndex !== -1) { // Check if the flying ball is a white ball - trigger freeze effect and disappear if (ball.color === 'white' && ball.isSpecial && ball.specialType === 'white_freeze') { handleSpecialBall(ball); ball.destroy(); flyingBalls.splice(i, 1); inserted = true; } else if (ball.color === 'fire' && ball.isSpecial && ball.specialType === 'fire_blast') { // Fire ball - trigger blast effect on the first ball it hits var hitBall = chain[collisionIndex]; // Temporarily set the hit ball as the fire ball for the blast effect hitBall.isSpecial = true; hitBall.specialType = 'fire_blast'; handleSpecialBall(hitBall); ball.destroy(); flyingBalls.splice(i, 1); inserted = true; } else { // Regular ball - insert into chain with collision avoidance var insertIndex = collisionIndex + 1; var trackSpacing = 7.0; // Increased spacing to prevent overlap // Create more space by pushing balls further back var pushBackDistance = trackSpacing * 1.5; // Extra space to prevent overlap // Adjust track positions for all balls after insertion point for (var k = insertIndex; k < chain.length; k++) { chain[k].trackPosition += pushBackDistance; // Move balls back to make space } insertBallIntoChain(ball, insertIndex); // Set the inserted ball's track position with proper spacing if (insertIndex < chain.length) { chain[insertIndex].trackPosition = chain[collisionIndex].trackPosition + trackSpacing; positionBallOnTrack(chain[insertIndex], chain[insertIndex].trackPosition); } // Re-space all balls after insertion to maintain equal intervals with extra spacing for (var k = insertIndex + 1; k < chain.length; k++) { var expectedPosition = chain[k - 1].trackPosition + trackSpacing; // Enforce minimum spacing to prevent overlap with buffer chain[k].trackPosition = Math.max(chain[k].trackPosition, expectedPosition + 1.0); } // Additional pass to ensure no overlapping after insertion for (var k = 0; k < chain.length - 1; k++) { var currentBall = chain[k]; var nextBall = chain[k + 1]; var dx = currentBall.x - nextBall.x; var dy = currentBall.y - nextBall.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 50) { // Push next ball further back nextBall.trackPosition += (50 - distance) / trackSpacing; positionBallOnTrack(nextBall, nextBall.trackPosition); } } ball.destroy(); flyingBalls.splice(i, 1); inserted = true; } } // Remove if off screen if (!inserted && (ball.x < -100 || ball.x > 2148 || ball.y < -100 || ball.y > 2832)) { ball.destroy(); flyingBalls.splice(i, 1); } } // Update shooter shooter.update(); // Update UI scoreTxt.setText('Score: ' + LK.getScore()); levelTxt.setText('Level: ' + level); // Show next ball preview with pulsing animation if (shooter.nextBall) { if (shooter.nextBall.parent) { shooter.nextBall.parent.removeChild(shooter.nextBall); } shooter.nextBall.x = -50; shooter.nextBall.y = 60; shooter.nextBall.scaleX = 0.8; shooter.nextBall.scaleY = 0.8; LK.gui.topRight.addChild(shooter.nextBall); // Add pulsing animation to next ball if (!shooter.nextBall.isPulsing) { shooter.nextBall.isPulsing = true; var _pulseNextBall = function pulseNextBall() { if (shooter.nextBall && shooter.nextBall.parent) { tween(shooter.nextBall, { scaleX: 0.9, scaleY: 0.9 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { if (shooter.nextBall && shooter.nextBall.parent) { tween(shooter.nextBall, { scaleX: 0.8, scaleY: 0.8 }, { duration: 800, easing: tween.easeInOut, onFinish: _pulseNextBall }); } } }); } }; _pulseNextBall(); } } // Increase chain speed over time (slower increase) normalChainSpeed += 0.00003; // Update other speeds to maintain ratios fastChainSpeed = normalChainSpeed * 6.67; // Maintain same ratio as before veryFastChainSpeed = normalChainSpeed * 20; // Maintain same ratio as before if (speedLevel === 0) { chainSpeed = normalChainSpeed; } else if (speedLevel === 1) { chainSpeed = fastChainSpeed; } else { chainSpeed = veryFastChainSpeed; } };
===================================================================
--- original.js
+++ change.js
@@ -7,8 +7,12 @@
* Classes
****/
var Ball = Container.expand(function (color) {
var self = Container.call(this);
+ // Safety check to prevent black balls
+ if (color === 'black') {
+ color = 'red'; // Default to red if black is passed
+ }
self.color = color;
self.ballColors = ['red', 'blue', 'green', 'yellow', 'purple', 'orange'];
self.isSpecial = false;
self.specialType = null; // 'explosive', 'freeze', 'rapid', 'white_freeze', 'black_destroyer'
@@ -249,8 +253,12 @@
}
} else {
randomColor = colors[Math.floor(Math.random() * colors.length)];
}
+ // Safety check to ensure we never create black balls
+ if (randomColor === 'black' || !randomColor) {
+ randomColor = colors[Math.floor(Math.random() * colors.length)]; // Default to valid colors
+ }
self.nextBall = new Ball(randomColor);
};
self.aimAt = function (x, y) {
var dx = x - self.x;
@@ -719,8 +727,12 @@
// Last 3 of every 8 balls use random colors
colorIndex = Math.floor(Math.random() * colors.length);
}
var color = colors[colorIndex];
+ // Safety check to ensure we never create black balls
+ if (color === 'black' || !color) {
+ color = colors[i % colors.length]; // Default to cycling through valid colors
+ }
var ball = game.addChild(new ChainBall(color));
ball.chainIndex = i;
// Initialize track position with consistent spacing for vortex
ball.trackPosition = i * 7.0; // Use consistent spacing to prevent overlap
8 ball billard with fire. In-Game asset. 2d. High contrast. No shadows
green neon ball. In-Game asset. 2d. High contrast. No shadows
blach hole gif. In-Game asset. 2d. High contrast. No shadows
space shooter cannon. In-Game asset. 2d. High contrast. No shadows
fire effect. In-Game asset. 2d. High contrast. No shadows
space track point. In-Game asset. 2d. High contrast. No shadows
aim . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
ice. In-Game asset. 2d. High contrast. No shadows
flying superman
laser beam. In-Game asset. 2d. High contrast. No shadows
green goblin. In-Game asset. 2d. High contrast. No shadows
rocket. No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
shoot
Sound effect
match
Sound effect
powerup
Sound effect
Gameplay
Music
gameover
Sound effect
frozen
Sound effect
celebration
Sound effect
white_shoot
Sound effect
fire_flying
Sound effect
superman_flying
Sound effect
superman_laser
Sound effect
villain_flying
Sound effect
villain_laser
Sound effect