User prompt
topu vurduktan sonra gitmeye devam etmesin
User prompt
topları vurduğundan patlama ve yok olma efekti olsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
mor renkli topa vurunca score sıfırlansın
User prompt
function handleBallHit(projectileColor, targetBallColor) { if (targetBallColor === "purple") { // MOR topa ne olursa olsun vurulunca puan azalacak score -= 10; playEffect("buzz"); // sinek efekti return; } if (projectileColor === targetBallColor) { // Doğru renge vurulduysa score += 5; playEffect("sparkle"); } else { // Yanlış renge vurulduysa score -= 5; playEffect("wrong"); } }
User prompt
function handleBallHit(projectileColor, targetBallColor) { if (targetBallColor === "purple") { // Mor top: ceza score -= 10; playEffect("buzz"); // pis sinek efekti return; } if (projectileColor === targetBallColor) { // Doğru renge vurdu: ödül score += 5; playEffect("sparkle"); // başarı efekti } else { // Yanlış renge vurdu: ceza score -= 5; playEffect("wrong"); // yanlış efekt } }
User prompt
if (ball.speed > MAX_SPEED) { ball.speed = MAX_SPEED; }
User prompt
mor renkli topu vurunca puan eksilsin
User prompt
aynı renkte toplar vurulmazsa puan verilmesin ve mor top hariç diğerleri puan versin.
User prompt
çok hızlı ara ara geçen toplar olmasın
User prompt
bukalemunun ortasında olan top daire içine alınıp daha belirgin hale gelsin. arada sırada çok hızlı geçen toplar yok edilsin.
User prompt
Please fix the bug: 'TypeError: Cannot read properties of null (reading 'ballGraphics')' in or related to this line: 'tween(hitBall.ballGraphics, {' Line Number: 605
User prompt
daha fazla top olsun ve daha ağır adımlarla hareket etsinler. aynı renkte olan toplar sürü şeklinde ara ara gelebilir. hepsi ilk vuruşta gitsin mor renkli top hariç.
User prompt
bukalemun topu ağızından fırlattığında topa efekt eklensin
User prompt
bukalemun topu gönderirken o tarafa doğru ağzında uzun dili dışarıya doğru gittiğini gösteren efekt eklensin
User prompt
toplara doğru gönderilen top bukalemunun ağız kısmında olsun ve bukalemunun kafasını döndürdüğü yöne doğru atılsın
User prompt
topları doğru vurdukça daha çok top gelsin
User prompt
her aynı renk vurulduğunda efekt olsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
aynı renge top atıldığında oyunun temasına uygun olarak bir efekt olsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
toplar daha çok olsun ve her aynı renkte top vurulduğunda daha çok gelsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
yuvarlaklar komple dağıllınca ve ekrandan yok olunca oyun bitsin
User prompt
1. bukalemunun ağzında yuvarlaklar ve rengi gözüksün ve ağzındaki yuvarlağın rengine göre kendi rengide o ağzındaki yuvarlağın rengine dönüşsün 2. yuvarlaklar her bir yana saçılsın ve düzenli bir biçimde tek sıra olarak hareket etsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Generate the first version of the source code of my game: Maya Frog Zuma. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Maya Frog Zuma
Initial prompt
Zuma tarzı, merkezde sabit duran bir kurbağanın renkli topları dönen bir zincire doğru fırlattığı, aynı renkten 3 veya daha fazla top yan yana geldiğinde patlayarak zinciri yavaşlattığı, toplar son noktaya ulaşmadan tüm zinciri bitirmeyi hedefleyen, antik Maya temalı, ses efektleri ve skor sistemi olan bir 2D oyun yap. retro piksel stilinde yap,mobil uyumlu yap.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Ball = Container.expand(function (color) { var self = Container.call(this); self.ballColor = color || 'red'; self.ballGraphics = self.attachAsset('ball_' + self.ballColor, { anchorX: 0.5, anchorY: 0.5 }); self.chainIndex = 0; self.pathPosition = 0; self.isExploding = false; self.isBigComboBall = false; // Default: not a big combo ball self.lastPathPosition = 0; self.isOffScreen = false; self.lastX = 0; self.lastY = 0; self.checkOffScreen = function () { // Check if ball has moved completely off screen var margin = 100; // Extra margin to ensure ball is truly gone var wasOnScreen = self.lastX >= -margin && self.lastX <= 2048 + margin && self.lastY >= -margin && self.lastY <= 2732 + margin; var isNowOffScreen = self.x < -margin || self.x > 2048 + margin || self.y < -margin || self.y > 2732 + margin; // Detect transition from on-screen to off-screen if (wasOnScreen && isNowOffScreen && !self.isOffScreen) { self.isOffScreen = true; } // Update last known positions self.lastX = self.x; self.lastY = self.y; }; self.explode = function () { if (self.isExploding) return; self.isExploding = true; tween(self.ballGraphics, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); LK.getSound('explosion').play(); }; self.destroyOnHit = function () { // 2x big combo balls: require 3 hits, persist longer if (self.isBigComboBall) { if (!self.bigHitCount) self.bigHitCount = 0; self.bigHitCount++; // Flash effect to show hit tween(self.ballGraphics, { tint: 0xFFD700, scaleX: 2.2, scaleY: 2.2 }, { duration: 120, easing: tween.easeOut, onFinish: function onFinish() { tween(self.ballGraphics, { tint: 0xFFFFFF, scaleX: 2.0, scaleY: 2.0 }, { duration: 100, easing: tween.easeIn }); } }); // Only destroy after 3 hits if (self.bigHitCount >= 3) { // Fade out and remove after a longer time tween(self.ballGraphics, { alpha: 0 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); self.isExploding = true; LK.getSound('explosion').play(); // If this was the persistent big combo ball, clear the reference if (typeof persistentBigComboBall !== "undefined" && persistentBigComboBall === self) { persistentBigComboBall = null; persistentBigComboBallColor = null; } return true; } else { // Persist on screen, not destroyed yet return false; } } // Simple effect: fade out the ballGraphics and remove the ball tween(self.ballGraphics, { alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { if (self.parent) { self.parent.removeChild(self); } } }); // Purple balls require multiple hits, others destroyed immediately if (self.ballColor === 'purple') { if (!self.hitCount) self.hitCount = 0; self.hitCount++; if (self.hitCount >= 2) { // Mor top tamamen yok edildiğinde: bir kaos patlaması gibi zincire çok ve karışık top ekle if (typeof addBallToChain === "function" && typeof addBallGroup === "function") { // 4-7 arası rastgele top ekle var extraBalls = 4 + Math.floor(Math.random() * 4); for (var i = 0; i < extraBalls; i++) { if (Math.random() < 0.5) { addBallToChain(); } else { addBallGroup(); } } } // Mor top yok edildiğinde purpleFrenzy başlat if (typeof triggerPurpleFrenzy === "function") { triggerPurpleFrenzy(); } // --- Spawn 5+ slow balls in all directions if purple, red, or yellow ball destroyed --- if (self.ballColor === 'purple' || self.ballColor === 'red' || self.ballColor === 'yellow') { var spawnCount = 5 + Math.floor(Math.random() * 3); // 5-7 balls var angleStep = Math.PI * 2 / spawnCount; for (var spawnIdx = 0; spawnIdx < spawnCount; spawnIdx++) { var angle = angleStep * spawnIdx + Math.random() * 0.2; // small random offset var colorChoices = ['purple', 'red', 'yellow']; var spawnColor = colorChoices[Math.floor(Math.random() * colorChoices.length)]; var newBall = new Ball(spawnColor); // Place at current position newBall.x = self.x; newBall.y = self.y; // Give a unique scatter direction and radius newBall.scatterDirection = angle; newBall.scatterRadius = 100 + Math.random() * 60; // Place at the end of the chain, but visually outside newBall.chainIndex = ballChain.length; newBall.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0; // Add to chain and game ballChain.push(newBall); if (typeof game !== "undefined") game.addChild(newBall); // Set very slow movement for these spawned balls newBall.isSpawnedSlow = true; newBall.slowMoveTicks = 0; // Overwrite updateBallPosition for this ball to move slowly outward newBall.update = function () { // Move outward in a straight line, very slowly var speed = 1.2; // very slow this.x += Math.cos(this.scatterDirection) * speed; this.y += Math.sin(this.scatterDirection) * speed; this.slowMoveTicks++; // After 180 ticks (~3s), let it join normal chain movement if (this.slowMoveTicks > 180) { // Remove custom update, let normal updateBallPosition take over delete this.update; } }; } } self.explode(); return true; } else { // Flash purple ball to show it was hit tween(self.ballGraphics, { tint: 0xFF00FF, scaleX: 1.2, scaleY: 1.2 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self.ballGraphics, { tint: 0xFFFFFF, scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } }); return false; } } else { // Subtle pop effect for every destroyed ball var pop = LK.getAsset('ball_' + self.ballColor, { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, scaleX: 0.7, scaleY: 0.7, alpha: 0.7 }); if (self.parent) self.parent.addChild(pop); tween(pop, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 220, easing: tween.easeOut, onFinish: function onFinish() { if (pop.parent) pop.parent.removeChild(pop); } }); self.explode(); return true; } }; return self; }); var Frog = Container.expand(function () { var self = Container.call(this); self.frogGraphics = self.attachAsset('frog', { anchorX: 0.5, anchorY: 0.5 }); self.currentBallColor = ballColors[Math.floor(Math.random() * ballColors.length)]; self.nextBallColor = ballColors[Math.floor(Math.random() * ballColors.length)]; // Create a dedicated preview area container above the frog self.previewArea = new Container(); self.previewArea.x = 0; self.previewArea.y = -260; // Place well above the frog's head, visually separated self.addChild(self.previewArea); // Add a subtle background for the preview area to make it distinct self.previewBg = LK.getAsset('bud', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: 1.18, scaleY: 1.18, alpha: 0.92, // normal tone, not too dark, visually separated but not harsh tint: 0x6b8e23 // olive green, natural and soft }); self.previewArea.addChild(self.previewBg); // Add the preview ball inside the preview area self.currentBall = self.attachAsset('ball_' + self.currentBallColor, { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: 0.7, scaleY: 0.7 }); self.previewArea.addChild(self.currentBall); // Removed highlight circle behind frog self.aimAngle = 0; self.canShoot = true; self.updateFrogColor = function () { // Color mapping for frog tinting based on ball color var colorMap = { 'red': 0xff8888, 'blue': 0x8888ff, 'yellow': 0xffff88, 'green': 0x88ff88, 'purple': 0xff88ff }; self.frogGraphics.tint = colorMap[self.currentBallColor] || 0xffffff; }; // Initialize frog color self.updateFrogColor(); self.updateAim = function (targetX, targetY) { var dx = targetX - self.x; var dy = targetY - self.y; self.aimAngle = Math.atan2(dy, dx); self.frogGraphics.rotation = self.aimAngle + Math.PI / 2; }; self.shoot = function () { if (!self.canShoot) return null; var projectile = new Projectile(self.currentBallColor); projectile.x = self.x; projectile.y = self.y; projectile.velocityX = Math.cos(self.aimAngle) * projectile.speed; projectile.velocityY = Math.sin(self.aimAngle) * projectile.speed; // Add visual effect when projectile is fired // Create shooting effect with glow and scale animation tween(projectile.projectileGraphics, { scaleX: 1.2, scaleY: 1.2, tint: 0xFFFFFF }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(projectile.projectileGraphics, { scaleX: 0.8, scaleY: 0.8, tint: 0xFFFFFF }, { duration: 100, easing: tween.easeIn }); } }); // Create muzzle flash effect at frog's mouth var muzzleFlash = LK.getAsset('ball_yellow', { anchorX: 0.5, anchorY: 0.5, x: self.x + Math.cos(self.aimAngle) * 30, y: self.y + Math.sin(self.aimAngle) * 30, scaleX: 0.4, scaleY: 0.4, alpha: 0.8, tint: 0xFFD700 }); game.addChild(muzzleFlash); // Animate muzzle flash tween(muzzleFlash, { scaleX: 0.8, scaleY: 0.8, alpha: 0 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { if (muzzleFlash.parent) { muzzleFlash.parent.removeChild(muzzleFlash); } } }); // Update frog's current ball self.currentBallColor = self.nextBallColor; self.nextBallColor = ballColors[Math.floor(Math.random() * ballColors.length)]; if (self.currentBall.parent) { self.currentBall.parent.removeChild(self.currentBall); } // Add the new preview ball to the preview area, keeping it visually separated self.currentBall = self.attachAsset('ball_' + self.currentBallColor, { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, scaleX: 0.7, scaleY: 0.7 }); self.previewArea.addChild(self.currentBall); // Removed highlight circle and pulsing effect logic // Update frog color to match new ball self.updateFrogColor(); LK.getSound('shoot').play(); return projectile; }; return self; }); // Game constants var Projectile = Container.expand(function (color) { var self = Container.call(this); self.ballColor = color; self.projectileGraphics = self.attachAsset('ball_' + self.ballColor, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); self.velocityX = 0; self.velocityY = 0; self.speed = 12; self.active = true; self.update = function () { if (!self.active) return; self.x += self.velocityX; self.y += self.velocityY; // Check bounds if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.active = false; if (self.parent) { self.parent.removeChild(self); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2d5016 }); /**** * Game Code ****/ // Initialize game assets with Maya theme and retro pixel style // Game constants var ballColors = ['red', 'blue', 'yellow', 'green', 'purple']; var pathRadius = 400; var centerX = 2048 / 2; var centerY = 2732 / 2; var chainSpeed = 0.22; // Slower chain movement for longer play var maxChainLength = 220; // Allow even more balls in chain var MAX_SPEED = 8; // Maximum allowed speed for balls // Game variables var ballChain = []; var projectiles = []; var gameRunning = true; var chainProgress = 0; var matchCombo = 0; // Combo tracking variables var comboCount = 0; var comboColor = undefined; // Add green leafy background image (fills the screen, behind all game elements) var leafyBg = LK.getAsset('path_marker', { anchorX: 0.5, anchorY: 0.5, x: centerX, y: centerY, scaleX: 2048 / 3000, scaleY: 2732 / 3000, alpha: 0.7 // subtle, not too strong }); game.addChild(leafyBg); // Create temple center var templeCenter = LK.getAsset('temple_center', { anchorX: 0.5, anchorY: 0.5, x: centerX, y: centerY }); game.addChild(templeCenter); // Create frog var frog = new Frog(); frog.x = centerX; frog.y = centerY; game.addChild(frog); // Create score display var scoreTxt = new Text2('Score: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Create ball usage counter display (top right) var ballUsageTxt = new Text2('Top Used: 0', { size: 60, fill: 0xFFFF00 }); ballUsageTxt.anchor.set(1, 0); ballUsageTxt.x = -50; // Will be positioned relative to gui.topRight ballUsageTxt.y = 50; LK.gui.topRight.addChild(ballUsageTxt); // Remove chainTxt from top left (no clain text) // Initialize ball chain function createInitialChain() { for (var i = 0; i < 130; i++) { // Increased from 90 to 130 for longer play var color = ballColors[Math.floor(Math.random() * ballColors.length)]; var ball = new Ball(color); ball.chainIndex = i; ball.pathPosition = i * 70; ballChain.push(ball); game.addChild(ball); updateBallPosition(ball); } } // Update ball position along scattered path with orderly movement function updateBallPosition(ball) { if (!ball.scatterDirection) { // Initialize scatter direction for each ball var angleOffset = ball.chainIndex * 137.5 % 360; // Golden angle for even distribution ball.scatterDirection = angleOffset * Math.PI / 180; ball.scatterRadius = 200 + ball.chainIndex % 3 * 50; // Varying distances } var totalProgress = chainProgress + ball.pathPosition; // If this is a 2x big combo ball, move it at least 5x slower var moveSpeedFactor = ball.isBigComboBall ? 0.1 : 0.5; // 0.1 is 5x slower than 0.5 var moveDistance = totalProgress * moveSpeedFactor; var targetX = centerX + Math.cos(ball.scatterDirection) * (ball.scatterRadius + moveDistance); var targetY = centerY + Math.sin(ball.scatterDirection) * (ball.scatterRadius + moveDistance); // Smooth movement towards target position // For big combo balls, also move more slowly towards their target var lerpFactor = ball.isBigComboBall ? 0.02 : 0.1; ball.x = ball.x + (targetX - ball.x) * lerpFactor; ball.y = ball.y + (targetY - ball.y) * lerpFactor; } // Add new ball to front of chain function addBallToChain() { if (ballChain.length >= maxChainLength) return; var color = ballColors[Math.floor(Math.random() * ballColors.length)]; var ball = new Ball(color); ball.chainIndex = ballChain.length; ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0; ballChain.push(ball); game.addChild(ball); updateBallPosition(ball); } // Add group of same color balls occasionally function addBallGroup() { if (ballChain.length >= maxChainLength - 5) return; var groupColor = ballColors[Math.floor(Math.random() * ballColors.length)]; var groupSize = 3 + Math.floor(Math.random() * 3); // 3-5 balls for (var i = 0; i < groupSize; i++) { if (ballChain.length >= maxChainLength) break; var ball = new Ball(groupColor); ball.chainIndex = ballChain.length; ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0; ballChain.push(ball); game.addChild(ball); updateBallPosition(ball); } } // Check for matches in chain function checkMatches() { var matches = []; var currentColor = null; var currentGroup = []; for (var i = 0; i < ballChain.length; i++) { var ball = ballChain[i]; if (ball.isExploding) continue; if (ball.ballColor === currentColor) { currentGroup.push(ball); } else { if (currentGroup.length >= 3) { matches = matches.concat(currentGroup); } currentColor = ball.ballColor; currentGroup = [ball]; } } // Check last group if (currentGroup.length >= 3) { matches = matches.concat(currentGroup); } if (matches.length > 0) { // Award points var points = matches.length * 10; if (matchCombo > 0) { points *= matchCombo + 1; } LK.setScore(LK.getScore() + points); matchCombo++; // Explode matched balls with Maya-themed effects for (var j = 0; j < matches.length; j++) { var matchedBall = matches[j]; // Create temple-themed glow effect tween(matchedBall.ballGraphics, { tint: 0xFFD700, // Golden glow scaleX: 1.3, scaleY: 1.3 }, { duration: 150, easing: tween.easeOut }); // Add screen flash effect for larger matches if (matches.length >= 5) { LK.effects.flashScreen(0xFFD700, 400); // Golden flash } matchedBall.explode(); } // Remove exploded balls from chain ballChain = ballChain.filter(function (ball) { return !ball.isExploding; }); // Reindex remaining balls for (var k = 0; k < ballChain.length; k++) { ballChain[k].chainIndex = k; } // Create energy burst effect at match location if (matches.length > 0) { var centerMatchX = 0; var centerMatchY = 0; for (var effectIndex = 0; effectIndex < matches.length; effectIndex++) { centerMatchX += matches[effectIndex].x; centerMatchY += matches[effectIndex].y; } centerMatchX /= matches.length; centerMatchY /= matches.length; // Create energy orbs radiating from match center for (var orbIndex = 0; orbIndex < 6; orbIndex++) { var energyOrb = LK.getAsset('ball_yellow', { anchorX: 0.5, anchorY: 0.5, x: centerMatchX, y: centerMatchY, scaleX: 0.3, scaleY: 0.3, alpha: 0.8 }); game.addChild(energyOrb); var angle = orbIndex * 60 * Math.PI / 180; var distance = 120 + Math.random() * 80; tween(energyOrb, { x: centerMatchX + Math.cos(angle) * distance, y: centerMatchY + Math.sin(angle) * distance, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { if (energyOrb.parent) { energyOrb.parent.removeChild(energyOrb); } } }); } } LK.getSound('match').play(); // Add new balls after successful matches to increase difficulty // Add more balls for larger matches to reward skillful play var ballsToAdd = 3 + Math.floor(matches.length / 3); // Base 3 + bonus for larger matches for (var addCount = 0; addCount < ballsToAdd; addCount++) { addBallToChain(); } // Check for chain reactions LK.setTimeout(function () { checkMatches(); }, 300); } else { matchCombo = 0; } } // Insert projectile into chain function insertProjectileIntoChain(projectile, insertIndex) { var newBall = new Ball(projectile.ballColor); newBall.chainIndex = insertIndex; // Adjust positions of balls after insertion point for (var i = insertIndex; i < ballChain.length; i++) { ballChain[i].chainIndex++; ballChain[i].pathPosition += 70; } // Set position for new ball if (insertIndex < ballChain.length) { newBall.pathPosition = ballChain[insertIndex].pathPosition - 70; } else { newBall.pathPosition = insertIndex * 70; } ballChain.splice(insertIndex, 0, newBall); game.addChild(newBall); updateBallPosition(newBall); // Remove projectile if (projectile.parent) { projectile.parent.removeChild(projectile); } // Check for matches LK.setTimeout(function () { checkMatches(); }, 100); } function handleBallHit(projectileColor, targetBallColor) { if (targetBallColor === "purple") { // Mor topa vurulunca 20 puan ekle LK.setScore(LK.getScore() + 20); LK.getSound('explosion').play(); // sinek efekti return; } if (projectileColor === targetBallColor) { // Doğru renge vurulduysa // Check if the hit ball is a big combo ball (2x size, 50 points) if (typeof hitBall !== "undefined" && hitBall.isBigComboBall) { LK.setScore(LK.getScore() + 50); } else { LK.setScore(LK.getScore() + 5); } LK.getSound('match').play(); } else { // Yanlış renge vurulduysa LK.setScore(Math.max(0, LK.getScore() - 5)); LK.getSound('shoot').play(); } } // Track number of balls used (shot) if (typeof ballsUsed === "undefined") var ballsUsed = 0; // Handle touch input game.down = function (x, y, obj) { if (!gameRunning) return; frog.updateAim(x, y); var projectile = frog.shoot(); if (projectile) { projectiles.push(projectile); game.addChild(projectile); ballsUsed++; ballUsageTxt.setText('Top Used: ' + ballsUsed); } }; game.move = function (x, y, obj) { if (!gameRunning) return; frog.updateAim(x, y); }; // Main game update loop game.update = function () { if (!gameRunning) return; // Gradually accelerate chain speed up to MAX_SPEED if (typeof chainSpeedAccel === "undefined") { var chainSpeedAccel = 0.00012; // Acceleration per tick } if (chainSpeed < MAX_SPEED * 0.18) { // Cap normal speed to 18% of MAX_SPEED chainSpeed += chainSpeedAccel; if (chainSpeed > MAX_SPEED * 0.18) chainSpeed = MAX_SPEED * 0.18; } // Update chain progress chainProgress += chainSpeed; // Update ball positions for (var i = 0; i < ballChain.length; i++) { var ball = ballChain[i]; if (!ball.isExploding) { ball.lastPathPosition = ball.pathPosition; var lastX = ball.x; var lastY = ball.y; // If this is a slow spawned ball with custom update, call it if (typeof ball.isSpawnedSlow !== "undefined" && ball.isSpawnedSlow && typeof ball.update === "function") { ball.update(); } else { updateBallPosition(ball); } // Check off-screen status, but do not remove persistent big combo ball if (!(typeof persistentBigComboBall !== "undefined" && ball === persistentBigComboBall)) { var wasOffScreen = ball.isOffScreen; ball.checkOffScreen(); // If the ball just went off screen this frame, spawn 2 new balls if (!wasOffScreen && ball.isOffScreen) { // Only spawn if game is running and not at max chain length if (gameRunning && ballChain.length < maxChainLength - 1) { addBallToChain(); addBallToChain(); } } } // Detect very fast moving balls and occasionally destroy them var deltaX = ball.x - lastX; var deltaY = ball.y - lastY; var speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // Limit ball speed to MAX_SPEED if (speed > MAX_SPEED) { var speedRatio = MAX_SPEED / speed; var limitedDeltaX = deltaX * speedRatio; var limitedDeltaY = deltaY * speedRatio; ball.x = lastX + limitedDeltaX; ball.y = lastY + limitedDeltaY; speed = MAX_SPEED; } if (speed > 8 && Math.random() < 0.02) { // 2% chance to destroy fast balls, but not persistent big combo ball if (!(typeof persistentBigComboBall !== "undefined" && ball === persistentBigComboBall)) { ball.explode(); ballChain.splice(i, 1); i--; // Adjust index after removal // Reindex remaining balls for (var reindexI = 0; reindexI < ballChain.length; reindexI++) { ballChain[reindexI].chainIndex = reindexI; } continue; } } // Check if chain reached center (game over condition) var totalProgress = chainProgress + ball.pathPosition; var distanceFromCenter = Math.sqrt((ball.x - centerX) * (ball.x - centerX) + (ball.y - centerY) * (ball.y - centerY)); if (distanceFromCenter <= 120 && ball.chainIndex === 0) { gameRunning = false; LK.showGameOver(); return; } } } // Update projectiles for (var j = projectiles.length - 1; j >= 0; j--) { var projectile = projectiles[j]; if (!projectile.active) { projectiles.splice(j, 1); continue; } // Check collision with chain balls var hitBall = null; var insertIndex = -1; for (var k = 0; k < ballChain.length; k++) { var chainBall = ballChain[k]; if (chainBall.isExploding) continue; var dx = projectile.x - chainBall.x; var dy = projectile.y - chainBall.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 55) { hitBall = chainBall; insertIndex = k; break; } } if (hitBall) { // Check if projectile color matches hit ball color for scoring var isColorMatch = projectile.ballColor === hitBall.ballColor; var isPurpleBall = hitBall.ballColor === 'purple'; // Destroy ball on hit (purple balls need multiple hits) var ballDestroyed = hitBall.destroyOnHit(); if (ballDestroyed) { // Remove destroyed ball from chain, but keep persistent big combo ball until 3 hits for (var removeIndex = 0; removeIndex < ballChain.length; removeIndex++) { if (ballChain[removeIndex] === hitBall) { // If this is the persistent big combo ball and it hasn't reached 3 hits, do not remove if (!(typeof persistentBigComboBall !== "undefined" && hitBall === persistentBigComboBall && (!hitBall.isExploding || hitBall.bigHitCount < 3))) { ballChain.splice(removeIndex, 1); } break; } } // Reindex remaining balls for (var reindexK = 0; reindexK < ballChain.length; reindexK++) { ballChain[reindexK].chainIndex = reindexK; } // Use centralized scoring function handleBallHit(projectile.ballColor, hitBall.ballColor); } // Only create special effects for matching colors (excluding purple) if (isColorMatch && !isPurpleBall && hitBall && hitBall.ballGraphics) { // Create Maya-themed matching effect tween(hitBall.ballGraphics, { tint: 0xFFD700, // Golden glow scaleX: 1.4, scaleY: 1.4 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { if (hitBall && hitBall.ballGraphics) { tween(hitBall.ballGraphics, { tint: 0xFFFFFF, // Return to normal scaleX: 1.0, scaleY: 1.0 }, { duration: 100, easing: tween.easeIn }); } } }); // Create energy burst effect around the hit ball for (var effectIndex = 0; effectIndex < 4; effectIndex++) { var energyParticle = LK.getAsset('ball_yellow', { anchorX: 0.5, anchorY: 0.5, x: hitBall.x, y: hitBall.y, scaleX: 0.2, scaleY: 0.2, alpha: 0.9 }); game.addChild(energyParticle); var angle = effectIndex * 90 * Math.PI / 180; var distance = 80; tween(energyParticle, { x: hitBall.x + Math.cos(angle) * distance, y: hitBall.y + Math.sin(angle) * distance, alpha: 0, scaleX: 0.05, scaleY: 0.05 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { if (energyParticle.parent) { energyParticle.parent.removeChild(energyParticle); } } }); } // Combo logic: Now also applies to purple balls! if (isColorMatch) { // --- Combo logic: Only count combo if same color as previous hit --- if (typeof comboCount === "undefined") comboCount = 1; if (typeof comboColor === "undefined") comboColor = hitBall.ballColor; if (comboColor === hitBall.ballColor) { comboCount++; } else { // Only reset combo if the color actually changed if (comboCount > 1) { // Optionally, you can trigger a combo break effect here if needed } comboCount = 1; comboColor = hitBall.ballColor; } // --- Combo Popup and Effects --- if (comboCount > 1) { // Show combo popup text at hitBall position var comboText = new Text2('COMBO x' + comboCount + '!', { size: 90 + Math.min(comboCount * 10, 120), fill: 0xFFD700, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); comboText.anchor.set(0.5, 0.5); comboText.x = hitBall.x; comboText.y = hitBall.y - 80; game.addChild(comboText); // Animate popup: scale up, float up, fade out tween(comboText, { scaleX: 1.4, scaleY: 1.4, y: comboText.y - 120, alpha: 0 }, { duration: 1400, // Increased from 700 to 1400ms for longer visibility easing: tween.easeOut, onFinish: function onFinish() { if (comboText.parent) comboText.parent.removeChild(comboText); } }); // Flash screen for high combos if (comboCount >= 5) { LK.effects.flashScreen(0xFFD700, 200 + comboCount * 30); } // Add a little shake to the game for big combos if (comboCount >= 7) { var shakeAmount = Math.min(comboCount * 2, 20); var originalX = game.x || 0; var originalY = game.y || 0; var shakeTicks = 12; var shakeTimer = LK.setInterval(function () { game.x = originalX + (Math.random() - 0.5) * shakeAmount; game.y = originalY + (Math.random() - 0.5) * shakeAmount; shakeTicks--; if (shakeTicks <= 0) { LK.clearInterval(shakeTimer); game.x = originalX; game.y = originalY; } }, 16); } // --- Extra balls for combos: spawn extra balls for each combo popup --- var extraComboBalls = Math.min(2 + Math.floor(comboCount / 2), 8); for (var extraComboI = 0; extraComboI < extraComboBalls; extraComboI++) { addBallToChain(); } } // Her doğru vuruştan sonra ortaya çıkan top sayısını artır (kombo stili) var ballsToAdd = Math.min(5 + comboCount, 20); // Kombo ile artan, bolca top for (var comboI = 0; comboI < ballsToAdd; comboI++) { addBallToChain(); } // Her doğru vuruşta toplar çok yavaş hareket etsin chainSpeed = 0.05; // Çok yavaş hareket // --- Combo: Track per-color hit counts for 2x big, 50-point ball after 3 hits of same color --- // Only increment color hit count if there is no persistent big combo ball on screen if (typeof colorHitCounts === "undefined") colorHitCounts = {}; if (typeof persistentBigComboBall === "undefined" || !persistentBigComboBall) { // Initialize color hit count if not present if (!colorHitCounts[hitBall.ballColor]) { colorHitCounts[hitBall.ballColor] = 0; } colorHitCounts[hitBall.ballColor]++; } // Track persistent 2x big combo ball if (typeof persistentBigComboBall === "undefined") persistentBigComboBall = null; if (typeof persistentBigComboBallColor === "undefined") persistentBigComboBallColor = null; // When 3 hits of the same color (at any time), spawn a 2x big random color ball and keep it until next 3 hits of any color if (colorHitCounts[hitBall.ballColor] === 3) { // Remove previous persistent big combo ball if it exists if (persistentBigComboBall && persistentBigComboBall.parent) { persistentBigComboBall.parent.removeChild(persistentBigComboBall); } // Pick a random color for the big ball var bigColors = ballColors.slice(); var randomBigColor = bigColors[Math.floor(Math.random() * bigColors.length)]; var bigBall = new Ball(randomBigColor); bigBall.chainIndex = ballChain.length; bigBall.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0; // Büyük top için scale artır bigBall.ballGraphics.scaleX = 2.0; bigBall.ballGraphics.scaleY = 2.0; // Mark as big and worth 50 points, and require 3 hits to destroy bigBall.isBigComboBall = true; bigBall.bigHitCount = 0; // Track hits for this big ball // Mark as persistent bigBall.isPersistentBigComboBall = true; persistentBigComboBall = bigBall; persistentBigComboBallColor = hitBall.ballColor; ballChain.push(bigBall); game.addChild(bigBall); updateBallPosition(bigBall); // 50 puan ekle LK.setScore(LK.getScore() + 50); // Reset this color's hit count colorHitCounts[hitBall.ballColor] = 0; } // Remove persistent big combo ball only if it was hit 3 times if (persistentBigComboBall && persistentBigComboBall.isExploding) { persistentBigComboBall = null; persistentBigComboBallColor = null; } } else { // Reset combo on wrong color or purple if (typeof comboCount !== "undefined" && comboCount > 2) { // Combo break effect: speed up chain and burst color chainSpeed = 0.35 + Math.random() * 0.1; // Color burst at frog var burstColors = [0xff4444, 0x44ff44, 0x4444ff, 0xffff44, 0xff44ff]; for (var burstI = 0; burstI < 7; burstI++) { var burst = LK.getAsset('ball_' + ballColors[Math.floor(Math.random() * ballColors.length)], { anchorX: 0.5, anchorY: 0.5, x: frog.x, y: frog.y, scaleX: 0.3, scaleY: 0.3, alpha: 0.8, tint: burstColors[burstI % burstColors.length] }); game.addChild(burst); var angle = Math.random() * Math.PI * 2; var dist = 120 + Math.random() * 60; tween(burst, { x: frog.x + Math.cos(angle) * dist, y: frog.y + Math.sin(angle) * dist, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { if (burst.parent) burst.parent.removeChild(burst); } }); } } comboCount = 0; comboColor = undefined; // Reset unique color combo if (typeof colorComboHits !== "undefined") colorComboHits = {}; if (typeof colorComboOrder !== "undefined") colorComboOrder = []; } } // Remove projectile immediately after hit projectile.active = false; if (projectile.parent) { projectile.parent.removeChild(projectile); } projectiles.splice(j, 1); if (!ballDestroyed) { insertProjectileIntoChain(projectile, insertIndex); } } } // --- Continuous, looping ball and group spawns until score is zero --- // Mor top yendikten sonra bir süre mor topların spawn oranını ve karmaşıklığını artır if (typeof purpleFrenzyTicks === "undefined") { var purpleFrenzyTicks = 0; var purpleFrenzyEndTick = 0; } if (typeof lastPurpleFrenzyTriggerTick === "undefined") { var lastPurpleFrenzyTriggerTick = -10000; } // Mor top yendikten sonra tetiklenecek fonksiyon if (typeof triggerPurpleFrenzy === "undefined") { var triggerPurpleFrenzy = function triggerPurpleFrenzy() { purpleFrenzyTicks = 0; purpleFrenzyEndTick = LK.ticks + 600 + Math.floor(Math.random() * 300); // 10-15 saniye lastPurpleFrenzyTriggerTick = LK.ticks; // Speed up chain and shake screen for a moment chainSpeed = 0.45; LK.effects.flashScreen(0xAA00FF, 400); var shakeTicks = 18; var originalX = game.x || 0; var originalY = game.y || 0; var shakeTimer = LK.setInterval(function () { game.x = originalX + (Math.random() - 0.5) * 18; game.y = originalY + (Math.random() - 0.5) * 18; shakeTicks--; if (shakeTicks <= 0) { LK.clearInterval(shakeTimer); game.x = originalX; game.y = originalY; } }, 16); }; } // Mor top yendikten sonra Ball.destroyOnHit içinde triggerPurpleFrenzy() çağrılır if (LK.getScore() > 0) { // Timers for next ball and group spawn if (typeof nextBallSpawnTick === "undefined") { var nextBallSpawnTick = LK.ticks + 10 + Math.floor(Math.random() * 30); // 0.16-0.66s (was 0.5-2s) var nextBallGroupSpawnTick = LK.ticks + 40 + Math.floor(Math.random() * 60); // 0.66-1.66s (was 2-5s) var nextChaosSpawnTick = LK.ticks + 60 + Math.floor(Math.random() * 100); // 1-2.66s (was 3-8s) } // Mor top fazlalığı aktif mi? var purpleFrenzyActive = purpleFrenzyEndTick > LK.ticks; // Random single ball spawns if (LK.ticks >= nextBallSpawnTick && ballChain.length < maxChainLength) { var ballsToAdd = purpleFrenzyActive ? 4 + Math.floor(Math.random() * 3) : 2 + Math.floor(Math.random() * 3); // 4-6 balls if frenzy, else 2-4 for (var i = 0; i < ballsToAdd; i++) { // Frenzy sırasında %40 mor top, %60 random if (purpleFrenzyActive && Math.random() < 0.4) { var ball = new Ball('purple'); ball.chainIndex = ballChain.length; ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0; ballChain.push(ball); game.addChild(ball); updateBallPosition(ball); } else { addBallToChain(); } } // Next spawn in 0.1-0.33s if frenzy, else 0.16-0.66s nextBallSpawnTick = LK.ticks + (purpleFrenzyActive ? 6 + Math.floor(Math.random() * 14) : 10 + Math.floor(Math.random() * 30)); } // Random group spawns if (LK.ticks >= nextBallGroupSpawnTick && ballChain.length < maxChainLength - 5) { var doGroup = Math.random() < (purpleFrenzyActive ? 0.95 : 0.85); // 95% group if frenzy, 85% else if (doGroup) { // Frenzy sırasında grup içinde mor toplar karışık if (purpleFrenzyActive && Math.random() < 0.5) { var groupSize = 5 + Math.floor(Math.random() * 3); // 5-7 balls for (var gi = 0; gi < groupSize; gi++) { var color = Math.random() < 0.5 ? 'purple' : ballColors[Math.floor(Math.random() * ballColors.length)]; var ball = new Ball(color); ball.chainIndex = ballChain.length; ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0; ballChain.push(ball); game.addChild(ball); updateBallPosition(ball); } } else { addBallGroup(); } } else { addBallToChain(); addBallToChain(); addBallToChain(); } // Next group in 0.5-1.25s if frenzy, else 0.66-1.66s nextBallGroupSpawnTick = LK.ticks + (purpleFrenzyActive ? 30 + Math.floor(Math.random() * 45) : 40 + Math.floor(Math.random() * 60)); } // Rare chaos burst: lots of balls at once, unpredictable if (LK.ticks >= nextChaosSpawnTick && ballChain.length < maxChainLength - 8) { var chaosCount = purpleFrenzyActive ? 10 + Math.floor(Math.random() * 6) : 6 + Math.floor(Math.random() * 5); // 10-15 balls if frenzy, else 6-10 for (var i = 0; i < chaosCount; i++) { if (purpleFrenzyActive && Math.random() < 0.5) { var color = Math.random() < 0.7 ? 'purple' : ballColors[Math.floor(Math.random() * ballColors.length)]; var ball = new Ball(color); ball.chainIndex = ballChain.length; ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0; ballChain.push(ball); game.addChild(ball); updateBallPosition(ball); } else if (Math.random() < 0.5) { addBallToChain(); } else { addBallGroup(); } } // Next chaos in 0.66-1.33s if frenzy, else 1-2.66s nextChaosSpawnTick = LK.ticks + (purpleFrenzyActive ? 40 + Math.floor(Math.random() * 40) : 60 + Math.floor(Math.random() * 100)); } } else { // If score is zero, stop all new ball spawns // Optionally, you could clear timers or handle end-of-loop logic here } // Update UI scoreTxt.setText('Score: ' + LK.getScore()); ballUsageTxt.setText('Top Used: ' + (typeof ballsUsed !== "undefined" ? ballsUsed : 0)); // Check if all balls are scattered off screen (game over condition) var allBallsOffScreen = ballChain.length > 0; for (var m = 0; m < ballChain.length; m++) { if (!ballChain[m].isOffScreen && !ballChain[m].isExploding) { allBallsOffScreen = false; break; } } if (allBallsOffScreen && ballChain.length > 0) { gameRunning = false; // Custom "The End" screen with green frame and music showTheEndScreen(); return; } // End the game if ballsUsed reaches 50 if (typeof ballsUsed !== "undefined" && ballsUsed >= 50) { gameRunning = false; showTheEndScreen(); return; } // Win condition - clear all balls if (ballChain.length === 0) { gameRunning = false; // Reset color hit counts and persistent big combo ball on win if (typeof colorHitCounts !== "undefined") colorHitCounts = {}; if (typeof persistentBigComboBall !== "undefined") persistentBigComboBall = null; if (typeof persistentBigComboBallColor !== "undefined") persistentBigComboBallColor = null; // Rainbow burst effect at center for (var rb = 0; rb < 12; rb++) { var colorIdx = rb % ballColors.length; var burst = LK.getAsset('ball_' + ballColors[colorIdx], { anchorX: 0.5, anchorY: 0.5, x: centerX, y: centerY, scaleX: 0.5, scaleY: 0.5, alpha: 1 }); game.addChild(burst); var angle = rb * (Math.PI * 2 / 12); var dist = 400 + Math.random() * 120; tween(burst, { x: centerX + Math.cos(angle) * dist, y: centerY + Math.sin(angle) * dist, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { if (burst.parent) burst.parent.removeChild(burst); } }); } // Slow motion effect before win var oldChainSpeed = chainSpeed; chainSpeed = 0.01; LK.setTimeout(function () { chainSpeed = oldChainSpeed; LK.setScore(LK.getScore() + 1000); // Bonus for clearing showTheEndScreen(); }, 900); return; } // --- Custom "The End" screen with green frame and music --- function showTheEndScreen() { // Stop all music and play end music (use 'final' sound as end music) LK.stopMusic(); LK.playMusic('final', { loop: false, fade: { start: 0, end: 1, duration: 1200 } }); // Overlay: green frame var frameThickness = 32; var frameColor = 0x00ff44; var frameAlpha = 0.85; // Top var topFrame = LK.getAsset('bud', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: 2048, height: frameThickness, tint: frameColor, alpha: frameAlpha }); // Bottom var bottomFrame = LK.getAsset('bud', { anchorX: 0, anchorY: 0, x: 0, y: 2732 - frameThickness, width: 2048, height: frameThickness, tint: frameColor, alpha: frameAlpha }); // Left var leftFrame = LK.getAsset('bud', { anchorX: 0, anchorY: 0, x: 0, y: 0, width: frameThickness, height: 2732, tint: frameColor, alpha: frameAlpha }); // Right var rightFrame = LK.getAsset('bud', { anchorX: 0, anchorY: 0, x: 2048 - frameThickness, y: 0, width: frameThickness, height: 2732, tint: frameColor, alpha: frameAlpha }); // "The End" text var theEndText = new Text2('THE END', { size: 260, fill: 0x00FF44, font: "'GillSans-Bold',Impact,'Arial Black',Tahoma" }); theEndText.anchor.set(0.5, 0.5); theEndText.x = 2048 / 2; theEndText.y = 2732 / 2; // Add to game overlay game.addChild(topFrame); game.addChild(bottomFrame); game.addChild(leftFrame); game.addChild(rightFrame); game.addChild(theEndText); // Animate "The End" text (pulse) tween(theEndText, { scaleX: 1.18, scaleY: 1.18 }, { duration: 900, yoyo: true, repeat: 2, easing: tween.easeInOut }); // Optionally, fade out all gameplay elements for (var i = 0; i < game.children.length; i++) { var obj = game.children[i]; if (obj !== topFrame && obj !== bottomFrame && obj !== leftFrame && obj !== rightFrame && obj !== theEndText) { tween(obj, { alpha: 0.18 }, { duration: 900, easing: tween.easeOut }); } } } }; // Initialize the game createInitialChain(); ; // Play background music 'amazon' LK.playMusic('amazon'); // Minimalistic tween library for animations
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Ball = Container.expand(function (color) {
var self = Container.call(this);
self.ballColor = color || 'red';
self.ballGraphics = self.attachAsset('ball_' + self.ballColor, {
anchorX: 0.5,
anchorY: 0.5
});
self.chainIndex = 0;
self.pathPosition = 0;
self.isExploding = false;
self.isBigComboBall = false; // Default: not a big combo ball
self.lastPathPosition = 0;
self.isOffScreen = false;
self.lastX = 0;
self.lastY = 0;
self.checkOffScreen = function () {
// Check if ball has moved completely off screen
var margin = 100; // Extra margin to ensure ball is truly gone
var wasOnScreen = self.lastX >= -margin && self.lastX <= 2048 + margin && self.lastY >= -margin && self.lastY <= 2732 + margin;
var isNowOffScreen = self.x < -margin || self.x > 2048 + margin || self.y < -margin || self.y > 2732 + margin;
// Detect transition from on-screen to off-screen
if (wasOnScreen && isNowOffScreen && !self.isOffScreen) {
self.isOffScreen = true;
}
// Update last known positions
self.lastX = self.x;
self.lastY = self.y;
};
self.explode = function () {
if (self.isExploding) return;
self.isExploding = true;
tween(self.ballGraphics, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
LK.getSound('explosion').play();
};
self.destroyOnHit = function () {
// 2x big combo balls: require 3 hits, persist longer
if (self.isBigComboBall) {
if (!self.bigHitCount) self.bigHitCount = 0;
self.bigHitCount++;
// Flash effect to show hit
tween(self.ballGraphics, {
tint: 0xFFD700,
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 120,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.ballGraphics, {
tint: 0xFFFFFF,
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
// Only destroy after 3 hits
if (self.bigHitCount >= 3) {
// Fade out and remove after a longer time
tween(self.ballGraphics, {
alpha: 0
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
self.isExploding = true;
LK.getSound('explosion').play();
// If this was the persistent big combo ball, clear the reference
if (typeof persistentBigComboBall !== "undefined" && persistentBigComboBall === self) {
persistentBigComboBall = null;
persistentBigComboBallColor = null;
}
return true;
} else {
// Persist on screen, not destroyed yet
return false;
}
}
// Simple effect: fade out the ballGraphics and remove the ball
tween(self.ballGraphics, {
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (self.parent) {
self.parent.removeChild(self);
}
}
});
// Purple balls require multiple hits, others destroyed immediately
if (self.ballColor === 'purple') {
if (!self.hitCount) self.hitCount = 0;
self.hitCount++;
if (self.hitCount >= 2) {
// Mor top tamamen yok edildiğinde: bir kaos patlaması gibi zincire çok ve karışık top ekle
if (typeof addBallToChain === "function" && typeof addBallGroup === "function") {
// 4-7 arası rastgele top ekle
var extraBalls = 4 + Math.floor(Math.random() * 4);
for (var i = 0; i < extraBalls; i++) {
if (Math.random() < 0.5) {
addBallToChain();
} else {
addBallGroup();
}
}
}
// Mor top yok edildiğinde purpleFrenzy başlat
if (typeof triggerPurpleFrenzy === "function") {
triggerPurpleFrenzy();
}
// --- Spawn 5+ slow balls in all directions if purple, red, or yellow ball destroyed ---
if (self.ballColor === 'purple' || self.ballColor === 'red' || self.ballColor === 'yellow') {
var spawnCount = 5 + Math.floor(Math.random() * 3); // 5-7 balls
var angleStep = Math.PI * 2 / spawnCount;
for (var spawnIdx = 0; spawnIdx < spawnCount; spawnIdx++) {
var angle = angleStep * spawnIdx + Math.random() * 0.2; // small random offset
var colorChoices = ['purple', 'red', 'yellow'];
var spawnColor = colorChoices[Math.floor(Math.random() * colorChoices.length)];
var newBall = new Ball(spawnColor);
// Place at current position
newBall.x = self.x;
newBall.y = self.y;
// Give a unique scatter direction and radius
newBall.scatterDirection = angle;
newBall.scatterRadius = 100 + Math.random() * 60;
// Place at the end of the chain, but visually outside
newBall.chainIndex = ballChain.length;
newBall.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0;
// Add to chain and game
ballChain.push(newBall);
if (typeof game !== "undefined") game.addChild(newBall);
// Set very slow movement for these spawned balls
newBall.isSpawnedSlow = true;
newBall.slowMoveTicks = 0;
// Overwrite updateBallPosition for this ball to move slowly outward
newBall.update = function () {
// Move outward in a straight line, very slowly
var speed = 1.2; // very slow
this.x += Math.cos(this.scatterDirection) * speed;
this.y += Math.sin(this.scatterDirection) * speed;
this.slowMoveTicks++;
// After 180 ticks (~3s), let it join normal chain movement
if (this.slowMoveTicks > 180) {
// Remove custom update, let normal updateBallPosition take over
delete this.update;
}
};
}
}
self.explode();
return true;
} else {
// Flash purple ball to show it was hit
tween(self.ballGraphics, {
tint: 0xFF00FF,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self.ballGraphics, {
tint: 0xFFFFFF,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
});
return false;
}
} else {
// Subtle pop effect for every destroyed ball
var pop = LK.getAsset('ball_' + self.ballColor, {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
scaleX: 0.7,
scaleY: 0.7,
alpha: 0.7
});
if (self.parent) self.parent.addChild(pop);
tween(pop, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 220,
easing: tween.easeOut,
onFinish: function onFinish() {
if (pop.parent) pop.parent.removeChild(pop);
}
});
self.explode();
return true;
}
};
return self;
});
var Frog = Container.expand(function () {
var self = Container.call(this);
self.frogGraphics = self.attachAsset('frog', {
anchorX: 0.5,
anchorY: 0.5
});
self.currentBallColor = ballColors[Math.floor(Math.random() * ballColors.length)];
self.nextBallColor = ballColors[Math.floor(Math.random() * ballColors.length)];
// Create a dedicated preview area container above the frog
self.previewArea = new Container();
self.previewArea.x = 0;
self.previewArea.y = -260; // Place well above the frog's head, visually separated
self.addChild(self.previewArea);
// Add a subtle background for the preview area to make it distinct
self.previewBg = LK.getAsset('bud', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 1.18,
scaleY: 1.18,
alpha: 0.92,
// normal tone, not too dark, visually separated but not harsh
tint: 0x6b8e23 // olive green, natural and soft
});
self.previewArea.addChild(self.previewBg);
// Add the preview ball inside the preview area
self.currentBall = self.attachAsset('ball_' + self.currentBallColor, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 0.7,
scaleY: 0.7
});
self.previewArea.addChild(self.currentBall);
// Removed highlight circle behind frog
self.aimAngle = 0;
self.canShoot = true;
self.updateFrogColor = function () {
// Color mapping for frog tinting based on ball color
var colorMap = {
'red': 0xff8888,
'blue': 0x8888ff,
'yellow': 0xffff88,
'green': 0x88ff88,
'purple': 0xff88ff
};
self.frogGraphics.tint = colorMap[self.currentBallColor] || 0xffffff;
};
// Initialize frog color
self.updateFrogColor();
self.updateAim = function (targetX, targetY) {
var dx = targetX - self.x;
var dy = targetY - self.y;
self.aimAngle = Math.atan2(dy, dx);
self.frogGraphics.rotation = self.aimAngle + Math.PI / 2;
};
self.shoot = function () {
if (!self.canShoot) return null;
var projectile = new Projectile(self.currentBallColor);
projectile.x = self.x;
projectile.y = self.y;
projectile.velocityX = Math.cos(self.aimAngle) * projectile.speed;
projectile.velocityY = Math.sin(self.aimAngle) * projectile.speed;
// Add visual effect when projectile is fired
// Create shooting effect with glow and scale animation
tween(projectile.projectileGraphics, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFFFFFF
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(projectile.projectileGraphics, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0xFFFFFF
}, {
duration: 100,
easing: tween.easeIn
});
}
});
// Create muzzle flash effect at frog's mouth
var muzzleFlash = LK.getAsset('ball_yellow', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x + Math.cos(self.aimAngle) * 30,
y: self.y + Math.sin(self.aimAngle) * 30,
scaleX: 0.4,
scaleY: 0.4,
alpha: 0.8,
tint: 0xFFD700
});
game.addChild(muzzleFlash);
// Animate muzzle flash
tween(muzzleFlash, {
scaleX: 0.8,
scaleY: 0.8,
alpha: 0
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (muzzleFlash.parent) {
muzzleFlash.parent.removeChild(muzzleFlash);
}
}
});
// Update frog's current ball
self.currentBallColor = self.nextBallColor;
self.nextBallColor = ballColors[Math.floor(Math.random() * ballColors.length)];
if (self.currentBall.parent) {
self.currentBall.parent.removeChild(self.currentBall);
}
// Add the new preview ball to the preview area, keeping it visually separated
self.currentBall = self.attachAsset('ball_' + self.currentBallColor, {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 0,
scaleX: 0.7,
scaleY: 0.7
});
self.previewArea.addChild(self.currentBall);
// Removed highlight circle and pulsing effect logic
// Update frog color to match new ball
self.updateFrogColor();
LK.getSound('shoot').play();
return projectile;
};
return self;
});
// Game constants
var Projectile = Container.expand(function (color) {
var self = Container.call(this);
self.ballColor = color;
self.projectileGraphics = self.attachAsset('ball_' + self.ballColor, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
self.velocityX = 0;
self.velocityY = 0;
self.speed = 12;
self.active = true;
self.update = function () {
if (!self.active) return;
self.x += self.velocityX;
self.y += self.velocityY;
// Check bounds
if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) {
self.active = false;
if (self.parent) {
self.parent.removeChild(self);
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2d5016
});
/****
* Game Code
****/
// Initialize game assets with Maya theme and retro pixel style
// Game constants
var ballColors = ['red', 'blue', 'yellow', 'green', 'purple'];
var pathRadius = 400;
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var chainSpeed = 0.22; // Slower chain movement for longer play
var maxChainLength = 220; // Allow even more balls in chain
var MAX_SPEED = 8; // Maximum allowed speed for balls
// Game variables
var ballChain = [];
var projectiles = [];
var gameRunning = true;
var chainProgress = 0;
var matchCombo = 0;
// Combo tracking variables
var comboCount = 0;
var comboColor = undefined;
// Add green leafy background image (fills the screen, behind all game elements)
var leafyBg = LK.getAsset('path_marker', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
scaleX: 2048 / 3000,
scaleY: 2732 / 3000,
alpha: 0.7 // subtle, not too strong
});
game.addChild(leafyBg);
// Create temple center
var templeCenter = LK.getAsset('temple_center', {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY
});
game.addChild(templeCenter);
// Create frog
var frog = new Frog();
frog.x = centerX;
frog.y = centerY;
game.addChild(frog);
// Create score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Create ball usage counter display (top right)
var ballUsageTxt = new Text2('Top Used: 0', {
size: 60,
fill: 0xFFFF00
});
ballUsageTxt.anchor.set(1, 0);
ballUsageTxt.x = -50; // Will be positioned relative to gui.topRight
ballUsageTxt.y = 50;
LK.gui.topRight.addChild(ballUsageTxt);
// Remove chainTxt from top left (no clain text)
// Initialize ball chain
function createInitialChain() {
for (var i = 0; i < 130; i++) {
// Increased from 90 to 130 for longer play
var color = ballColors[Math.floor(Math.random() * ballColors.length)];
var ball = new Ball(color);
ball.chainIndex = i;
ball.pathPosition = i * 70;
ballChain.push(ball);
game.addChild(ball);
updateBallPosition(ball);
}
}
// Update ball position along scattered path with orderly movement
function updateBallPosition(ball) {
if (!ball.scatterDirection) {
// Initialize scatter direction for each ball
var angleOffset = ball.chainIndex * 137.5 % 360; // Golden angle for even distribution
ball.scatterDirection = angleOffset * Math.PI / 180;
ball.scatterRadius = 200 + ball.chainIndex % 3 * 50; // Varying distances
}
var totalProgress = chainProgress + ball.pathPosition;
// If this is a 2x big combo ball, move it at least 5x slower
var moveSpeedFactor = ball.isBigComboBall ? 0.1 : 0.5; // 0.1 is 5x slower than 0.5
var moveDistance = totalProgress * moveSpeedFactor;
var targetX = centerX + Math.cos(ball.scatterDirection) * (ball.scatterRadius + moveDistance);
var targetY = centerY + Math.sin(ball.scatterDirection) * (ball.scatterRadius + moveDistance);
// Smooth movement towards target position
// For big combo balls, also move more slowly towards their target
var lerpFactor = ball.isBigComboBall ? 0.02 : 0.1;
ball.x = ball.x + (targetX - ball.x) * lerpFactor;
ball.y = ball.y + (targetY - ball.y) * lerpFactor;
}
// Add new ball to front of chain
function addBallToChain() {
if (ballChain.length >= maxChainLength) return;
var color = ballColors[Math.floor(Math.random() * ballColors.length)];
var ball = new Ball(color);
ball.chainIndex = ballChain.length;
ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0;
ballChain.push(ball);
game.addChild(ball);
updateBallPosition(ball);
}
// Add group of same color balls occasionally
function addBallGroup() {
if (ballChain.length >= maxChainLength - 5) return;
var groupColor = ballColors[Math.floor(Math.random() * ballColors.length)];
var groupSize = 3 + Math.floor(Math.random() * 3); // 3-5 balls
for (var i = 0; i < groupSize; i++) {
if (ballChain.length >= maxChainLength) break;
var ball = new Ball(groupColor);
ball.chainIndex = ballChain.length;
ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0;
ballChain.push(ball);
game.addChild(ball);
updateBallPosition(ball);
}
}
// Check for matches in chain
function checkMatches() {
var matches = [];
var currentColor = null;
var currentGroup = [];
for (var i = 0; i < ballChain.length; i++) {
var ball = ballChain[i];
if (ball.isExploding) continue;
if (ball.ballColor === currentColor) {
currentGroup.push(ball);
} else {
if (currentGroup.length >= 3) {
matches = matches.concat(currentGroup);
}
currentColor = ball.ballColor;
currentGroup = [ball];
}
}
// Check last group
if (currentGroup.length >= 3) {
matches = matches.concat(currentGroup);
}
if (matches.length > 0) {
// Award points
var points = matches.length * 10;
if (matchCombo > 0) {
points *= matchCombo + 1;
}
LK.setScore(LK.getScore() + points);
matchCombo++;
// Explode matched balls with Maya-themed effects
for (var j = 0; j < matches.length; j++) {
var matchedBall = matches[j];
// Create temple-themed glow effect
tween(matchedBall.ballGraphics, {
tint: 0xFFD700,
// Golden glow
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
easing: tween.easeOut
});
// Add screen flash effect for larger matches
if (matches.length >= 5) {
LK.effects.flashScreen(0xFFD700, 400); // Golden flash
}
matchedBall.explode();
}
// Remove exploded balls from chain
ballChain = ballChain.filter(function (ball) {
return !ball.isExploding;
});
// Reindex remaining balls
for (var k = 0; k < ballChain.length; k++) {
ballChain[k].chainIndex = k;
}
// Create energy burst effect at match location
if (matches.length > 0) {
var centerMatchX = 0;
var centerMatchY = 0;
for (var effectIndex = 0; effectIndex < matches.length; effectIndex++) {
centerMatchX += matches[effectIndex].x;
centerMatchY += matches[effectIndex].y;
}
centerMatchX /= matches.length;
centerMatchY /= matches.length;
// Create energy orbs radiating from match center
for (var orbIndex = 0; orbIndex < 6; orbIndex++) {
var energyOrb = LK.getAsset('ball_yellow', {
anchorX: 0.5,
anchorY: 0.5,
x: centerMatchX,
y: centerMatchY,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8
});
game.addChild(energyOrb);
var angle = orbIndex * 60 * Math.PI / 180;
var distance = 120 + Math.random() * 80;
tween(energyOrb, {
x: centerMatchX + Math.cos(angle) * distance,
y: centerMatchY + Math.sin(angle) * distance,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
if (energyOrb.parent) {
energyOrb.parent.removeChild(energyOrb);
}
}
});
}
}
LK.getSound('match').play();
// Add new balls after successful matches to increase difficulty
// Add more balls for larger matches to reward skillful play
var ballsToAdd = 3 + Math.floor(matches.length / 3); // Base 3 + bonus for larger matches
for (var addCount = 0; addCount < ballsToAdd; addCount++) {
addBallToChain();
}
// Check for chain reactions
LK.setTimeout(function () {
checkMatches();
}, 300);
} else {
matchCombo = 0;
}
}
// Insert projectile into chain
function insertProjectileIntoChain(projectile, insertIndex) {
var newBall = new Ball(projectile.ballColor);
newBall.chainIndex = insertIndex;
// Adjust positions of balls after insertion point
for (var i = insertIndex; i < ballChain.length; i++) {
ballChain[i].chainIndex++;
ballChain[i].pathPosition += 70;
}
// Set position for new ball
if (insertIndex < ballChain.length) {
newBall.pathPosition = ballChain[insertIndex].pathPosition - 70;
} else {
newBall.pathPosition = insertIndex * 70;
}
ballChain.splice(insertIndex, 0, newBall);
game.addChild(newBall);
updateBallPosition(newBall);
// Remove projectile
if (projectile.parent) {
projectile.parent.removeChild(projectile);
}
// Check for matches
LK.setTimeout(function () {
checkMatches();
}, 100);
}
function handleBallHit(projectileColor, targetBallColor) {
if (targetBallColor === "purple") {
// Mor topa vurulunca 20 puan ekle
LK.setScore(LK.getScore() + 20);
LK.getSound('explosion').play(); // sinek efekti
return;
}
if (projectileColor === targetBallColor) {
// Doğru renge vurulduysa
// Check if the hit ball is a big combo ball (2x size, 50 points)
if (typeof hitBall !== "undefined" && hitBall.isBigComboBall) {
LK.setScore(LK.getScore() + 50);
} else {
LK.setScore(LK.getScore() + 5);
}
LK.getSound('match').play();
} else {
// Yanlış renge vurulduysa
LK.setScore(Math.max(0, LK.getScore() - 5));
LK.getSound('shoot').play();
}
}
// Track number of balls used (shot)
if (typeof ballsUsed === "undefined") var ballsUsed = 0;
// Handle touch input
game.down = function (x, y, obj) {
if (!gameRunning) return;
frog.updateAim(x, y);
var projectile = frog.shoot();
if (projectile) {
projectiles.push(projectile);
game.addChild(projectile);
ballsUsed++;
ballUsageTxt.setText('Top Used: ' + ballsUsed);
}
};
game.move = function (x, y, obj) {
if (!gameRunning) return;
frog.updateAim(x, y);
};
// Main game update loop
game.update = function () {
if (!gameRunning) return;
// Gradually accelerate chain speed up to MAX_SPEED
if (typeof chainSpeedAccel === "undefined") {
var chainSpeedAccel = 0.00012; // Acceleration per tick
}
if (chainSpeed < MAX_SPEED * 0.18) {
// Cap normal speed to 18% of MAX_SPEED
chainSpeed += chainSpeedAccel;
if (chainSpeed > MAX_SPEED * 0.18) chainSpeed = MAX_SPEED * 0.18;
}
// Update chain progress
chainProgress += chainSpeed;
// Update ball positions
for (var i = 0; i < ballChain.length; i++) {
var ball = ballChain[i];
if (!ball.isExploding) {
ball.lastPathPosition = ball.pathPosition;
var lastX = ball.x;
var lastY = ball.y;
// If this is a slow spawned ball with custom update, call it
if (typeof ball.isSpawnedSlow !== "undefined" && ball.isSpawnedSlow && typeof ball.update === "function") {
ball.update();
} else {
updateBallPosition(ball);
}
// Check off-screen status, but do not remove persistent big combo ball
if (!(typeof persistentBigComboBall !== "undefined" && ball === persistentBigComboBall)) {
var wasOffScreen = ball.isOffScreen;
ball.checkOffScreen();
// If the ball just went off screen this frame, spawn 2 new balls
if (!wasOffScreen && ball.isOffScreen) {
// Only spawn if game is running and not at max chain length
if (gameRunning && ballChain.length < maxChainLength - 1) {
addBallToChain();
addBallToChain();
}
}
}
// Detect very fast moving balls and occasionally destroy them
var deltaX = ball.x - lastX;
var deltaY = ball.y - lastY;
var speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
// Limit ball speed to MAX_SPEED
if (speed > MAX_SPEED) {
var speedRatio = MAX_SPEED / speed;
var limitedDeltaX = deltaX * speedRatio;
var limitedDeltaY = deltaY * speedRatio;
ball.x = lastX + limitedDeltaX;
ball.y = lastY + limitedDeltaY;
speed = MAX_SPEED;
}
if (speed > 8 && Math.random() < 0.02) {
// 2% chance to destroy fast balls, but not persistent big combo ball
if (!(typeof persistentBigComboBall !== "undefined" && ball === persistentBigComboBall)) {
ball.explode();
ballChain.splice(i, 1);
i--; // Adjust index after removal
// Reindex remaining balls
for (var reindexI = 0; reindexI < ballChain.length; reindexI++) {
ballChain[reindexI].chainIndex = reindexI;
}
continue;
}
}
// Check if chain reached center (game over condition)
var totalProgress = chainProgress + ball.pathPosition;
var distanceFromCenter = Math.sqrt((ball.x - centerX) * (ball.x - centerX) + (ball.y - centerY) * (ball.y - centerY));
if (distanceFromCenter <= 120 && ball.chainIndex === 0) {
gameRunning = false;
LK.showGameOver();
return;
}
}
}
// Update projectiles
for (var j = projectiles.length - 1; j >= 0; j--) {
var projectile = projectiles[j];
if (!projectile.active) {
projectiles.splice(j, 1);
continue;
}
// Check collision with chain balls
var hitBall = null;
var insertIndex = -1;
for (var k = 0; k < ballChain.length; k++) {
var chainBall = ballChain[k];
if (chainBall.isExploding) continue;
var dx = projectile.x - chainBall.x;
var dy = projectile.y - chainBall.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 55) {
hitBall = chainBall;
insertIndex = k;
break;
}
}
if (hitBall) {
// Check if projectile color matches hit ball color for scoring
var isColorMatch = projectile.ballColor === hitBall.ballColor;
var isPurpleBall = hitBall.ballColor === 'purple';
// Destroy ball on hit (purple balls need multiple hits)
var ballDestroyed = hitBall.destroyOnHit();
if (ballDestroyed) {
// Remove destroyed ball from chain, but keep persistent big combo ball until 3 hits
for (var removeIndex = 0; removeIndex < ballChain.length; removeIndex++) {
if (ballChain[removeIndex] === hitBall) {
// If this is the persistent big combo ball and it hasn't reached 3 hits, do not remove
if (!(typeof persistentBigComboBall !== "undefined" && hitBall === persistentBigComboBall && (!hitBall.isExploding || hitBall.bigHitCount < 3))) {
ballChain.splice(removeIndex, 1);
}
break;
}
}
// Reindex remaining balls
for (var reindexK = 0; reindexK < ballChain.length; reindexK++) {
ballChain[reindexK].chainIndex = reindexK;
}
// Use centralized scoring function
handleBallHit(projectile.ballColor, hitBall.ballColor);
}
// Only create special effects for matching colors (excluding purple)
if (isColorMatch && !isPurpleBall && hitBall && hitBall.ballGraphics) {
// Create Maya-themed matching effect
tween(hitBall.ballGraphics, {
tint: 0xFFD700,
// Golden glow
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (hitBall && hitBall.ballGraphics) {
tween(hitBall.ballGraphics, {
tint: 0xFFFFFF,
// Return to normal
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 100,
easing: tween.easeIn
});
}
}
});
// Create energy burst effect around the hit ball
for (var effectIndex = 0; effectIndex < 4; effectIndex++) {
var energyParticle = LK.getAsset('ball_yellow', {
anchorX: 0.5,
anchorY: 0.5,
x: hitBall.x,
y: hitBall.y,
scaleX: 0.2,
scaleY: 0.2,
alpha: 0.9
});
game.addChild(energyParticle);
var angle = effectIndex * 90 * Math.PI / 180;
var distance = 80;
tween(energyParticle, {
x: hitBall.x + Math.cos(angle) * distance,
y: hitBall.y + Math.sin(angle) * distance,
alpha: 0,
scaleX: 0.05,
scaleY: 0.05
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
if (energyParticle.parent) {
energyParticle.parent.removeChild(energyParticle);
}
}
});
}
// Combo logic: Now also applies to purple balls!
if (isColorMatch) {
// --- Combo logic: Only count combo if same color as previous hit ---
if (typeof comboCount === "undefined") comboCount = 1;
if (typeof comboColor === "undefined") comboColor = hitBall.ballColor;
if (comboColor === hitBall.ballColor) {
comboCount++;
} else {
// Only reset combo if the color actually changed
if (comboCount > 1) {
// Optionally, you can trigger a combo break effect here if needed
}
comboCount = 1;
comboColor = hitBall.ballColor;
}
// --- Combo Popup and Effects ---
if (comboCount > 1) {
// Show combo popup text at hitBall position
var comboText = new Text2('COMBO x' + comboCount + '!', {
size: 90 + Math.min(comboCount * 10, 120),
fill: 0xFFD700,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
comboText.anchor.set(0.5, 0.5);
comboText.x = hitBall.x;
comboText.y = hitBall.y - 80;
game.addChild(comboText);
// Animate popup: scale up, float up, fade out
tween(comboText, {
scaleX: 1.4,
scaleY: 1.4,
y: comboText.y - 120,
alpha: 0
}, {
duration: 1400,
// Increased from 700 to 1400ms for longer visibility
easing: tween.easeOut,
onFinish: function onFinish() {
if (comboText.parent) comboText.parent.removeChild(comboText);
}
});
// Flash screen for high combos
if (comboCount >= 5) {
LK.effects.flashScreen(0xFFD700, 200 + comboCount * 30);
}
// Add a little shake to the game for big combos
if (comboCount >= 7) {
var shakeAmount = Math.min(comboCount * 2, 20);
var originalX = game.x || 0;
var originalY = game.y || 0;
var shakeTicks = 12;
var shakeTimer = LK.setInterval(function () {
game.x = originalX + (Math.random() - 0.5) * shakeAmount;
game.y = originalY + (Math.random() - 0.5) * shakeAmount;
shakeTicks--;
if (shakeTicks <= 0) {
LK.clearInterval(shakeTimer);
game.x = originalX;
game.y = originalY;
}
}, 16);
}
// --- Extra balls for combos: spawn extra balls for each combo popup ---
var extraComboBalls = Math.min(2 + Math.floor(comboCount / 2), 8);
for (var extraComboI = 0; extraComboI < extraComboBalls; extraComboI++) {
addBallToChain();
}
}
// Her doğru vuruştan sonra ortaya çıkan top sayısını artır (kombo stili)
var ballsToAdd = Math.min(5 + comboCount, 20); // Kombo ile artan, bolca top
for (var comboI = 0; comboI < ballsToAdd; comboI++) {
addBallToChain();
}
// Her doğru vuruşta toplar çok yavaş hareket etsin
chainSpeed = 0.05; // Çok yavaş hareket
// --- Combo: Track per-color hit counts for 2x big, 50-point ball after 3 hits of same color ---
// Only increment color hit count if there is no persistent big combo ball on screen
if (typeof colorHitCounts === "undefined") colorHitCounts = {};
if (typeof persistentBigComboBall === "undefined" || !persistentBigComboBall) {
// Initialize color hit count if not present
if (!colorHitCounts[hitBall.ballColor]) {
colorHitCounts[hitBall.ballColor] = 0;
}
colorHitCounts[hitBall.ballColor]++;
}
// Track persistent 2x big combo ball
if (typeof persistentBigComboBall === "undefined") persistentBigComboBall = null;
if (typeof persistentBigComboBallColor === "undefined") persistentBigComboBallColor = null;
// When 3 hits of the same color (at any time), spawn a 2x big random color ball and keep it until next 3 hits of any color
if (colorHitCounts[hitBall.ballColor] === 3) {
// Remove previous persistent big combo ball if it exists
if (persistentBigComboBall && persistentBigComboBall.parent) {
persistentBigComboBall.parent.removeChild(persistentBigComboBall);
}
// Pick a random color for the big ball
var bigColors = ballColors.slice();
var randomBigColor = bigColors[Math.floor(Math.random() * bigColors.length)];
var bigBall = new Ball(randomBigColor);
bigBall.chainIndex = ballChain.length;
bigBall.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0;
// Büyük top için scale artır
bigBall.ballGraphics.scaleX = 2.0;
bigBall.ballGraphics.scaleY = 2.0;
// Mark as big and worth 50 points, and require 3 hits to destroy
bigBall.isBigComboBall = true;
bigBall.bigHitCount = 0; // Track hits for this big ball
// Mark as persistent
bigBall.isPersistentBigComboBall = true;
persistentBigComboBall = bigBall;
persistentBigComboBallColor = hitBall.ballColor;
ballChain.push(bigBall);
game.addChild(bigBall);
updateBallPosition(bigBall);
// 50 puan ekle
LK.setScore(LK.getScore() + 50);
// Reset this color's hit count
colorHitCounts[hitBall.ballColor] = 0;
}
// Remove persistent big combo ball only if it was hit 3 times
if (persistentBigComboBall && persistentBigComboBall.isExploding) {
persistentBigComboBall = null;
persistentBigComboBallColor = null;
}
} else {
// Reset combo on wrong color or purple
if (typeof comboCount !== "undefined" && comboCount > 2) {
// Combo break effect: speed up chain and burst color
chainSpeed = 0.35 + Math.random() * 0.1;
// Color burst at frog
var burstColors = [0xff4444, 0x44ff44, 0x4444ff, 0xffff44, 0xff44ff];
for (var burstI = 0; burstI < 7; burstI++) {
var burst = LK.getAsset('ball_' + ballColors[Math.floor(Math.random() * ballColors.length)], {
anchorX: 0.5,
anchorY: 0.5,
x: frog.x,
y: frog.y,
scaleX: 0.3,
scaleY: 0.3,
alpha: 0.8,
tint: burstColors[burstI % burstColors.length]
});
game.addChild(burst);
var angle = Math.random() * Math.PI * 2;
var dist = 120 + Math.random() * 60;
tween(burst, {
x: frog.x + Math.cos(angle) * dist,
y: frog.y + Math.sin(angle) * dist,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
if (burst.parent) burst.parent.removeChild(burst);
}
});
}
}
comboCount = 0;
comboColor = undefined;
// Reset unique color combo
if (typeof colorComboHits !== "undefined") colorComboHits = {};
if (typeof colorComboOrder !== "undefined") colorComboOrder = [];
}
}
// Remove projectile immediately after hit
projectile.active = false;
if (projectile.parent) {
projectile.parent.removeChild(projectile);
}
projectiles.splice(j, 1);
if (!ballDestroyed) {
insertProjectileIntoChain(projectile, insertIndex);
}
}
}
// --- Continuous, looping ball and group spawns until score is zero ---
// Mor top yendikten sonra bir süre mor topların spawn oranını ve karmaşıklığını artır
if (typeof purpleFrenzyTicks === "undefined") {
var purpleFrenzyTicks = 0;
var purpleFrenzyEndTick = 0;
}
if (typeof lastPurpleFrenzyTriggerTick === "undefined") {
var lastPurpleFrenzyTriggerTick = -10000;
}
// Mor top yendikten sonra tetiklenecek fonksiyon
if (typeof triggerPurpleFrenzy === "undefined") {
var triggerPurpleFrenzy = function triggerPurpleFrenzy() {
purpleFrenzyTicks = 0;
purpleFrenzyEndTick = LK.ticks + 600 + Math.floor(Math.random() * 300); // 10-15 saniye
lastPurpleFrenzyTriggerTick = LK.ticks;
// Speed up chain and shake screen for a moment
chainSpeed = 0.45;
LK.effects.flashScreen(0xAA00FF, 400);
var shakeTicks = 18;
var originalX = game.x || 0;
var originalY = game.y || 0;
var shakeTimer = LK.setInterval(function () {
game.x = originalX + (Math.random() - 0.5) * 18;
game.y = originalY + (Math.random() - 0.5) * 18;
shakeTicks--;
if (shakeTicks <= 0) {
LK.clearInterval(shakeTimer);
game.x = originalX;
game.y = originalY;
}
}, 16);
};
}
// Mor top yendikten sonra Ball.destroyOnHit içinde triggerPurpleFrenzy() çağrılır
if (LK.getScore() > 0) {
// Timers for next ball and group spawn
if (typeof nextBallSpawnTick === "undefined") {
var nextBallSpawnTick = LK.ticks + 10 + Math.floor(Math.random() * 30); // 0.16-0.66s (was 0.5-2s)
var nextBallGroupSpawnTick = LK.ticks + 40 + Math.floor(Math.random() * 60); // 0.66-1.66s (was 2-5s)
var nextChaosSpawnTick = LK.ticks + 60 + Math.floor(Math.random() * 100); // 1-2.66s (was 3-8s)
}
// Mor top fazlalığı aktif mi?
var purpleFrenzyActive = purpleFrenzyEndTick > LK.ticks;
// Random single ball spawns
if (LK.ticks >= nextBallSpawnTick && ballChain.length < maxChainLength) {
var ballsToAdd = purpleFrenzyActive ? 4 + Math.floor(Math.random() * 3) : 2 + Math.floor(Math.random() * 3); // 4-6 balls if frenzy, else 2-4
for (var i = 0; i < ballsToAdd; i++) {
// Frenzy sırasında %40 mor top, %60 random
if (purpleFrenzyActive && Math.random() < 0.4) {
var ball = new Ball('purple');
ball.chainIndex = ballChain.length;
ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0;
ballChain.push(ball);
game.addChild(ball);
updateBallPosition(ball);
} else {
addBallToChain();
}
}
// Next spawn in 0.1-0.33s if frenzy, else 0.16-0.66s
nextBallSpawnTick = LK.ticks + (purpleFrenzyActive ? 6 + Math.floor(Math.random() * 14) : 10 + Math.floor(Math.random() * 30));
}
// Random group spawns
if (LK.ticks >= nextBallGroupSpawnTick && ballChain.length < maxChainLength - 5) {
var doGroup = Math.random() < (purpleFrenzyActive ? 0.95 : 0.85); // 95% group if frenzy, 85% else
if (doGroup) {
// Frenzy sırasında grup içinde mor toplar karışık
if (purpleFrenzyActive && Math.random() < 0.5) {
var groupSize = 5 + Math.floor(Math.random() * 3); // 5-7 balls
for (var gi = 0; gi < groupSize; gi++) {
var color = Math.random() < 0.5 ? 'purple' : ballColors[Math.floor(Math.random() * ballColors.length)];
var ball = new Ball(color);
ball.chainIndex = ballChain.length;
ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0;
ballChain.push(ball);
game.addChild(ball);
updateBallPosition(ball);
}
} else {
addBallGroup();
}
} else {
addBallToChain();
addBallToChain();
addBallToChain();
}
// Next group in 0.5-1.25s if frenzy, else 0.66-1.66s
nextBallGroupSpawnTick = LK.ticks + (purpleFrenzyActive ? 30 + Math.floor(Math.random() * 45) : 40 + Math.floor(Math.random() * 60));
}
// Rare chaos burst: lots of balls at once, unpredictable
if (LK.ticks >= nextChaosSpawnTick && ballChain.length < maxChainLength - 8) {
var chaosCount = purpleFrenzyActive ? 10 + Math.floor(Math.random() * 6) : 6 + Math.floor(Math.random() * 5); // 10-15 balls if frenzy, else 6-10
for (var i = 0; i < chaosCount; i++) {
if (purpleFrenzyActive && Math.random() < 0.5) {
var color = Math.random() < 0.7 ? 'purple' : ballColors[Math.floor(Math.random() * ballColors.length)];
var ball = new Ball(color);
ball.chainIndex = ballChain.length;
ball.pathPosition = ballChain.length > 0 ? ballChain[ballChain.length - 1].pathPosition + 70 : 0;
ballChain.push(ball);
game.addChild(ball);
updateBallPosition(ball);
} else if (Math.random() < 0.5) {
addBallToChain();
} else {
addBallGroup();
}
}
// Next chaos in 0.66-1.33s if frenzy, else 1-2.66s
nextChaosSpawnTick = LK.ticks + (purpleFrenzyActive ? 40 + Math.floor(Math.random() * 40) : 60 + Math.floor(Math.random() * 100));
}
} else {
// If score is zero, stop all new ball spawns
// Optionally, you could clear timers or handle end-of-loop logic here
}
// Update UI
scoreTxt.setText('Score: ' + LK.getScore());
ballUsageTxt.setText('Top Used: ' + (typeof ballsUsed !== "undefined" ? ballsUsed : 0));
// Check if all balls are scattered off screen (game over condition)
var allBallsOffScreen = ballChain.length > 0;
for (var m = 0; m < ballChain.length; m++) {
if (!ballChain[m].isOffScreen && !ballChain[m].isExploding) {
allBallsOffScreen = false;
break;
}
}
if (allBallsOffScreen && ballChain.length > 0) {
gameRunning = false;
// Custom "The End" screen with green frame and music
showTheEndScreen();
return;
}
// End the game if ballsUsed reaches 50
if (typeof ballsUsed !== "undefined" && ballsUsed >= 50) {
gameRunning = false;
showTheEndScreen();
return;
}
// Win condition - clear all balls
if (ballChain.length === 0) {
gameRunning = false;
// Reset color hit counts and persistent big combo ball on win
if (typeof colorHitCounts !== "undefined") colorHitCounts = {};
if (typeof persistentBigComboBall !== "undefined") persistentBigComboBall = null;
if (typeof persistentBigComboBallColor !== "undefined") persistentBigComboBallColor = null;
// Rainbow burst effect at center
for (var rb = 0; rb < 12; rb++) {
var colorIdx = rb % ballColors.length;
var burst = LK.getAsset('ball_' + ballColors[colorIdx], {
anchorX: 0.5,
anchorY: 0.5,
x: centerX,
y: centerY,
scaleX: 0.5,
scaleY: 0.5,
alpha: 1
});
game.addChild(burst);
var angle = rb * (Math.PI * 2 / 12);
var dist = 400 + Math.random() * 120;
tween(burst, {
x: centerX + Math.cos(angle) * dist,
y: centerY + Math.sin(angle) * dist,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
if (burst.parent) burst.parent.removeChild(burst);
}
});
}
// Slow motion effect before win
var oldChainSpeed = chainSpeed;
chainSpeed = 0.01;
LK.setTimeout(function () {
chainSpeed = oldChainSpeed;
LK.setScore(LK.getScore() + 1000); // Bonus for clearing
showTheEndScreen();
}, 900);
return;
}
// --- Custom "The End" screen with green frame and music ---
function showTheEndScreen() {
// Stop all music and play end music (use 'final' sound as end music)
LK.stopMusic();
LK.playMusic('final', {
loop: false,
fade: {
start: 0,
end: 1,
duration: 1200
}
});
// Overlay: green frame
var frameThickness = 32;
var frameColor = 0x00ff44;
var frameAlpha = 0.85;
// Top
var topFrame = LK.getAsset('bud', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: frameThickness,
tint: frameColor,
alpha: frameAlpha
});
// Bottom
var bottomFrame = LK.getAsset('bud', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2732 - frameThickness,
width: 2048,
height: frameThickness,
tint: frameColor,
alpha: frameAlpha
});
// Left
var leftFrame = LK.getAsset('bud', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: frameThickness,
height: 2732,
tint: frameColor,
alpha: frameAlpha
});
// Right
var rightFrame = LK.getAsset('bud', {
anchorX: 0,
anchorY: 0,
x: 2048 - frameThickness,
y: 0,
width: frameThickness,
height: 2732,
tint: frameColor,
alpha: frameAlpha
});
// "The End" text
var theEndText = new Text2('THE END', {
size: 260,
fill: 0x00FF44,
font: "'GillSans-Bold',Impact,'Arial Black',Tahoma"
});
theEndText.anchor.set(0.5, 0.5);
theEndText.x = 2048 / 2;
theEndText.y = 2732 / 2;
// Add to game overlay
game.addChild(topFrame);
game.addChild(bottomFrame);
game.addChild(leftFrame);
game.addChild(rightFrame);
game.addChild(theEndText);
// Animate "The End" text (pulse)
tween(theEndText, {
scaleX: 1.18,
scaleY: 1.18
}, {
duration: 900,
yoyo: true,
repeat: 2,
easing: tween.easeInOut
});
// Optionally, fade out all gameplay elements
for (var i = 0; i < game.children.length; i++) {
var obj = game.children[i];
if (obj !== topFrame && obj !== bottomFrame && obj !== leftFrame && obj !== rightFrame && obj !== theEndText) {
tween(obj, {
alpha: 0.18
}, {
duration: 900,
easing: tween.easeOut
});
}
}
}
};
// Initialize the game
createInitialChain();
;
// Play background music 'amazon'
LK.playMusic('amazon');
// Minimalistic tween library for animations
kocaman gözleri olan ve ağzında olan her renge göre renk değiştiren bir bukalemun. In-Game asset. 2d. High contrast. No shadows
minik ve çok sevimli bir yusufçuk. In-Game asset. 2d. High contrast. No shadows
minik sevimli bir tırtıl. In-Game asset. 2d. High contrast. No shadows
çirkin ve sevimsiz kara sinek mor renkli bir kısmı. In-Game asset. 2d. High contrast. No shadows
çok güzel renkleri olan ağırlıklı olarak kırmızı renkli kelebek. In-Game asset. 2d. High contrast. No shadows
yeşil yapraklar ve ağaçlar olan orta bir alan. In-Game asset. 2d. High contrast. No shadows
oval kütük küçük bir meydan gibi üstten görünümlü. In-Game asset. 2d. High contrast. No shadows
çember şeklinde bir görseli çerçeve içine alan çevresi yeşillik çerçeve. In-Game asset. 2d. High contrast. No shadows