User prompt
her türlü Kaleyi nişanlayıp Son Golü vursun !
User prompt
Dediğim gibi Velocity sini yükselt Nagi 5. aşamaya geldiğinde Topu kaleye atarken
User prompt
Şutu attıktan sonra 150 saniye Cooldown a girsin ve sadece top çalabilsin etrafından
User prompt
Nagi otomatik aşırı yıkıcı bir şut göndersin kaleye dümdüz hiç bir oyuncunun engelleyemediği sonra 150 saniye Cooldown a girsin 5 Staged Revolver Fake Volley Reo Ego veremesin Nagi ye Cooldown da
User prompt
Kaiser ekle Opponent e ! Kaiser de 2 Şut özelliği olsun biri Normal kaiser impact diyeride falsolu Kaiser:Impact Magnuss Flow a girince ! ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Nagi otomatik aşırı yıkıcı bir şut göndersin kaleye dümdüz hiç bir oyuncunun engelleyemediği sonra 150 saniye Cooldown a girsin 5 Staged Revolver Fake Volley Reo Ego veremesin Nagi ye Cooldown da ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Kaiser ekle karşı tarafa ! Kaiser de 2 Şut özelliği olsun biri Normal kaiser impact diyeride falsolu Kaiser:Impact Magnuss Flow a girince ! ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
karşı takıma Aiku Barou Rin ve Kaiser Ekle ! ve yarat hepsine yeni güzel özellikler ekle ! Kaiser de 2 Şut özelliği olsun biri Normal kaiser impact diyeride falsolu Kaiser:Impact Magnuss Flow a girince ! ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
5 Staged Revolver Fake Volley 4 tane Fak Vole vururken 4 kişi üzerine gelen tamamen dursun ve Nagi 5. de konuşmaya devam etsin türkçe bir şekilde artık uyandığını belirtip kaleye şut atsın ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Timeout.tick error: Cannot use 'in' operator to search for 'alpha' in null' in or related to this line: 'tween(self.thankYouText, {' Line Number: 2410
User prompt
Diyer kişilerden uzaklaşıp önünü açıp vurması gerekiyor biraz karmaşıklaştırmak yani Nagi yi(5 Staged Revolver Fake Volley de ) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Nagi kalabalıktan uzaklaşıp Ekranda ''teşekkürler Reo...'' dedikten sonra ilk Fake Vole de yani Stage leri sürekli ''1..'' diye ekranda yazmalı ve 5. de yani topu vurup gol atacağı zaman !
User prompt
top Nagi de olmalı 5 Staged Revolver Fake Volley için ve dediğim gibi durdurulamaz bir şut atmalı Nagi
User prompt
5 Staged Revolver Volley yap Nagi için ! Reo da Nagi nin Egosunu ateşlesin ! ve Nagi Flow a girip 5 kişiyi Stun layıp veya 5 kere 1 kişiStun layıp çalımlayıp Çok sert bir şekilde kaleye göndersin ! ses dosyası yarat bide ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
vede karakterler asla kendi kalelerine atamasınlar ve topu Rakip kaleye atmaya çalışsınlar tek amaçları topu alıp en hızlıca Topu karşı kaleye ulaştırmak
User prompt
her karakter için şut yarat ? lütfen Karakterler top sürerek kaleye giriyorlar istemiyorum bunu
User prompt
karşı tarafın forveti gibi karşı kaleye gol atabilsin her karakter
User prompt
Kale görüş açılarını ve menzillerine girdimi ''Shot'' özelliğini kullanıp topu kaleye göndersinler ve Dribbling ile kaleye giremesinler
User prompt
''Shot'' diye bi skill ekle ve ''Pas'' diye bir skill ekle hepsine
User prompt
kaleye en yakın olana pas versinler
User prompt
Topu kaleye atmaya çalışsınlar seçtikleri kişi pas versinler ve pas isteyebilsinler
User prompt
tüm oyuncu sınıflarına şut atma ekle oyuncu gibi
User prompt
doğalına döndür her karakteri
User prompt
Yapay zekaları yeniden porgramla düşünebilsinler birkaç olasılığı ve daha aktif olsunlar sürekli haraket etsinler
User prompt
tüm karakter normal haraket etsin ve futbol oynasın yeteneklerini aktif olarak kullansın haraket etsinler defansa atağa topu alamya yardım etsinler !
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var ChigiriPlayer = Container.expand(function (colorOverride) { var self = Container.call(this); var opponentGraphics = self.attachAsset('opponent', { anchorX: 0.5, anchorY: 0.5 }); if (typeof colorOverride === "number" && opponentGraphics) { opponentGraphics.tint = colorOverride; } self.speed = 5; self.role = 'chigiri'; self.homeX = 800; self.homeY = 2000; self.maxDistance = 600; self.hasBall = false; self.isSpeedBursting = false; self.speedBurstTarget = null; self.speedBurstCooldown = 0; self.speedBurstCooldownTime = 8000; // 8 seconds self.normalSpeed = 5; self.burstSpeed = 12; self.stamina = 100; self.maxStamina = 100; self.staminaDrainRate = 0.8; self.staminaRegenRate = 0.3; self.lowStaminaSpeed = 1.5; // Very slow when stamina is low self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, hiori, reo, nagi, bachira, chigiri]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // Update speed burst cooldown if (self.speedBurstCooldown > 0) { self.speedBurstCooldown -= 16; if (self.speedBurstCooldown < 0) self.speedBurstCooldown = 0; } // Steal ball if opponent is inside area var nearestOpponent = null; var nearestDistance = Infinity; for (var i = 0; i < opponents.length; i++) { var opponentDist = Math.sqrt((opponents[i].x - self.x) * (opponents[i].x - self.x) + (opponents[i].y - self.y) * (opponents[i].y - self.y)); if (opponentDist < nearestDistance) { nearestDistance = opponentDist; nearestOpponent = opponents[i]; } } if (nearestOpponent && nearestDistance < 80 && nearestOpponent.hasBall) { // Steal the ball nearestOpponent.hasBall = false; ball.active = true; // Ball moves toward Chigiri var stealDx = self.x - nearestOpponent.x; var stealDy = self.y - nearestOpponent.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 8; ball.velocityY = stealDy / stealDist * 8; } ballDetachCooldown = ballDetachCooldownTime; // Stun the opponent for 2 seconds after stealing ball nearestOpponent.stunned = true; nearestOpponent.originalSpeed = nearestOpponent.speed; nearestOpponent.speed = 0; tween(nearestOpponent, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(nearestOpponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.setTimeout(function () { nearestOpponent.stunned = false; nearestOpponent.speed = nearestOpponent.originalSpeed || 2.5; }, 2000); } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Coordinated ball pursuit - only chase if designated as active pursuer if (self.coordinatedRole === 'pursuer' && distance < 800 && !self.isSpeedBursting) { // Chase ball with hybrid intelligence - increased range and speed var chaseSpeed = distance < 150 ? self.normalSpeed * 1.8 : self.normalSpeed * 1.5; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; } else if (self.coordinatedRole !== 'pursuer' && !self.isSpeedBursting) { // Move to support position instead of chasing ball var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole); var supportDx = supportPos.x - self.x; var supportDy = supportPos.y - self.y; var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDist > 50) { var supportSpeed = self.normalSpeed * 0.8; self.x += supportDx / supportDist * supportSpeed; self.y += supportDy / supportDist * supportSpeed; } } // Speed burst mechanic if (!self.isSpeedBursting && self.speedBurstCooldown <= 0 && distance < 100) { // Throw ball forward and start speed burst var throwX = opponentGoal.x + (Math.random() - 0.5) * 200; var throwY = opponentGoal.y + 200; var throwDx = throwX - ball.x; var throwDy = throwY - ball.y; var throwDist = Math.sqrt(throwDx * throwDx + throwDy * throwDy); if (throwDist > 0) { ball.velocityX = throwDx / throwDist * 14; ball.velocityY = throwDy / throwDist * 14; } // Set speed burst target and activate self.speedBurstTarget = { x: throwX, y: throwY }; self.isSpeedBursting = true; self.speedBurstCooldown = self.speedBurstCooldownTime; // Start cooldown // Visual effect for speed burst tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); } if (self.isSpeedBursting && self.speedBurstTarget) { // Move at burst speed toward target var burstDx = self.speedBurstTarget.x - self.x; var burstDy = self.speedBurstTarget.y - self.y; var burstDist = Math.sqrt(burstDx * burstDx + burstDy * burstDy); if (burstDist > 30) { self.x += burstDx / burstDist * self.burstSpeed; self.y += burstDy / burstDist * self.burstSpeed; // Drain stamina during speed burst self.stamina -= self.staminaDrainRate * 2; // Double drain during burst if (self.stamina < 0) self.stamina = 0; } else { // Reached target, stop speed burst self.isSpeedBursting = false; self.speedBurstTarget = null; } } else { // Stamina regeneration when not moving much var currentSpeed = Math.sqrt((self.x - (self.lastMoveX || self.x)) * (self.x - (self.lastMoveX || self.x)) + (self.y - (self.lastMoveY || self.y)) * (self.y - (self.lastMoveY || self.y))); if (currentSpeed < 2 && self.stamina < self.maxStamina) { self.stamina += self.staminaRegenRate; if (self.stamina > self.maxStamina) self.stamina = self.maxStamina; } self.lastMoveX = self.x; self.lastMoveY = self.y; // Determine current speed based on stamina var currentMoveSpeed = self.stamina > 20 ? self.normalSpeed : self.lowStaminaSpeed; // Normal hybrid behavior - enhanced ball pursuit if (distance < 500) { // Move toward ball with improved midfielder intelligence but adjust for stamina var pursuitSpeed = distance < 200 ? currentMoveSpeed * 1.3 : currentMoveSpeed * 1.1; self.x += dx / distance * pursuitSpeed; self.y += dy / distance * pursuitSpeed; // Drain stamina during pursuit self.stamina -= self.staminaDrainRate * 0.5; if (self.stamina < 0) self.stamina = 0; // Coordinated strategic play when close to ball if (distance < 80) { // Try strategic passing first if (formationCoordinator.executeStrategicPass(self)) { // Strategic pass executed } else { // Fallback to individual decision making // Check for nearby teammates to pass to var nearestTeammate = null; var nearestTeammateDistance = Infinity; var teammates = [hiori, reo, nagi, bachira, player]; for (var t = 0; t < teammates.length; t++) { if (teammates[t] && teammates[t] !== self) { var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y)); if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) { nearestTeammateDistance = teammateDist; nearestTeammate = teammates[t]; } } } // 30% chance to pass if teammate available, otherwise shoot if (nearestTeammate && Math.random() < 0.3) { var passDx = nearestTeammate.x - ball.x; var passDy = nearestTeammate.y - ball.y; var passDistance = Math.sqrt(passDx * passDx + passDy * passDy); if (passDistance > 0) { ball.velocityX = passDx / passDistance * 10; ball.velocityY = passDy / passDistance * 10; } } else { // Shoot at goal var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 11; ball.velocityY = goalDy / goalDistance * 11; } } } } // Move to tactical position when not actively involved with ball if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) { var tacticalDx = self.tacticalX - self.x; var tacticalDy = self.tacticalY - self.y; var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy); if (tacticalDist > 80) { var tacticalSpeed = self.speed * 0.6; self.x += tacticalDx / tacticalDist * tacticalSpeed; self.y += tacticalDy / tacticalDist * tacticalSpeed; } } } else { // Return to defensive position var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { // Use stamina-adjusted speed for returning home var currentMoveSpeed = self.stamina > 20 ? self.normalSpeed : self.lowStaminaSpeed; self.x += homeDx / homeDistance * currentMoveSpeed * 0.4; self.y += homeDy / homeDistance * currentMoveSpeed * 0.4; // Light stamina drain when returning to position self.stamina -= self.staminaDrainRate * 0.3; if (self.stamina < 0) self.stamina = 0; } } } }; // 1v1 dribble move logic for Chigiri self.lastX = self.lastX || self.x; self.lastY = self.lastY || self.y; self.lastDribbleAttempt = self.lastDribbleAttempt || 0; self.isDribbling1v1 = self.isDribbling1v1 || false; self.dribble1v1Start = self.dribble1v1Start || 0; self.dribble1v1Duration = 350; // ms self.dribble1v1TargetX = self.dribble1v1TargetX || 0; self.dribble1v1TargetY = self.dribble1v1TargetY || 0; var origUpdate = self.update; self.update = function () { // 1v1 dribble move: if close to opponent and moving toward them, trigger a dribble move var closestOpponent = null; var closestDist = Infinity; for (var i = 0; i < opponents.length; i++) { var opp = opponents[i]; var dist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y)); if (dist < closestDist) { closestDist = dist; closestOpponent = opp; } } if (!self.isDribbling1v1 && Date.now() - (self.lastDribbleAttempt || 0) > 1200 && closestDist < 110 && closestOpponent && !closestOpponent.stunned) { // Check if moving toward opponent var vx = self.x - (self.lastX || self.x); var vy = self.y - (self.lastY || self.y); var toOppX = closestOpponent.x - self.x; var toOppY = closestOpponent.y - self.y; var dot = vx * toOppX + vy * toOppY; if (dot > 0) { // Initiate dribble move: dash past opponent self.isDribbling1v1 = true; self.dribble1v1Start = Date.now(); self.lastDribbleAttempt = Date.now(); var oppDist = Math.sqrt(toOppX * toOppX + toOppY * toOppY); var overshoot = 80; self.dribble1v1TargetX = self.x + toOppX / oppDist * (oppDist + overshoot); self.dribble1v1TargetY = self.y + toOppY / oppDist * (oppDist + overshoot); // Stun the opponent briefly closestOpponent.stunned = true; closestOpponent.originalSpeed = closestOpponent.speed; closestOpponent.speed = 0; tween(closestOpponent, { tint: 0xAAAAAA, scaleX: 1.1, scaleY: 1.1 }, { duration: 200, onFinish: function onFinish() { tween(closestOpponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.setTimeout(function () { closestOpponent.stunned = false; closestOpponent.speed = closestOpponent.originalSpeed || 2.5; }, 1000); // Visual effect for Chigiri tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 120, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); } } // Animate dribble move if (self.isDribbling1v1) { var t = Math.min((Date.now() - self.dribble1v1Start) / self.dribble1v1Duration, 1); // Use linear interpolation for smooth movement, but only for the duration of the dribble self.x = self.lastX + (self.dribble1v1TargetX - self.lastX) * t; self.y = self.lastY + (self.dribble1v1TargetY - self.lastY) * t; if (t >= 1) { self.isDribbling1v1 = false; } } else { // Only update lastX/lastY when not dribbling, to avoid jitter self.lastX = self.x; self.lastY = self.y; } origUpdate(); }; return self; }); var DefensePlayer = Container.expand(function (colorOverride) { var self = Container.call(this); var opponentGraphics = self.attachAsset('opponent', { anchorX: 0.5, anchorY: 0.5 }); if (typeof colorOverride === "number" && opponentGraphics) { opponentGraphics.tint = colorOverride; } self.speed = 2.5; self.role = 'defense'; self.homeX = 1024; self.homeY = 600; self.maxDistance = 300; self.hasBall = false; self.update = function () { // Steal ball if player is inside opponent var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); if (playerDist < 80 && player.hasBall) { // Steal the ball player.hasBall = false; ball.active = true; // Ball moves away from player, toward defense var stealDx = self.x - player.x; var stealDy = self.y - player.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 10; ball.velocityY = stealDy / stealDist * 10; } // Prevent immediate re-attachment ballDetachCooldown = ballDetachCooldownTime; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Smart defensive positioning based on ball Y coordinate if (ball.y > 1500) { // Ball is in midfield/defense area - stay in position and intercept var interceptX = 1024; // Stay central var interceptY = Math.max(600, ball.y - 200); // Position between ball and goal var interceptDx = interceptX - self.x; var interceptDy = interceptY - self.y; var interceptDist = Math.sqrt(interceptDx * interceptDx + interceptDy * interceptDy); if (interceptDist > 50) { self.x += interceptDx / interceptDist * self.speed * 0.6; self.y += interceptDy / interceptDist * self.speed * 0.6; } } // Defense priority: desperately try to get the ball if (distance < 250) { // Move toward ball - reduced chase range and speed self.x += dx / distance * (self.speed * 0.8); self.y += dy / distance * (self.speed * 0.8); // Sliding tackle if player is close to defense var playerDistance = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); if (playerDistance < 120 && ball.active && !player.hasBall) { // Slide tackle: dash toward ball and clear it away var slideDx = ball.x - self.x; var slideDy = ball.y - self.y; var slideDist = Math.sqrt(slideDx * slideDx + slideDy * slideDist); if (slideDist > 0) { // Animate slide (quick move) tween(self, { x: self.x + slideDx / slideDist * 60, y: self.y + slideDy / slideDist * 60 }, { duration: 120, easing: tween.cubicOut }); // Clear ball far away from player var clearX = ball.x < 1024 ? ball.x - 400 : ball.x + 400; var clearY = ball.y - 500; var clearDx = clearX - ball.x; var clearDy = clearY - ball.y; var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy); if (clearDist > 0) { ball.velocityX = clearDx / clearDist * 13; ball.velocityY = clearDy / clearDist * 13; } } } // Aggressive ball stealing when player team has ball and is near goal var ballCarrierNearGoal = false; var allPlayerTeam = [player, chigiri, hiori, reo, nagi, bachira]; for (var p = 0; p < allPlayerTeam.length; p++) { if (allPlayerTeam[p] && allPlayerTeam[p].hasBall && allPlayerTeam[p].y < 1000) { ballCarrierNearGoal = true; var carrierDist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y)); if (carrierDist < 150) { // Steal ball from carrier allPlayerTeam[p].hasBall = false; ball.active = true; // Ball moves toward defense var stealDx = self.x - allPlayerTeam[p].x; var stealDy = self.y - allPlayerTeam[p].y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 12; ball.velocityY = stealDy / stealDist * 12; } ballDetachCooldown = ballDetachCooldownTime; break; } } } // 1v1 defensive dribble when ball carrier approaches if (!self.isDribbling1v1Defense) self.isDribbling1v1Defense = false; if (!self.lastDefenseDribble) self.lastDefenseDribble = 0; var closestPlayerTeammate = null; var closestPlayerDist = Infinity; for (var p = 0; p < allPlayerTeam.length; p++) { if (allPlayerTeam[p]) { var dist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y)); if (dist < closestPlayerDist) { closestPlayerDist = dist; closestPlayerTeammate = allPlayerTeam[p]; } } } if (!self.isDribbling1v1Defense && Date.now() - self.lastDefenseDribble > 2000 && closestPlayerDist < 120 && closestPlayerTeammate) { // Check if player is moving toward defense var toPlayerX = closestPlayerTeammate.x - self.x; var toPlayerY = closestPlayerTeammate.y - self.y; if (toPlayerY < 0) { // Player moving toward goal // Defensive 1v1 dribble: block and steal self.isDribbling1v1Defense = true; self.lastDefenseDribble = Date.now(); // Move to block path var blockX = self.x + toPlayerX * 0.5; var blockY = self.y + toPlayerY * 0.5; tween(self, { x: blockX, y: blockY, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { // Try to steal ball or slow down player if (closestPlayerTeammate.hasBall) { closestPlayerTeammate.hasBall = false; ball.active = true; ball.velocityX = (Math.random() - 0.5) * 8; ball.velocityY = -5; ballDetachCooldown = ballDetachCooldownTime; } // Stun player briefly closestPlayerTeammate.stunned = true; closestPlayerTeammate.originalSpeed = closestPlayerTeammate.speed; closestPlayerTeammate.speed = closestPlayerTeammate.speed * 0.3; LK.setTimeout(function () { closestPlayerTeammate.stunned = false; closestPlayerTeammate.speed = closestPlayerTeammate.originalSpeed || 3; }, 1500); tween(self, { scaleX: 1, scaleY: 1 }, { duration: 200 }); self.isDribbling1v1Defense = false; } }); } } // Kick ball away from player goal if close enough if (distance < 80) { // Find nearest forward player var nearestForward = null; var nearestDistance = Infinity; for (var i = 0; i < opponents.length; i++) { if (opponents[i].role === 'forward') { var forwardDx = opponents[i].x - self.x; var forwardDy = opponents[i].y - self.y; var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy); if (forwardDistance < nearestDistance) { nearestDistance = forwardDistance; nearestForward = opponents[i]; } } } // Pass to forward if found, otherwise clear as before if (nearestForward) { var passDx = nearestForward.x - ball.x; var passDy = nearestForward.y - ball.y; var passDistance = Math.sqrt(passDx * passDx + passDy * passDy); if (passDistance > 0) { ball.velocityX = passDx / passDistance * 10; ball.velocityY = passDy / passDistance * 10; } } else { var kickAwayX = ball.x < 1024 ? ball.x - 200 : ball.x + 200; var kickAwayY = ball.y - 300; // Kick upfield var kickDx = kickAwayX - ball.x; var kickDy = kickAwayY - ball.y; var kickDistance = Math.sqrt(kickDx * kickDx + kickDy * kickDy); if (kickDistance > 0) { ball.velocityX = kickDx / kickDistance * 8; ball.velocityY = kickDy / kickDistance * 8; } } } } else { // Return to defensive position var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { self.x += homeDx / homeDistance * self.speed * 0.5; self.y += homeDy / homeDistance * self.speed * 0.5; } } }; return self; }); var ForwardPlayer = Container.expand(function () { var self = Container.call(this); var opponentGraphics = self.attachAsset('opponent', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2.8; self.role = 'forward'; self.homeX = 1024; self.homeY = 1400; self.hasBall = false; self.directShotCooldown = self.directShotCooldown || 0; self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, chigiri, hiori, reo, nagi, bachira]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // Steal ball if player is inside opponent var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); if (playerDist < 80 && player.hasBall) { // Steal the ball player.hasBall = false; ball.active = true; // Ball moves away from player, toward forward var stealDx = self.x - player.x; var stealDy = self.y - player.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 10; ball.velocityY = stealDy / stealDist * 10; } // Prevent immediate re-attachment ballDetachCooldown = ballDetachCooldownTime; } // Defensive behavior when ball is in opponent's half and player team has ball var allPlayerTeam = [player, chigiri, hiori, reo, nagi, bachira]; var playerTeamHasBall = false; var ballCarrierDistance = Infinity; var ballCarrier = null; for (var p = 0; p < allPlayerTeam.length; p++) { if (allPlayerTeam[p] && allPlayerTeam[p].hasBall) { playerTeamHasBall = true; var carrierDist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y)); if (carrierDist < ballCarrierDistance) { ballCarrierDistance = carrierDist; ballCarrier = allPlayerTeam[p]; } break; } } // If player team has ball and forward is close, try to steal if (playerTeamHasBall && ballCarrier && ballCarrierDistance < 100) { // Forward 1v1 defensive dribble if (!self.lastDefensiveMove) self.lastDefensiveMove = 0; if (Date.now() - self.lastDefensiveMove > 1500) { self.lastDefensiveMove = Date.now(); // Quick tackle move var tackleX = ballCarrier.x; var tackleY = ballCarrier.y; tween(self, { x: tackleX, y: tackleY, scaleX: 1.3, scaleY: 1.3, tint: 0xFFFF00 }, { duration: 300, onFinish: function onFinish() { // Steal ball if still close var finalDist = Math.sqrt((ballCarrier.x - self.x) * (ballCarrier.x - self.x) + (ballCarrier.y - self.y) * (ballCarrier.y - self.y)); if (finalDist < 80 && ballCarrier.hasBall) { ballCarrier.hasBall = false; ball.active = true; ball.velocityX = (Math.random() - 0.5) * 6; ball.velocityY = Math.random() * -8; ballDetachCooldown = ballDetachCooldownTime; } tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); } } // Handle direct shot cooldown if (self.directShotCooldown > 0) { self.directShotCooldown -= 16; if (self.directShotCooldown < 0) self.directShotCooldown = 0; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Forward: attack and try to score - more aggressive if (distance < 500) { // Move toward ball with pursuit var pursuitSpeed = distance < 200 ? self.speed * 1.5 : self.speed * 1.2; self.x += dx / distance * pursuitSpeed; self.y += dy / distance * pursuitSpeed; // If close to ball, shoot at goal if (distance < 80) { // Direct Shot every 30s if (self.directShotCooldown <= 0) { var goalDx = playerGoal.x - ball.x; var goalDy = playerGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { // Direct Shot: fast and straight ball.velocityX = goalDx / goalDistance * 12; ball.velocityY = goalDy / goalDistance * 12; } self.directShotCooldown = 30000; // 30 seconds } else { // Normal shot var goalDx = playerGoal.x - ball.x; var goalDy = playerGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 10; ball.velocityY = goalDy / goalDistance * 10; } } } } else { // Move toward attacking position var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { self.x += homeDx / homeDistance * self.speed * 0.4; self.y += homeDy / homeDistance * self.speed * 0.4; } } }; return self; }); var Goal = Container.expand(function () { var self = Container.call(this); // Goal area var goalGraphics = self.attachAsset('goal', { anchorX: 0.5, anchorY: 0.5 }); goalGraphics.alpha = 0.3; // Left goalpost var leftPost = self.addChild(LK.getAsset('goalpost', { anchorX: 0.5, anchorY: 0.5 })); leftPost.x = -210; leftPost.y = 0; // Right goalpost var rightPost = self.addChild(LK.getAsset('goalpost', { anchorX: 0.5, anchorY: 0.5 })); rightPost.x = 210; rightPost.y = 0; return self; }); var Goalkeeper = Container.expand(function (isPlayer) { var self = Container.call(this); var keeperGraphics = self.attachAsset(isPlayer ? 'player' : 'opponent', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; self.isPlayer = isPlayer; self.rushCooldown = 0; self.rushCooldownTime = 20000; // 20 seconds self.isRushing = false; self.originalX = 0; self.originalY = 0; self.rushSpeed = 8; self.update = function () { // Update rush cooldown if (self.rushCooldown > 0) { self.rushCooldown -= 16; // Approximately 60 FPS if (self.rushCooldown < 0) self.rushCooldown = 0; } // Check if should rush for ball var ballDistance = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y)); if (!self.isRushing && self.rushCooldown <= 0 && ballDistance < 400) { // Start rushing self.isRushing = true; self.rushCooldown = self.rushCooldownTime; } if (self.isRushing) { // Rush toward ball var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0 && distance > 50) { self.x += dx / distance * self.rushSpeed; self.y += dy / distance * self.rushSpeed; } else if (distance <= 50) { // Catch ball if close enough and force away from all players ball.velocityX = 0; ball.velocityY = 0; ball.x = self.x; ball.y = self.y; ball.active = false; ball.untouchable = true; // Make ball untouchable during goalkeeper control // Force ball away from any player who has it player.hasBall = false; var allPlayers = [chigiri, hiori, reo, nagi, bachira]; for (var p = 0; p < allPlayers.length; p++) { if (allPlayers[p] && allPlayers[p].hasBall) { allPlayers[p].hasBall = false; } } // Also force away from opponents for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o].hasBall) { opponents[o].hasBall = false; } } // Return to original position first self.isRushing = false; tween(self, { x: self.originalX, y: self.originalY }, { duration: 1000, onFinish: function onFinish() { // After returning to position, throw ball to center field ball.active = true; var centerX = 1024; var centerY = 1366; var throwPower = 15; var throwDx = centerX - ball.x; var throwDy = centerY - ball.y; var throwDistance = Math.sqrt(throwDx * throwDx + throwDy * throwDy); if (throwDistance > 0) { ball.velocityX = throwDx / throwDistance * throwPower; ball.velocityY = throwDy / throwDistance * throwPower; } } }); } } else { // Normal goalkeeper movement - stay near goal line var goalCenterX = 1024; var dx = goalCenterX - self.x; if (Math.abs(dx) > 5) { self.x += dx > 0 ? self.speed : -self.speed; } } }; return self; }); var HioriPlayer = Container.expand(function (colorOverride) { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); if (typeof colorOverride === "number" && playerGraphics) { playerGraphics.tint = colorOverride; } self.speed = 4; self.role = 'hiori'; self.homeX = 1200; self.homeY = 2000; self.hasBall = false; // FLOW SYSTEM self.flowActive = false; self.flowDribbleCount = 0; self.flowDribbleMax = 4; // 4 extra dribbles in Flow self.flowText = null; self.flowTriggerChecked = false; // To avoid retriggering // Zig-zag dribbling variables self.isDribbling = false; self.dribbleStartTime = 0; self.dribbleDuration = 2000; // 2 seconds - slower duration self.dribbleSpeed = 6; self.dribbleDirection = 1; // 1 for right, -1 for left self.dribblePhase = 0; // 0-1 progress through dribble self.dribbleStartX = 0; self.dribbleStartY = 0; self.dribbleTargetX = 0; self.dribbleTargetY = 0; // Zigzag pause variables self.zigzagCount = 0; self.zigzagPaused = false; self.zigzagPauseStart = 0; self.zigzagPauseDuration = 10000; // 10 seconds // Winter Zone variables self.winterZoneActive = false; self.winterZoneRadius = 200; self.winterZoneCooldown = 0; self.winterZoneCooldownTime = 12000; // 12 seconds self.winterZoneDuration = 0; self.winterZoneMaxDuration = 10000; // 10 seconds - increased duration self.slowedOpponents = []; // Winter Zone visual self.winterZoneVisual = null; // Perfect pass variables self.passChargingTime = 0; self.perfectPassCooldown = 0; self.perfectPassCooldownTime = 6000; // 6 seconds self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, chigiri, reo, nagi, bachira, hiori]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // FLOW TRIGGER: If 2 goals conceded, activate Flow for Hiori (only once) if (!self.flowActive && !self.flowTriggerChecked && typeof opponentScore !== "undefined" && opponentScore >= 2) { self.flowActive = true; self.flowDribbleCount = 0; self.flowTriggerChecked = true; // Show Flow text above Hiori if (!self.flowText) { self.flowText = self.addChild(new Text2('FLOW', { size: 48, fill: 0x00FFFF })); self.flowText.anchor.set(0.5, 0.5); self.flowText.x = 0; self.flowText.y = -100; self.flowText.alpha = 1; tween(self.flowText, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 300, onFinish: function onFinish() { tween(self.flowText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300 }); } }); } } // Remove Flow text if not in Flow if (!self.flowActive && self.flowText) { self.flowText.destroy(); self.flowText = null; } // Steal ball from nearby opponents and stun them for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (oppDist < 80 && opponent.hasBall) { // Steal the ball opponent.hasBall = false; ball.active = true; // Ball moves toward Hiori var stealDx = self.x - opponent.x; var stealDy = self.y - opponent.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 8; ball.velocityY = stealDy / stealDist * 8; } ballDetachCooldown = ballDetachCooldownTime; // Stun the opponent for 2 seconds after stealing ball opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; tween(opponent, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.setTimeout(function () { opponent.stunned = false; opponent.speed = opponent.originalSpeed || 2.5; }, 2000); break; } } // Update cooldowns if (self.winterZoneCooldown > 0) { self.winterZoneCooldown -= 16; if (self.winterZoneCooldown < 0) self.winterZoneCooldown = 0; } if (self.perfectPassCooldown > 0) { self.perfectPassCooldown -= 16; if (self.perfectPassCooldown < 0) self.perfectPassCooldown = 0; } // FLOW: If in Flow, boost Winter Zone and dribbling var flowWinterZoneRadius = self.flowActive ? 420 : 200; var flowWinterZoneSlow = self.flowActive ? 0.12 : 0.3; // 12% speed in Flow var flowWinterZoneDuration = self.flowActive ? 18000 : self.winterZoneMaxDuration; // 18s in Flow var flowWinterZoneCooldown = self.flowActive ? 6000 : self.winterZoneCooldownTime; // 6s in Flow // Winter Zone duration and effect if (self.winterZoneActive) { self.winterZoneDuration -= 16; if (self.winterZoneDuration <= 0) { self.winterZoneActive = false; // Remove Winter Zone visual if (self.winterZoneVisual) { self.winterZoneVisual.destroy(); self.winterZoneVisual = null; } // Restore opponent speeds for (var i = 0; i < self.slowedOpponents.length; i++) { var opponent = self.slowedOpponents[i]; opponent.speed = opponent.originalSpeed || opponent.speed * 2; } self.slowedOpponents = []; } else { // Apply Winter Zone effect to nearby opponents for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (dist <= (self.winterZoneActive ? flowWinterZoneRadius : self.winterZoneRadius)) { if (self.slowedOpponents.indexOf(opponent) === -1) { opponent.originalSpeed = opponent.speed; opponent.speed = opponent.speed * (self.winterZoneActive ? flowWinterZoneSlow : 0.3); // Much slower in Flow self.slowedOpponents.push(opponent); } } } } } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // FLOW: Dribbling logic if (self.isDribbling) { var elapsed = Date.now() - self.dribbleStartTime; var progress = Math.min(elapsed / self.dribbleDuration, 1); // Create zig-zag pattern - slower frequency for better visibility var zigzagAmplitude = 80; var zigzagFrequency = 4; var zigzagOffset = Math.sin(progress * Math.PI * zigzagFrequency) * zigzagAmplitude; // Move toward target with zig-zag motion - slower speed var targetDx = self.dribbleTargetX - self.dribbleStartX; var targetDy = self.dribbleTargetY - self.dribbleStartY; var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy); if (targetDist > 0) { // Calculate perpendicular vector for zig-zag var perpX = -targetDy / targetDist; var perpY = targetDx / targetDist; // Apply zig-zag movement with slower speed var slowSpeed = 0.6; // Reduced from 1.0 to make it slower self.x = self.dribbleStartX + targetDx * progress * slowSpeed + perpX * zigzagOffset; self.y = self.dribbleStartY + targetDy * progress * slowSpeed + perpY * zigzagOffset; // Ball follows during dribble if (distance < 100) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } } // End dribbling if (progress >= 1) { self.isDribbling = false; self.zigzagCount = (self.zigzagCount || 0) + 1; // FLOW: If in Flow, allow up to 4 extra dribbles in a row if (self.flowActive && self.flowDribbleCount < self.flowDribbleMax) { self.flowDribbleCount++; // Start another dribble immediately self.isDribbling = true; self.dribbleStartTime = Date.now(); self.dribbleStartX = self.x; self.dribbleStartY = self.y; // Dribble toward opponent goal, randomize a bit self.dribbleTargetX = opponentGoal.x + (Math.random() - 0.5) * 300; self.dribbleTargetY = opponentGoal.y + 200 + Math.random() * 100; } else { // After 4 zigzags, pause for 10 seconds if (self.zigzagCount >= 4) { self.zigzagPaused = true; self.zigzagPauseStart = Date.now(); self.zigzagCount = 0; } // Reset Flow dribble count after sequence if (self.flowActive) self.flowDribbleCount = 0; } } } else { // Coordinated ball pursuit - only chase if designated as active pursuer if (self.coordinatedRole === 'pursuer' && distance < 700) { var chaseSpeed = distance < 150 ? self.speed * 1.7 : self.speed * 1.4; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; } else if (self.coordinatedRole !== 'pursuer' && distance < 700) { // Move to support position instead of chasing ball var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole); var supportDx = supportPos.x - self.x; var supportDy = supportPos.y - self.y; var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDist > 50) { var supportSpeed = self.speed * 0.7; self.x += supportDx / supportDist * supportSpeed; self.y += supportDy / supportDist * supportSpeed; } // Handle zigzag pause if (self.zigzagPaused) { var pauseElapsed = Date.now() - self.zigzagPauseStart; if (pauseElapsed >= self.zigzagPauseDuration) { self.zigzagPaused = false; } } // Start zig-zag dribbling when close to ball (only if not paused) if (distance < 80 && !self.isDribbling && !self.zigzagPaused) { self.isDribbling = true; self.dribbleStartTime = Date.now(); self.dribbleStartX = self.x; self.dribbleStartY = self.y; // Dribble toward opponent goal self.dribbleTargetX = opponentGoal.x + (Math.random() - 0.5) * 300; self.dribbleTargetY = opponentGoal.y + 200; // FLOW: If in Flow, reset dribble count for new sequence if (self.flowActive) self.flowDribbleCount = 0; } } } // FLOW: Winter Zone is much bigger, lasts longer, slows more, and triggers more often if (!self.winterZoneActive && self.winterZoneCooldown <= 0) { var nearbyOpponents = 0; for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (dist <= flowWinterZoneRadius + 50) { nearbyOpponents++; } } if (nearbyOpponents >= 2) { // Activate Winter Zone self.winterZoneActive = true; self.winterZoneDuration = flowWinterZoneDuration; self.winterZoneCooldown = flowWinterZoneCooldown; // Create Winter Zone visual area if (self.winterZoneVisual) { self.winterZoneVisual.destroy(); self.winterZoneVisual = null; } self.winterZoneVisual = self.addChild(LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: self.flowActive ? 8 : 4, scaleY: self.flowActive ? 8 : 4, tint: 0x87CEEB })); self.winterZoneVisual.alpha = 0.3; // Visual effect tween(self, { scaleX: 1.5, scaleY: 1.5, tint: 0x87CEEB }, { duration: 200, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); } } // Strategic playmaking when close to ball if (distance < 80) { // Hiori as playmaker prioritizes strategic passing if (formationCoordinator.executeStrategicPass(self)) { // Strategic pass executed } else { // Check for nearby teammates to pass to var nearestTeammate = null; var nearestTeammateDistance = Infinity; var teammates = [chigiri, reo, nagi, bachira, player]; for (var t = 0; t < teammates.length; t++) { if (teammates[t] && teammates[t] !== self) { var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y)); if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) { nearestTeammateDistance = teammateDist; nearestTeammate = teammates[t]; } } } // FLOW: If in Flow, perfect pass always goes directly to player, no curve, and is instant if (self.perfectPassCooldown <= 0) { // Look for player to pass to var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); if (playerDist < 600 && playerDist > 100) { // Execute perfect pass var passDx = player.x - ball.x; var passDy = player.y - ball.y; var passDist = Math.sqrt(passDx * passDx + passDy * passDy); if (passDist > 0) { if (self.flowActive) { // FLOW: Pass is instant and direct ball.x = player.x; ball.y = player.y - 50; ball.velocityX = 0; ball.velocityY = 0; ball.active = false; player.hasBall = true; } else { // Normal perfect pass with slight curve to avoid interception var curveAmount = 3; ball.velocityX = passDx / passDist * 12 + curveAmount; ball.velocityY = passDy / passDist * 12; ball.active = true; } self.perfectPassCooldown = self.perfectPassCooldownTime; // Visual effect for perfect pass tween(ball, { scaleX: 1.3, scaleY: 1.3 }, { duration: 150, onFinish: function onFinish() { tween(ball, { scaleX: 1, scaleY: 1 }, { duration: 150 }); } }); } } } else if (nearestTeammate && Math.random() < 0.4) { // 40% chance to pass to teammate (higher as playmaker) var passDx = nearestTeammate.x - ball.x; var passDy = nearestTeammate.y - ball.y; var passDistance = Math.sqrt(passDx * passDx + passDy * passDy); if (passDistance > 0) { ball.velocityX = passDx / passDistance * 9; ball.velocityY = passDy / passDistance * 9; } } else { // Shoot at goal var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 12; ball.velocityY = goalDy / goalDistance * 12; } } } } // Move to tactical position when supporting team if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) { var tacticalDx = self.tacticalX - self.x; var tacticalDy = self.tacticalY - self.y; var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy); if (tacticalDist > 80) { var tacticalSpeed = self.speed * 0.5; self.x += tacticalDx / tacticalDist * tacticalSpeed; self.y += tacticalDy / tacticalDist * tacticalSpeed; } } // Return to home position when not actively playing if (distance > 500) { var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { self.x += homeDx / homeDistance * self.speed * 0.4; self.y += homeDy / homeDistance * self.speed * 0.4; } } }; return self; }); var MeguruBachiraPlayer = Container.expand(function (colorOverride) { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); if (typeof colorOverride === "number" && playerGraphics) { playerGraphics.tint = colorOverride; } self.speed = 4.5; self.role = 'bachira'; self.homeX = 1000; self.homeY = 1900; self.hasBall = false; // FLOW SYSTEM for Bachira self.flowActive = false; self.flowTriggerChecked = false; self.flowText = null; // Monster Trans variables removed // Roulette dribbling variables self.rouletteActive = false; self.rouletteStartTime = 0; self.rouletteDuration = 1500; // 1.5 seconds self.rouletteRotations = 3; // 3 full rotations self.rouletteRadius = 80; self.rouletteCooldown = 0; self.rouletteCooldownTime = 4000; // 4 seconds self.rouletteOriginX = 0; self.rouletteOriginY = 0; self.rouletteStunRadius = 120; self.rouletteStunnedOpponents = []; self.rouletteStunEnd = 0; // Monster Dribbling variables self.monsterDribblingActive = false; self.monsterDribblingStartTime = 0; self.monsterDribblingDuration = 2000; // 2 seconds self.monsterDribblingSpeed = 8; self.monsterDribblingCooldown = 0; self.monsterDribblingCooldownTime = 6000; // 6 seconds self.monsterDribblingStartX = 0; self.monsterDribblingStartY = 0; self.monsterDribblingTargetX = 0; self.monsterDribblingTargetY = 0; self.monsterDribblingStunRadius = 150; self.monsterDribblingStunnedOpponents = []; self.monsterDribblingStunEnd = 0; // Rush to ball variables (after Roulette) self.rushingToBall = false; self.rushTargetX = 0; self.rushTargetY = 0; self.rushSpeed = 7; self.readyToShoot = false; self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, chigiri, hiori, reo, nagi, bachira]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // FLOW TRIGGER: Monster Trans for Bachira, random chance for Hiori, only one Flow at a time if (!self.flowActive && !self.flowTriggerChecked) { // Count Bachira and Hiori on field var bachiraCount = typeof bachira !== "undefined" ? 1 : 0; var hioriCount = typeof hiori !== "undefined" ? 1 : 0; var totalFlowCandidates = bachiraCount + hioriCount; var shouldFlow = false; // If both Bachira and Hiori are present, randomly pick one for Flow if (totalFlowCandidates > 1) { // 50% chance for Bachira, 50% for Hiori var pick = Math.random(); if (pick < 0.5) { shouldFlow = true; // Bachira gets Flow if (typeof hiori !== "undefined") { hiori.flowActive = false; hiori.flowTriggerChecked = true; if (hiori.flowText) { hiori.flowText.destroy(); hiori.flowText = null; } } } else { shouldFlow = false; if (typeof hiori !== "undefined" && !hiori.flowActive && !hiori.flowTriggerChecked) { hiori.flowActive = true; hiori.flowTriggerChecked = true; hiori.flowDribbleCount = 0; // Show Flow text above Hiori if (!hiori.flowText) { hiori.flowText = hiori.addChild(new Text2('FLOW', { size: 48, fill: 0x00FFFF })); hiori.flowText.anchor.set(0.5, 0.5); hiori.flowText.x = 0; hiori.flowText.y = -100; hiori.flowText.alpha = 1; if (hiori.flowText) { // Defensive: only tween if flowText exists tween(hiori.flowText, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 300, onFinish: function onFinish() { if (hiori.flowText) { // Defensive: only tween if flowText still exists tween(hiori.flowText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300 }); } } }); } } } } } else { // Only Bachira or only Hiori present shouldFlow = true; } if (shouldFlow) { // Only set flowTriggerChecked, but do NOT activate Monster Trans/Flow yet self.flowTriggerChecked = true; // self.flowActive and self.monsterTransActive will be set when using Roulette or Monster Dribbling } } // Remove Flow text and effects if not in Flow if (!self.flowActive && self.flowText) { self.flowText.destroy(); self.flowText = null; } // Monster Trans visual cleanup removed // Steal ball from nearby opponents and stun them for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (oppDist < 80 && opponent.hasBall) { // Steal the ball opponent.hasBall = false; ball.active = true; // Ball moves toward Bachira var stealDx = self.x - opponent.x; var stealDy = self.y - opponent.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 8; ball.velocityY = stealDy / stealDist * 8; } ballDetachCooldown = ballDetachCooldownTime; // Stun the opponent for 2 seconds after stealing ball opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; tween(opponent, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.setTimeout(function () { opponent.stunned = false; opponent.speed = opponent.originalSpeed || 2.5; }, 2000); break; } } // Update cooldowns if (self.rouletteCooldown > 0) { self.rouletteCooldown -= 16; if (self.rouletteCooldown < 0) self.rouletteCooldown = 0; } if (self.monsterDribblingCooldown > 0) { // In Flow mode, no cooldowns if (!self.flowActive) { self.monsterDribblingCooldown -= 16; if (self.monsterDribblingCooldown < 0) self.monsterDribblingCooldown = 0; } else { self.monsterDribblingCooldown = 0; // No cooldown in Flow } } // Handle Roulette stun end if (self.rouletteStunEnd > 0 && Date.now() > self.rouletteStunEnd) { // Restore stunned opponents from Roulette for (var i = 0; i < self.rouletteStunnedOpponents.length; i++) { var opponent = self.rouletteStunnedOpponents[i]; opponent.stunned = false; opponent.originalSpeed = opponent.originalSpeed || opponent.speed; opponent.speed = opponent.originalSpeed; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } self.rouletteStunnedOpponents = []; self.rouletteStunEnd = 0; } // Handle Monster Dribbling stun end if (self.monsterDribblingStunEnd > 0 && Date.now() > self.monsterDribblingStunEnd) { // Restore stunned opponents from Monster Dribbling for (var i = 0; i < self.monsterDribblingStunnedOpponents.length; i++) { var opponent = self.monsterDribblingStunnedOpponents[i]; opponent.stunned = false; opponent.originalSpeed = opponent.originalSpeed || opponent.speed; opponent.speed = opponent.originalSpeed; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } self.monsterDribblingStunnedOpponents = []; self.monsterDribblingStunEnd = 0; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Roulette dribbling logic if (self.rouletteActive) { var elapsed = Date.now() - self.rouletteStartTime; var t = Math.min(elapsed / self.rouletteDuration, 1); // Create spinning motion around origin point var angle = t * self.rouletteRotations * 2 * Math.PI; self.x = self.rouletteOriginX + Math.cos(angle) * self.rouletteRadius; self.y = self.rouletteOriginY + Math.sin(angle) * self.rouletteRadius; // Ball follows during roulette if (distance < 100) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } // End roulette and apply stun effect if (t >= 1) { self.rouletteActive = false; self.applyRouletteStun(); } } // Monster Dribbling logic if (self.monsterDribblingActive) { var elapsed = Date.now() - self.monsterDribblingStartTime; var t = Math.min(elapsed / self.monsterDribblingDuration, 1); // Fast, erratic movement toward target var chaosAmplitude = 40; var chaosFrequency = 8; var chaosOffsetX = Math.sin(t * Math.PI * chaosFrequency) * chaosAmplitude; var chaosOffsetY = Math.cos(t * Math.PI * chaosFrequency * 1.3) * chaosAmplitude; // Move toward target with chaos var targetDx = self.monsterDribblingTargetX - self.monsterDribblingStartX; var targetDy = self.monsterDribblingTargetY - self.monsterDribblingStartY; self.x = self.monsterDribblingStartX + targetDx * t + chaosOffsetX; self.y = self.monsterDribblingStartY + targetDy * t + chaosOffsetY; // Ball follows during monster dribbling if (distance < 120) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } // Stun all opponents passed by during Monster Dribbling (only in Flow/Monster Trans) if (self.flowActive) { for (var i = 0; i < opponents.length; i++) { var opp = opponents[i]; var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y)); if (oppDist <= self.monsterDribblingStunRadius && !opp.stunned) { opp.stunned = true; opp.originalSpeed = opp.speed; opp.speed = 0; self.monsterDribblingStunnedOpponents.push(opp); tween(opp, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 250 }); } } if (self.monsterDribblingStunnedOpponents.length > 0) { self.monsterDribblingStunEnd = Date.now() + 4000; } } // End monster dribbling and apply stun effect if (t >= 1) { self.monsterDribblingActive = false; self.applyMonsterDribblingStun(); } } // Rush to ball after Roulette throw if (self.rushingToBall) { var rushDx = self.rushTargetX - self.x; var rushDy = self.rushTargetY - self.y; var rushDist = Math.sqrt(rushDx * rushDx + rushDy * rushDy); if (rushDist > 30) { // Rush toward the thrown ball location self.x += rushDx / rushDist * self.rushSpeed; self.y += rushDy / rushDist * self.rushSpeed; // Stun all opponents passed by during Monster Trans rush (only in Flow) if (self.flowActive) { for (var i = 0; i < opponents.length; i++) { var opp = opponents[i]; var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y)); if (oppDist <= self.monsterDribblingStunRadius && !opp.stunned) { opp.stunned = true; opp.originalSpeed = opp.speed; opp.speed = 0; self.monsterDribblingStunnedOpponents.push(opp); tween(opp, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 250 }); } } if (self.monsterDribblingStunnedOpponents.length > 0) { self.monsterDribblingStunEnd = Date.now() + 4000; } } } else { // Reached the target area, check if ball is nearby var ballDist = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y)); if (ballDist < 100) { // Ball is close, prepare to shoot self.readyToShoot = true; self.rushingToBall = false; // Control the ball briefly ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; // Immediately shoot toward opponent goal var goalDx = opponentGoal.x - self.x; var goalDy = opponentGoal.y - self.y; var goalDist = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDist > 0) { ball.velocityX = goalDx / goalDist * 18; // Powerful shot ball.velocityY = goalDy / goalDist * 18; ball.active = true; // Visual effect for powerful shot tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0xFF4500 }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: colorOverride || 0xFFFFFF }, { duration: 300 }); } }); } // Reset after shot self.readyToShoot = false; } else { // Ball not found at target, stop rushing self.rushingToBall = false; } } } else if (!self.rouletteActive && !self.monsterDribblingActive && self.coordinatedRole === 'pursuer' && distance < 700) { // Normal ball pursuit - more aggressive var chaseSpeed = distance < 150 ? self.speed * 1.7 : self.speed * 1.4; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; } else if (!self.rouletteActive && !self.monsterDribblingActive && self.coordinatedRole !== 'pursuer' && distance < 700) { // Move to support position instead of chasing ball var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole); var supportDx = supportPos.x - self.x; var supportDy = supportPos.y - self.y; var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDist > 50) { var supportSpeed = self.speed * 0.8; self.x += supportDx / supportDist * supportSpeed; self.y += supportDy / supportDist * supportSpeed; } // Creative wing play with tactical awareness if (distance < 80) { // Strategic passing consideration for wing play if (formationCoordinator.executeStrategicPass(self)) { // Strategic pass executed - Bachira creates from wide } else { // Check for nearby teammates to pass to var nearestTeammate = null; var nearestTeammateDistance = Infinity; var teammates = [chigiri, hiori, reo, nagi, player]; for (var t = 0; t < teammates.length; t++) { if (teammates[t] && teammates[t] !== self) { var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y)); if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) { nearestTeammateDistance = teammateDist; nearestTeammate = teammates[t]; } } } // 20% chance to pass if teammate available, 30% for abilities, 50% for goal shot var actionChoice = Math.random(); if (nearestTeammate && actionChoice < 0.2) { // Pass to teammate var passDx = nearestTeammate.x - ball.x; var passDy = nearestTeammate.y - ball.y; var passDistance = Math.sqrt(passDx * passDx + passDy * passDy); if (passDistance > 0) { ball.velocityX = passDx / passDistance * 11; ball.velocityY = passDy / passDistance * 11; } } else if (actionChoice < 0.5) { // Use special abilities (existing code) // Start abilities when close to ball if (distance < 80) { // Roulette (normal cooldown applies) if (!self.rouletteActive && self.rouletteCooldown <= 0) { var shouldUseRoulette = Math.random() < 0.4; // 40% chance if (shouldUseRoulette) { self.rouletteActive = true; self.rouletteStartTime = Date.now(); self.rouletteOriginX = self.x; self.rouletteOriginY = self.y; // If Flow was triggered for Bachira, activate Flow now if (self.flowTriggerChecked && !self.flowActive) { self.flowActive = true; // Show Flow text above Bachira if (!self.flowText) { self.flowText = self.addChild(new Text2('FLOW', { size: 48, fill: 0x00FFFF })); self.flowText.anchor.set(0.5, 0.5); self.flowText.x = 0; self.flowText.y = -100; self.flowText.alpha = 1; tween(self.flowText, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 300, onFinish: function onFinish() { tween(self.flowText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300 }); } }); } } if (!self.flowActive) { self.rouletteCooldown = self.rouletteCooldownTime; } // Visual effect for roulette start tween(self, { tint: 0x00FFFF }, { duration: 100, onFinish: function onFinish() { tween(self, { tint: colorOverride || 0xFFFFFF }, { duration: 100 }); } }); } } // Monster Dribbling (no cooldown in Flow) if (!self.monsterDribblingActive && (self.flowActive || self.monsterDribblingCooldown <= 0)) { var shouldUseMonsterDribbling = Math.random() < 0.6; // 60% chance if (shouldUseMonsterDribbling) { self.monsterDribblingActive = true; self.monsterDribblingStartTime = Date.now(); self.monsterDribblingStartX = self.x; self.monsterDribblingStartY = self.y; // Target toward opponent goal self.monsterDribblingTargetX = opponentGoal.x + (Math.random() - 0.5) * 200; self.monsterDribblingTargetY = opponentGoal.y + 300; // If Flow was triggered for Bachira, activate Flow now if (self.flowTriggerChecked && !self.flowActive) { self.flowActive = true; // Show Flow text above Bachira if (!self.flowText) { self.flowText = self.addChild(new Text2('FLOW', { size: 48, fill: 0x00FFFF })); self.flowText.anchor.set(0.5, 0.5); self.flowText.x = 0; self.flowText.y = -100; self.flowText.alpha = 1; tween(self.flowText, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 300, onFinish: function onFinish() { tween(self.flowText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300 }); } }); } } if (!self.flowActive) { self.monsterDribblingCooldown = self.monsterDribblingCooldownTime; } // Visual effect for monster dribbling start tween(self, { tint: 0xFF0000, scaleX: 1.3, scaleY: 1.3 }, { duration: 200, onFinish: function onFinish() { tween(self, { tint: colorOverride || 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); } } } } else { // Shoot at goal if no abilities used var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 13; ball.velocityY = goalDy / goalDistance * 13; } } } } // Move to tactical wing position when not directly involved if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) { var tacticalDx = self.tacticalX - self.x; var tacticalDy = self.tacticalY - self.y; var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy); if (tacticalDist > 80) { var tacticalSpeed = self.speed * 0.7; self.x += tacticalDx / tacticalDist * tacticalSpeed; self.y += tacticalDy / tacticalDist * tacticalSpeed; } } } else if (!self.rouletteActive && !self.monsterDribblingActive && distance > 500) { // Return to home position when not actively playing var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { self.x += homeDx / homeDistance * self.speed * 0.4; self.y += homeDy / homeDistance * self.speed * 0.4; } } }; self.applyRouletteStun = function () { var opponentsInRange = []; for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var opponentDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (opponentDist <= self.rouletteStunRadius) { opponentsInRange.push(opponent); } } // Stun opponents in range for (var i = 0; i < opponentsInRange.length; i++) { var opponent = opponentsInRange[i]; opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; // Completely stop them self.rouletteStunnedOpponents.push(opponent); // Visual effect for stunned opponent tween(opponent, { tint: 0x00FFFF, scaleX: 1.1, scaleY: 1.1 }, { duration: 200 }); } if (opponentsInRange.length > 0) { self.rouletteStunEnd = Date.now() + 1000; // 1 second stun } // Check if Bachira is in Flow and close to opponent goal for special message if (self.flowActive && typeof opponentGoal !== "undefined") { var goalDistance = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y)); if (goalDistance < 400) { // Close to goal // Create and display the special message var goalMessage = game.addChild(new Text2('Futbolu seviyorum... İçimdeki Canavarla birlikte !', { size: 64, fill: 0xFF4500 })); goalMessage.anchor.set(0.5, 0.5); goalMessage.x = 1024; // Center of screen goalMessage.y = 1000; // Middle of screen goalMessage.alpha = 0; // Animate message appearance tween(goalMessage, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500, onFinish: function onFinish() { // Keep message visible for 3 seconds then fade out LK.setTimeout(function () { tween(goalMessage, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 800, onFinish: function onFinish() { goalMessage.destroy(); } }); }, 3000); } }); } } // After Roulette, throw ball to edge and rush to it self.throwBallToEdgeAndRush(); }; self.applyMonsterDribblingStun = function () { var opponentsInRange = []; for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var opponentDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (opponentDist <= self.monsterDribblingStunRadius) { opponentsInRange.push(opponent); } } // Stun opponents in range for (var i = 0; i < opponentsInRange.length; i++) { var opponent = opponentsInRange[i]; opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; // Completely stop them self.monsterDribblingStunnedOpponents.push(opponent); // Visual effect for stunned opponent tween(opponent, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 250 }); } if (opponentsInRange.length > 0) { self.monsterDribblingStunEnd = Date.now() + 1000; // 1 second stun } }; self.throwBallToEdgeAndRush = function () { // Determine which edge to throw to (left or right side of field) var throwToLeft = self.x < 1024; // If Bachira is on left side, throw left; otherwise right var edgeX = throwToLeft ? 200 : 1848; // Near left or right edge var edgeY = self.y + (Math.random() - 0.5) * 200; // Slightly randomize Y position // Ensure edge Y is within field bounds if (edgeY < 300) edgeY = 300; if (edgeY > 2400) edgeY = 2400; // Throw ball to edge with moderate power var throwDx = edgeX - ball.x; var throwDy = edgeY - ball.y; var throwDist = Math.sqrt(throwDx * throwDx + throwDy * throwDy); if (throwDist > 0) { ball.velocityX = throwDx / throwDist * 10; ball.velocityY = throwDy / throwDist * 10; ball.active = true; } // Set rush target and start rushing self.rushTargetX = edgeX; self.rushTargetY = edgeY; self.rushingToBall = true; self.readyToShoot = false; // Visual effect for ball throw tween(ball, { scaleX: 1.3, scaleY: 1.3, tint: 0x00FFFF }, { duration: 200, onFinish: function onFinish() { tween(ball, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); }; return self; }); var MidfieldPlayer = Container.expand(function () { var self = Container.call(this); var opponentGraphics = self.attachAsset('opponent', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2.2; self.role = 'midfield'; self.homeX = 1024; self.homeY = 1000; self.hasBall = false; // Dribbling state self.isDribbling = false; self.dribbleStartTime = 0; self.dribbleDuration = 1000; self.dribbleRadius = 120; self.dribbleStartAngle = 0; self.dribbleEndAngle = 0; self.dribbleOriginX = 0; self.dribbleOriginY = 0; self.hasDribbled = false; self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, chigiri, hiori, reo, nagi, bachira]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // Steal ball if player is inside opponent var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); if (playerDist < 80 && player.hasBall) { // Steal the ball player.hasBall = false; ball.active = true; // Ball moves away from player, toward midfield var stealDx = self.x - player.x; var stealDy = self.y - player.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 10; ball.velocityY = stealDy / stealDist * 10; } // Prevent immediate re-attachment ballDetachCooldown = ballDetachCooldownTime; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Smart midfield positioning based on ball location if (ball.y > 1200) { // Ball is in player's half - position defensively var defensiveX = 1024; var defensiveY = Math.max(1000, ball.y - 300); var defDx = defensiveX - self.x; var defDy = defensiveY - self.y; var defDist = Math.sqrt(defDx * defDx + defDy * defDy); if (defDist > 50) { self.x += defDx / defDist * self.speed * 0.7; self.y += defDy / defDist * self.speed * 0.7; } } // Aggressive ball stealing from player team var allPlayerTeam = [player, chigiri, hiori, reo, nagi, bachira]; for (var p = 0; p < allPlayerTeam.length; p++) { if (allPlayerTeam[p] && allPlayerTeam[p].hasBall) { var carrierDist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y)); if (carrierDist < 120) { // Midfield tackle - steal ball allPlayerTeam[p].hasBall = false; ball.active = true; var stealDx = self.x - allPlayerTeam[p].x; var stealDy = self.y - allPlayerTeam[p].y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 10; ball.velocityY = stealDy / stealDist * 10; } ballDetachCooldown = ballDetachCooldownTime; break; } } } // Midfield: get ball and pass to forward - more aggressive if (distance < 400) { // Move toward ball when chasing var chaseSpeed = distance < 150 ? self.speed * 1.3 : self.speed * 1.1; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; // If close to ball, run away from player and pass to forward if (distance < 80) { // Move away from player for a moment var awayDx = self.x - player.x; var awayDy = self.y - player.y; var awayDist = Math.sqrt(awayDx * awayDx + awayDy * awayDy); if (awayDist > 0) { tween(self, { x: self.x + awayDx / awayDist * 120, y: self.y + awayDy / awayDist * 120 }, { duration: 200, easing: tween.cubicOut }); } // Find nearest forward player var nearestForward = null; var nearestDistance = Infinity; for (var i = 0; i < opponents.length; i++) { if (opponents[i].role === 'forward') { var forwardDx = opponents[i].x - self.x; var forwardDy = opponents[i].y - self.y; var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy); if (forwardDistance < nearestDistance) { nearestDistance = forwardDistance; nearestForward = opponents[i]; } } } // Pass to forward if found if (nearestForward) { // Perfect pass: ball goes directly to forward with good speed var passDx = nearestForward.x - ball.x; var passDy = nearestForward.y - ball.y; var passDistance = Math.sqrt(passDx * passDx + passDy * passDy); if (passDistance > 0) { ball.velocityX = passDx / passDistance * 10; ball.velocityY = passDy / passDistance * 10; } } else { // No forward to pass to: perform dribbling and curved shot if (!self.isDribbling) { // Start dribbling: move in a half-circle around the player self.isDribbling = true; self.dribbleStartTime = Date.now(); self.dribbleDuration = 1000; // 1 second self.dribbleRadius = 120; // Calculate angle from midfield to player var angleToPlayer = Math.atan2(player.y - self.y, player.x - self.x); self.dribbleStartAngle = angleToPlayer - Math.PI / 2; self.dribbleEndAngle = angleToPlayer + Math.PI / 2; self.dribbleOriginX = self.x; self.dribbleOriginY = self.y; self.hasDribbled = false; } if (self.isDribbling) { var elapsed = Date.now() - self.dribbleStartTime; var t = Math.min(elapsed / self.dribbleDuration, 1); // Interpolate angle for half-circle var angle = self.dribbleStartAngle + (self.dribbleEndAngle - self.dribbleStartAngle) * t; self.x = self.dribbleOriginX + Math.cos(angle) * self.dribbleRadius; self.y = self.dribbleOriginY + Math.sin(angle) * self.dribbleRadius; // Ball follows midfield during dribble if (distance < 80) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } // When dribble completes, shoot with curve if (t >= 1 && !self.hasDribbled) { self.hasDribbled = true; self.isDribbling = false; // Falsolu şut: apply curve by adding to X velocity var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { // Add curve: right-footed, so curve to the right (positive X) var curveAmount = 7; ball.velocityX = goalDx / goalDistance * 12 + curveAmount; ball.velocityY = goalDy / goalDistance * 12; ball.active = true; } } } } } } else { // Return to midfield position var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { self.x += homeDx / homeDistance * self.speed * 0.3; self.y += homeDy / homeDistance * self.speed * 0.3; } } }; return self; }); var NagiPlayer = Container.expand(function (colorOverride) { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); if (typeof colorOverride === "number" && playerGraphics) { playerGraphics.tint = colorOverride; } self.speed = 5.5; // Increased base speed for better movement self.role = 'nagi'; self.homeX = 1400; self.homeY = 2000; self.hasBall = false; // --- NEW NAGI ULTIMATE BALL CONTROL SYSTEM --- self.ballControlRange = 200; // Massive ball control range self.ballControlCooldown = 0; self.ballControlCooldownTime = 3000; // 3 second cooldown self.ballControlText = null; // --- PERFECTED FAKE VOLLEY SYSTEM --- self.fakeVolleyState = 0; // 0: ready, 1: fake motion, 2: real shot self.fakeVolleyCombo = 0; // Track combo count self.fakeVolleyMaxCombo = 3; // Maximum fake volleys before forced real shot self.fakeVolleyStunRadius = 180; // Larger stun radius self.fakeVolleyStunDuration = 1500; // 1.5 seconds stun self.fakeVolleyStunnedOpponents = []; self.fakeVolleyStunEnd = 0; self.fakeVolleyCooldown = 0; self.fakeVolleyCooldownTime = 4000; // 4 second cooldown // --- REVOLVER SHOT SYSTEM --- self.revolverActive = false; self.revolverCount = 0; self.revolverMaxShots = 5; // 5 rapid shots self.revolverShotDelay = 300; // 300ms between shots self.revolverLastShot = 0; self.revolverCooldown = 0; self.revolverCooldownTime = 15000; // 15 second cooldown // --- GENIUS TRAP SYSTEM --- self.geniusTrapRange = 400; // Ultra wide range self.geniusTrapActive = false; self.geniusTrapVisual = null; self.geniusTrapCooldown = 0; self.geniusTrapCooldownTime = 8000; // 8 second cooldown // --- FLOW SYSTEM --- self.flowActive = false; self.flowTriggerChecked = false; self.flowText = null; self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, chigiri, hiori, reo, nagi, bachira]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // --- FLOW TRIGGER: Random Flow activation when close to ball --- if (!self.flowActive && !self.flowTriggerChecked && Math.random() < 0.001) { // 0.1% chance per frame self.flowActive = true; self.flowTriggerChecked = true; // Show Flow text above Nagi if (!self.flowText) { self.flowText = self.addChild(new Text2('GENIUS FLOW', { size: 48, fill: 0x9966FF })); self.flowText.anchor.set(0.5, 0.5); self.flowText.x = 0; self.flowText.y = -100; self.flowText.alpha = 1; tween(self.flowText, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 300, onFinish: function onFinish() { tween(self.flowText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300 }); } }); } // Flow lasts for 10 seconds LK.setTimeout(function () { self.flowActive = false; if (self.flowText) { self.flowText.destroy(); self.flowText = null; } // Reset trigger after Flow ends LK.setTimeout(function () { self.flowTriggerChecked = false; }, 30000); // Can trigger again after 30 seconds }, 10000); } // Remove Flow text if not in Flow if (!self.flowActive && self.flowText) { self.flowText.destroy(); self.flowText = null; } // Update cooldowns if (self.ballControlCooldown > 0) { self.ballControlCooldown -= 16; if (self.ballControlCooldown < 0) self.ballControlCooldown = 0; } if (self.fakeVolleyCooldown > 0) { self.fakeVolleyCooldown -= 16; if (self.fakeVolleyCooldown < 0) self.fakeVolleyCooldown = 0; } if (self.revolverCooldown > 0) { self.revolverCooldown -= 16; if (self.revolverCooldown < 0) self.revolverCooldown = 0; } if (self.geniusTrapCooldown > 0) { self.geniusTrapCooldown -= 16; if (self.geniusTrapCooldown < 0) self.geniusTrapCooldown = 0; } // Handle stun end for fake volley if (self.fakeVolleyStunEnd > 0 && Date.now() > self.fakeVolleyStunEnd) { // Restore stunned opponents for (var i = 0; i < self.fakeVolleyStunnedOpponents.length; i++) { var opponent = self.fakeVolleyStunnedOpponents[i]; opponent.stunned = false; opponent.originalSpeed = opponent.originalSpeed || opponent.speed; opponent.speed = opponent.originalSpeed; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } self.fakeVolleyStunnedOpponents = []; self.fakeVolleyStunEnd = 0; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // --- ULTIMATE BALL CONTROL SYSTEM --- // Automatically steal ball from ANY opponent within massive range if (self.ballControlCooldown <= 0) { var controlledBall = false; // Check for opponents with ball within Ball Control range for (var i = 0; i < opponents.length; i++) { var opp = opponents[i]; if (opp.hasBall) { var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y)); if (oppDist <= self.ballControlRange) { // Instant ball steal from massive range opp.hasBall = false; ball.active = false; ball.x = self.x; ball.y = self.y + 40; ball.velocityX = 0; ball.velocityY = 0; self.hasBall = true; controlledBall = true; // Show "GENIUS BALL CONTROL" text if (!self.ballControlText) { self.ballControlText = self.addChild(new Text2('GENIUS BALL CONTROL', { size: 50, fill: 0x9966FF })); self.ballControlText.anchor.set(0.5, 0.5); self.ballControlText.x = 0; self.ballControlText.y = -100; tween(self.ballControlText, { alpha: 1, scaleX: 1.3, scaleY: 1.3 }, { duration: 300, onFinish: function onFinish() { LK.setTimeout(function () { tween(self.ballControlText, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 500, onFinish: function onFinish() { if (self.ballControlText) { self.ballControlText.destroy(); self.ballControlText = null; } } }); }, 2000); } }); } // Stun the opponent for longer duration opp.stunned = true; opp.originalSpeed = opp.speed; opp.speed = 0; tween(opp, { tint: 0x9966FF, scaleX: 1.3, scaleY: 1.3 }, { duration: 300, onFinish: function onFinish() { tween(opp, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } }); LK.setTimeout(function () { opp.stunned = false; opp.speed = opp.originalSpeed || 2.5; }, 3000); // 3 second stun self.ballControlCooldown = self.ballControlCooldownTime; break; } } } // Also control loose balls within range if (!controlledBall && distance <= self.ballControlRange && ball.active && !self.hasBall) { ball.active = false; ball.x = self.x; ball.y = self.y + 40; ball.velocityX = 0; ball.velocityY = 0; self.hasBall = true; self.ballControlCooldown = self.ballControlCooldownTime; if (!self.ballControlText) { self.ballControlText = self.addChild(new Text2('BALL CONTROL', { size: 40, fill: 0x9966FF })); self.ballControlText.anchor.set(0.5, 0.5); self.ballControlText.x = 0; self.ballControlText.y = -80; tween(self.ballControlText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { LK.setTimeout(function () { tween(self.ballControlText, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 300, onFinish: function onFinish() { if (self.ballControlText) { self.ballControlText.destroy(); self.ballControlText = null; } } }); }, 1500); } }); } } } // --- GENIUS TRAP SYSTEM --- // Show trap visual when ball is in range if (distance <= self.geniusTrapRange && !self.geniusTrapVisual && !self.hasBall) { self.geniusTrapVisual = self.addChild(LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, // Even larger visual scaleY: 8, tint: 0x9966FF })); self.geniusTrapVisual.alpha = 0.2; } else if (distance > self.geniusTrapRange && self.geniusTrapVisual) { self.geniusTrapVisual.destroy(); self.geniusTrapVisual = null; } // --- PERFECTED FAKE VOLLEY SYSTEM --- if (self.hasBall && distance < 100 && self.fakeVolleyCooldown <= 0) { if (self.fakeVolleyState === 0) { // Start fake volley sequence self.fakeVolleyState = 1; self.fakeVolleyCombo++; // Stun ALL nearby opponents with larger radius var opponentsInRange = []; for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var opponentDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (opponentDist <= self.fakeVolleyStunRadius) { opponentsInRange.push(opponent); } } for (var i = 0; i < opponentsInRange.length; i++) { var opponent = opponentsInRange[i]; opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; self.fakeVolleyStunnedOpponents.push(opponent); tween(opponent, { tint: 0xFFFF00, scaleX: 1.2, scaleY: 1.2 }, { duration: 200 }); } self.fakeVolleyStunEnd = Date.now() + self.fakeVolleyStunDuration; // Fake shot motion - very minimal ball movement to show it's fake ball.velocityX = (Math.random() - 0.5) * 1; ball.velocityY = (Math.random() - 0.5) * 1; ball.active = true; LK.setTimeout(function () { ball.active = false; ball.x = self.x; ball.y = self.y + 40; ball.velocityX = 0; ball.velocityY = 0; self.hasBall = true; }, 200); // Visual effect for fake motion tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0xFFFF00 }, { duration: 150, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 150 }); } }); // Set up for real shot or continue faking LK.setTimeout(function () { if (self.fakeVolleyCombo >= self.fakeVolleyMaxCombo) { // Must do real shot now self.fakeVolleyState = 2; } else { // Can choose to fake again or shoot self.fakeVolleyState = Math.random() < 0.6 ? 0 : 2; // 60% chance to fake again } }, 600); } else if (self.fakeVolleyState === 2) { // Real powerful shot self.fakeVolleyState = 0; self.fakeVolleyCombo = 0; self.fakeVolleyCooldown = self.fakeVolleyCooldownTime; var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 18; // More powerful shot ball.velocityY = goalDy / goalDistance * 18; ball.active = true; self.hasBall = false; } // Visual effect for real shot tween(self, { scaleX: 1.5, scaleY: 1.5, tint: 0xFF0000 }, { duration: 400, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 400 }); } }); } } // --- REVOLVER SHOT SYSTEM (Flow State) --- if (self.flowActive && self.hasBall && !self.revolverActive && self.revolverCooldown <= 0) { // Activate Revolver Shot sequence self.revolverActive = true; self.revolverCount = 0; self.revolverLastShot = Date.now(); // Show Revolver text var revolverText = self.addChild(new Text2('REVOLVER SHOT!', { size: 60, fill: 0xFF0000 })); revolverText.anchor.set(0.5, 0.5); revolverText.x = 0; revolverText.y = -140; tween(revolverText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, onFinish: function onFinish() { LK.setTimeout(function () { revolverText.destroy(); }, 2000); } }); } // Execute Revolver shots if (self.revolverActive && Date.now() - self.revolverLastShot >= self.revolverShotDelay) { self.revolverCount++; self.revolverLastShot = Date.now(); // Fire shot toward goal var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { // Add slight randomness to each shot var spread = (Math.random() - 0.5) * 100; ball.velocityX = (goalDx + spread) / goalDistance * 20; ball.velocityY = goalDy / goalDistance * 20; ball.active = true; } // Visual effect for each shot tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xFF6600 }, { duration: 100, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 100 }); } }); // End Revolver after all shots if (self.revolverCount >= self.revolverMaxShots) { self.revolverActive = false; self.revolverCooldown = self.revolverCooldownTime; self.hasBall = false; } } // Normal movement and positioning if (!self.revolverActive) { // Coordinated ball pursuit - only chase if designated as active pursuer if (self.coordinatedRole === 'pursuer' && distance < 700) { var chaseSpeed = distance < 150 ? self.speed * 1.8 : self.speed * 1.5; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; } else if (self.coordinatedRole !== 'pursuer' && distance < 700) { // Move to support position instead of chasing ball var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole); var supportDx = supportPos.x - self.x; var supportDy = supportPos.y - self.y; var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDist > 50) { var supportSpeed = self.speed * 1.1; // Faster support movement self.x += supportDx / supportDist * supportSpeed; self.y += supportDy / supportDist * supportSpeed; } // Strategic striker play when close to ball if (distance < 80 && !self.hasBall) { // Check for teammates to pass to if Nagi gets the ball var nearestTeammate = null; var nearestTeammateDistance = Infinity; var teammates = [chigiri, hiori, reo, bachira, player]; for (var t = 0; t < teammates.length; t++) { if (teammates[t] && teammates[t] !== self) { var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y)); if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) { nearestTeammateDistance = teammateDist; nearestTeammate = teammates[t]; } } } // 15% chance to pass to teammate, 85% for abilities/shooting (ultra striker focus) if (nearestTeammate && Math.random() < 0.15 && self.hasBall) { var passDx = nearestTeammate.x - ball.x; var passDy = nearestTeammate.y - ball.y; var passDistance = Math.sqrt(passDx * passDx + passDy * passDy); if (passDistance > 0) { ball.velocityX = passDx / passDistance * 15; // Stronger pass ball.velocityY = passDy / passDistance * 15; ball.active = true; self.hasBall = false; } } } // Striker positioning - always look for goal scoring opportunities if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) { var tacticalDx = self.tacticalX - self.x; var tacticalDy = self.tacticalY - self.y; var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy); if (tacticalDist > 80) { var tacticalSpeed = self.speed * 1.0; // Full speed tactical movement self.x += tacticalDx / tacticalDist * tacticalSpeed; self.y += tacticalDy / tacticalDist * tacticalSpeed; } } } else { // Return to home position when not actively playing var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { self.x += homeDx / homeDistance * self.speed * 0.6; // Faster return home self.y += homeDy / homeDistance * self.speed * 0.6; } } } }; // Steal ball from nearby opponents (enhanced version) for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (oppDist < 90 && opponent.hasBall && self.ballControlCooldown <= 0) { // Enhanced ball stealing opponent.hasBall = false; ball.active = false; ball.x = self.x; ball.y = self.y + 40; ball.velocityX = 0; ball.velocityY = 0; self.hasBall = true; self.ballControlCooldown = self.ballControlCooldownTime; // Stun the opponent for 2 seconds after stealing ball opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; tween(opponent, { tint: 0x9966FF, scaleX: 1.3, scaleY: 1.3 }, { duration: 300, onFinish: function onFinish() { tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } }); LK.setTimeout(function () { opponent.stunned = false; opponent.speed = opponent.originalSpeed || 2.5; }, 2000); break; } } return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; self.hasBall = false; // 1v1 dribble move logic for player self.lastX = self.lastX || self.x; self.lastY = self.lastY || self.y; self.lastDribbleAttempt = self.lastDribbleAttempt || 0; self.isDribbling1v1 = self.isDribbling1v1 || false; self.dribble1v1Start = self.dribble1v1Start || 0; self.dribble1v1Duration = 400; // ms self.dribble1v1TargetX = self.dribble1v1TargetX || 0; self.dribble1v1TargetY = self.dribble1v1TargetY || 0; self.update = function () { // Check for ball stealing from opponents for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (oppDist < 80 && opponent.hasBall) { // Steal the ball opponent.hasBall = false; ball.active = true; // Ball moves toward Player var stealDx = self.x - opponent.x; var stealDy = self.y - opponent.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 8; ball.velocityY = stealDy / stealDist * 8; } ballDetachCooldown = ballDetachCooldownTime; // Stun the opponent for 2 seconds after stealing ball opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; tween(opponent, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.setTimeout(function () { opponent.stunned = false; opponent.speed = opponent.originalSpeed || 2.5; }, 2000); break; } } // 1v1 dribble move: if player is close to an opponent and moving toward them, trigger a dribble move var closestOpponent = null; var closestDist = Infinity; for (var i = 0; i < opponents.length; i++) { var opp = opponents[i]; var dist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y)); if (dist < closestDist) { closestDist = dist; closestOpponent = opp; } } // Only allow dribble if not already dribbling, cooldown, and close enough if (!self.isDribbling1v1 && Date.now() - (self.lastDribbleAttempt || 0) > 1200 && closestDist < 110 && closestOpponent && !closestOpponent.stunned) { // Check if player is moving toward opponent var vx = self.x - (self.lastX || self.x); var vy = self.y - (self.lastY || self.y); var toOppX = closestOpponent.x - self.x; var toOppY = closestOpponent.y - self.y; var dot = vx * toOppX + vy * toOppY; if (dot > 0) { // Initiate dribble move: dash past opponent self.isDribbling1v1 = true; self.dribble1v1Start = Date.now(); self.lastDribbleAttempt = Date.now(); // Target a point just past the opponent var oppDist = Math.sqrt(toOppX * toOppX + toOppY * toOppY); var overshoot = 80; self.dribble1v1TargetX = self.x + toOppX / oppDist * (oppDist + overshoot); self.dribble1v1TargetY = self.y + toOppY / oppDist * (oppDist + overshoot); // Stun the opponent briefly closestOpponent.stunned = true; closestOpponent.originalSpeed = closestOpponent.speed; closestOpponent.speed = 0; tween(closestOpponent, { tint: 0xAAAAAA, scaleX: 1.1, scaleY: 1.1 }, { duration: 200, onFinish: function onFinish() { tween(closestOpponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.setTimeout(function () { closestOpponent.stunned = false; closestOpponent.speed = closestOpponent.originalSpeed || 2.5; }, 1000); // Visual effect for player tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 120, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); } } // Animate dribble move if (self.isDribbling1v1) { var t = Math.min((Date.now() - self.dribble1v1Start) / self.dribble1v1Duration, 1); // Use linear interpolation for smooth movement, but only for the duration of the dribble self.x = self.lastX + (self.dribble1v1TargetX - self.lastX) * t; self.y = self.lastY + (self.dribble1v1TargetY - self.lastY) * t; if (t >= 1) { self.isDribbling1v1 = false; } } else { // Only update lastX/lastY when not dribbling, to avoid jitter self.lastX = self.x; self.lastY = self.y; } }; return self; }); var ReoPlayer = Container.expand(function (colorOverride) { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); if (typeof colorOverride === "number" && playerGraphics) { playerGraphics.tint = colorOverride; } self.speed = 4; self.role = 'reo'; self.homeX = 600; self.homeY = 1800; self.hasBall = false; // Mode switching variables self.isDefensiveMode = true; self.defensiveColor = 0x000000; // Black self.attackingColor = 0xffa500; // Orange self.modeSwitch = 0; self.modeSwitchInterval = 5000; // 5 seconds // Chameleon ability variables self.chameleonCooldown = 0; self.chameleonInterval = 30000; // 30 seconds self.copiedStats = null; self.originalSpeed = 4; self.originalDribbling = 1; self.originalShot = 1; // Copied ability variables self.copiedWinterZone = false; self.copiedWinterZoneRadius = 200; self.copiedWinterZoneCooldown = 0; self.copiedWinterZoneActive = false; self.copiedWinterZoneDuration = 0; self.copiedWinterZoneMaxDuration = 8000; self.copiedSlowedOpponents = []; self.copiedWinterZoneVisual = null; self.copiedSpeedBurst = false; self.copiedSpeedBurstCooldown = 0; self.copiedSpeedBurstActive = false; self.copiedBurstSpeed = 10; self.copiedNormalSpeed = 4; self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, chigiri, hiori, reo, nagi, bachira]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // Steal ball from nearby opponents and stun them for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (oppDist < 80 && opponent.hasBall) { // Steal the ball opponent.hasBall = false; ball.active = true; // Ball moves toward Reo var stealDx = self.x - opponent.x; var stealDy = self.y - opponent.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 8; ball.velocityY = stealDy / stealDist * 8; } ballDetachCooldown = ballDetachCooldownTime; // Stun the opponent for 2 seconds after stealing ball opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; tween(opponent, { tint: 0xFF0000, scaleX: 1.2, scaleY: 1.2 }, { duration: 200, onFinish: function onFinish() { tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 200 }); } }); LK.setTimeout(function () { opponent.stunned = false; opponent.speed = opponent.originalSpeed || 2.5; }, 2000); break; } } // Update mode switching self.modeSwitch += 16; if (self.modeSwitch >= self.modeSwitchInterval) { self.modeSwitch = 0; self.isDefensiveMode = !self.isDefensiveMode; // Change color based on mode if (self.isDefensiveMode) { tween(playerGraphics, { tint: self.defensiveColor }, { duration: 300 }); } else { tween(playerGraphics, { tint: self.attackingColor }, { duration: 300 }); } } // Update Chameleon cooldown self.chameleonCooldown += 16; if (self.chameleonCooldown >= self.chameleonInterval) { self.chameleonCooldown = 0; self.activateChameleon(); } // Update copied abilities if (self.copiedWinterZone) { // Update Winter Zone cooldown if (self.copiedWinterZoneCooldown > 0) { self.copiedWinterZoneCooldown -= 16; if (self.copiedWinterZoneCooldown < 0) self.copiedWinterZoneCooldown = 0; } // Winter Zone duration and effect if (self.copiedWinterZoneActive) { self.copiedWinterZoneDuration -= 16; if (self.copiedWinterZoneDuration <= 0) { self.copiedWinterZoneActive = false; // Remove Winter Zone visual if (self.copiedWinterZoneVisual) { self.copiedWinterZoneVisual.destroy(); self.copiedWinterZoneVisual = null; } // Restore opponent speeds for (var i = 0; i < self.copiedSlowedOpponents.length; i++) { var opponent = self.copiedSlowedOpponents[i]; opponent.speed = opponent.originalSpeed || opponent.speed * 2; } self.copiedSlowedOpponents = []; } else { // Apply Winter Zone effect to nearby opponents for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (dist <= self.copiedWinterZoneRadius) { if (self.copiedSlowedOpponents.indexOf(opponent) === -1) { opponent.originalSpeed = opponent.speed; opponent.speed = opponent.speed * 0.3; // Slow to 30% speed self.copiedSlowedOpponents.push(opponent); } } } } } // Activate Winter Zone when opponents get close if (!self.copiedWinterZoneActive && self.copiedWinterZoneCooldown <= 0) { var nearbyOpponents = 0; for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y)); if (dist <= self.copiedWinterZoneRadius + 50) { nearbyOpponents++; } } if (nearbyOpponents >= 2) { // Activate Winter Zone self.copiedWinterZoneActive = true; self.copiedWinterZoneDuration = self.copiedWinterZoneMaxDuration; self.copiedWinterZoneCooldown = 12000; // 12 seconds cooldown // Create Winter Zone visual area self.copiedWinterZoneVisual = self.addChild(LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 4, tint: 0x87CEEB })); self.copiedWinterZoneVisual.alpha = 0.3; // Visual effect tween(self, { scaleX: 1.5, scaleY: 1.5, tint: 0x87CEEB }, { duration: 200, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor }, { duration: 200 }); } }); } } } if (self.copiedSpeedBurst) { // Update Speed Burst cooldown if (self.copiedSpeedBurstCooldown > 0) { self.copiedSpeedBurstCooldown -= 16; if (self.copiedSpeedBurstCooldown < 0) self.copiedSpeedBurstCooldown = 0; } // Speed burst when close to ball if (!self.copiedSpeedBurstActive && self.copiedSpeedBurstCooldown <= 0 && distance < 100) { self.copiedSpeedBurstActive = true; self.copiedSpeedBurstCooldown = 8000; // 8 seconds cooldown self.speed = self.copiedBurstSpeed; // Visual effect for speed burst tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); // Speed burst lasts for 2 seconds LK.setTimeout(function () { self.copiedSpeedBurstActive = false; self.speed = self.copiedNormalSpeed; }, 2000); } } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Mode-based behavior with coordination if (self.isDefensiveMode) { // Defensive mode: more active interception and ball pursuit if (self.coordinatedRole === 'pursuer' && distance < 750) { var defensiveSpeed = self.speed * 1.4; self.x += dx / distance * defensiveSpeed; self.y += dy / distance * defensiveSpeed; } else if (self.coordinatedRole !== 'pursuer' && distance < 750) { // Move to defensive support position var supportPos = ballPursuitCoordinator.getSupportPosition('defense'); var supportDx = supportPos.x - self.x; var supportDy = supportPos.y - self.y; var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDist > 50) { var supportSpeed = self.speed * 0.9; self.x += supportDx / supportDist * supportSpeed; self.y += supportDy / supportDist * supportSpeed; } // Defensive tackle if (distance < 80) { var clearX = ball.x < 1024 ? ball.x - 300 : ball.x + 300; var clearY = ball.y - 200; var clearDx = clearX - ball.x; var clearDy = clearY - ball.y; var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy); if (clearDist > 0) { ball.velocityX = clearDx / clearDist * 8; ball.velocityY = clearDy / clearDist * 8; } } } else { // Return to defensive position, but move more actively var homeDx = self.homeX - self.x; var homeDy = self.homeY - self.y; var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDistance > 50) { self.x += homeDx / homeDistance * self.speed * 0.7; self.y += homeDy / homeDistance * self.speed * 0.7; } } } else { // Attacking mode: chase ball very aggressively and support attack if (self.coordinatedRole === 'pursuer' && distance < 900) { var attackSpeed = self.speed * 2.0; self.x += dx / distance * attackSpeed; self.y += dy / distance * attackSpeed; } else if (self.coordinatedRole !== 'pursuer' && distance < 900) { // Move to attacking support position var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole); var supportDx = supportPos.x - self.x; var supportDy = supportPos.y - self.y; var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDist > 50) { var supportSpeed = self.speed * 1.2; self.x += supportDx / supportDist * supportSpeed; self.y += supportDy / supportDist * supportSpeed; } // Adaptive tactical play based on mode when close to ball if (distance < 80) { // Mode-based strategic decision making if (self.isDefensiveMode) { // Defensive mode - prioritize safe clearing and distribution if (formationCoordinator.executeStrategicPass(self)) { // Strategic defensive pass executed } else { // Fallback defensive action var clearX = ball.x < 1024 ? ball.x - 300 : ball.x + 300; var clearY = ball.y - 200; var clearDx = clearX - ball.x; var clearDy = clearY - ball.y; var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy); if (clearDist > 0) { ball.velocityX = clearDx / clearDist * 10; ball.velocityY = clearDy / clearDist * 10; } } } else { // Attacking mode - focus on progressive play if (formationCoordinator.executeStrategicPass(self)) { // Strategic attacking pass executed } else { // Check for nearby teammates to pass to var nearestTeammate = null; var nearestTeammateDistance = Infinity; var teammates = [chigiri, hiori, nagi, bachira, player]; for (var t = 0; t < teammates.length; t++) { if (teammates[t] && teammates[t] !== self) { var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y)); if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 450) { nearestTeammateDistance = teammateDist; nearestTeammate = teammates[t]; } } } // 35% chance to pass if teammate available, otherwise shoot if (nearestTeammate && Math.random() < 0.35) { var passDx = nearestTeammate.x - ball.x; var passDy = nearestTeammate.y - ball.y; var passDistance = Math.sqrt(passDx * passDx + passDy * passDy); if (passDistance > 0) { ball.velocityX = passDx / passDistance * 12; ball.velocityY = passDy / passDistance * 12; } } else { // Attack shot var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 14; ball.velocityY = goalDy / goalDistance * 14; } } } } } // Position based on tactical role and mode if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) { var tacticalDx = self.tacticalX - self.x; var tacticalDy = self.tacticalY - self.y; var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy); if (tacticalDist > 80) { var tacticalSpeed = self.speed * (self.isDefensiveMode ? 0.6 : 0.8); self.x += tacticalDx / tacticalDist * tacticalSpeed; self.y += tacticalDy / tacticalDist * tacticalSpeed; } } } else { // If not near ball, move up to a supporting attacking position var supportX = opponentGoal.x - 200; var supportY = opponentGoal.y + 400; var supportDx = supportX - self.x; var supportDy = supportY - self.y; var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy); if (supportDist > 30) { self.x += supportDx / supportDist * self.speed * 0.7; self.y += supportDy / supportDist * self.speed * 0.7; } } } }; self.activateChameleon = function () { // Find random NPC to copy stats from (including player team members) var availableNPCs = []; for (var i = 0; i < opponents.length; i++) { availableNPCs.push(opponents[i]); } // Also add player team members with special abilities if (typeof hiori !== 'undefined') availableNPCs.push(hiori); if (typeof chigiri !== 'undefined') availableNPCs.push(chigiri); if (typeof nagi !== 'undefined') availableNPCs.push(nagi); if (availableNPCs.length > 0) { var randomNPC = availableNPCs[Math.floor(Math.random() * availableNPCs.length)]; // Copy stats from random NPC self.copiedStats = { speed: randomNPC.speed || self.originalSpeed, dribbling: randomNPC.dribbleSpeed || self.originalDribbling, shot: randomNPC.burstSpeed || self.originalShot, role: randomNPC.role || 'reo' }; // Apply copied stats self.speed = self.copiedStats.speed; // Copy special abilities based on role if (self.copiedStats.role === 'hiori') { // Copy Winter Zone ability self.copiedWinterZone = true; self.copiedWinterZoneRadius = 200; self.copiedWinterZoneCooldown = 0; self.copiedWinterZoneActive = false; self.copiedWinterZoneDuration = 0; self.copiedWinterZoneMaxDuration = 8000; // Slightly shorter than original self.copiedSlowedOpponents = []; self.copiedWinterZoneVisual = null; } else if (self.copiedStats.role === 'chigiri') { // Copy Speed Burst ability self.copiedSpeedBurst = true; self.copiedSpeedBurstCooldown = 0; self.copiedSpeedBurstActive = false; self.copiedBurstSpeed = 10; self.copiedNormalSpeed = self.speed; } else { // Reset copied abilities self.copiedWinterZone = false; self.copiedSpeedBurst = false; } // Visual effect for Chameleon activation tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xff00ff }, { duration: 200, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor }, { duration: 200 }); } }); } }; return self; }); var SaePlayer = Container.expand(function (colorOverride) { var self = Container.call(this); var playerGraphics = self.attachAsset('opponent', { anchorX: 0.5, anchorY: 0.5 }); if (typeof colorOverride === "number" && playerGraphics) { playerGraphics.tint = colorOverride; } self.speed = 3.2; self.role = 'sae'; self.homeX = 1024; self.homeY = 800; self.hasBall = false; // Perfect Pass variables self.perfectPassRange = 800; self.perfectPassCooldown = 0; self.perfectPassCooldownTime = 8000; // 8 seconds // Half-circle dribbling variables self.isDribbling = false; self.dribbleStartTime = 0; self.dribbleDuration = 1500; // 1.5 seconds self.dribbleRadius = 100; self.dribbleStartAngle = 0; self.dribbleEndAngle = 0; self.dribbleOriginX = 0; self.dribbleOriginY = 0; self.dribbleStunRadius = 150; self.dribbleStunDuration = 2000; // 2 seconds self.dribbleStunnedOpponents = []; self.dribbleStunEnd = 0; // Zigzag dribbling variables self.isZigzagDribbling = false; self.zigzagStartTime = 0; self.zigzagDuration = 2000; // 2 seconds self.zigzagStartX = 0; self.zigzagStartY = 0; self.zigzagTargetX = 0; self.zigzagTargetY = 0; self.zigzagDevourRadius = 120; // Zigzag chain variables self.zigzagCount = 0; self.zigzagPaused = false; self.zigzagPauseStart = 0; self.zigzagPauseDuration = 10000; // 10 seconds pause after 6 zigzags // Chop dribbling variables self.isChopDribbling = false; self.chopStartTime = 0; self.chopDuration = 1000; // 1 second self.chopStartX = 0; self.chopStartY = 0; self.chopTargetX = 0; self.chopTargetY = 0; self.chopDevourRadius = 100; // Devoured effect variables self.devouredOpponents = []; self.devouredEndTime = 0; self.devouredDuration = 3000; // 3 seconds // Dash ability variables self.dashCooldown = 0; self.dashCooldownTime = 10000; // 10 seconds self.dashSpeed = 10; self.dashDuration = 800; // 0.8 seconds self.dashActive = false; self.dashEndTime = 0; // False shot variables self.falseShotCooldown = 0; self.falseShotCooldownTime = 12000; // 12 seconds self.falseShotActive = false; // FLOW SYSTEM for Sae if (typeof self.flowActive === "undefined") { self.flowActive = false; self.flowTriggerChecked = false; self.flowDribbleCount = 0; self.flowDribbleMax = 999; // Unlimited while in Flow self.flowText = null; self.flowHilalDribbling = false; self.flowHilalStartTime = 0; self.flowHilalDuration = 1200; // ms per crescent self.flowHilalOriginX = 0; self.flowHilalOriginY = 0; self.flowHilalRadius = 180; self.flowHilalStartAngle = 0; self.flowHilalEndAngle = 0; self.flowHilalPhase = 0; self.flowHilalCount = 0; self.flowHilalMax = 4; // Number of crescents before shot self.flowHilalShotDone = false; } // FLOW TRIGGER: If 2 goals scored by Sae, activate Flow (only once) if (!self.flowActive && !self.flowTriggerChecked && typeof opponentScore !== "undefined" && opponentScore >= 2) { self.flowActive = true; self.flowDribbleCount = 0; self.flowTriggerChecked = true; self.flowHilalDribbling = false; self.flowHilalCount = 0; self.flowHilalShotDone = false; // Show Flow text above Sae if (!self.flowText) { self.flowText = self.addChild(new Text2('FLOW', { size: 48, fill: 0xFF6600 })); self.flowText.anchor.set(0.5, 0.5); self.flowText.x = 0; self.flowText.y = -100; self.flowText.alpha = 1; tween(self.flowText, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 300, onFinish: function onFinish() { tween(self.flowText, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 300 }); } }); } } // Remove Flow text if not in Flow if (!self.flowActive && self.flowText) { self.flowText.destroy(); self.flowText = null; } self.update = function () { // Hitbox collision detection with other characters var hitboxRadius = 50; var allCharacters = [player, chigiri, hiori, reo, nagi, bachira]; for (var c = 0; c < allCharacters.length; c++) { if (allCharacters[c] && allCharacters[c] !== self) { var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y)); if (charDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - allCharacters[c].x) / charDist * 2; var pushY = (self.y - allCharacters[c].y) / charDist * 2; self.x += pushX; self.y += pushY; allCharacters[c].x -= pushX; allCharacters[c].y -= pushY; } } } // Check collision with opponents too for (var o = 0; o < opponents.length; o++) { if (opponents[o] && opponents[o] !== self) { var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y)); if (oppDist < hitboxRadius * 2) { // Push characters apart var pushX = (self.x - opponents[o].x) / oppDist * 2; var pushY = (self.y - opponents[o].y) / oppDist * 2; self.x += pushX; self.y += pushY; opponents[o].x -= pushX; opponents[o].y -= pushY; } } } // Update cooldowns if (self.perfectPassCooldown > 0) { self.perfectPassCooldown -= 16; if (self.perfectPassCooldown < 0) self.perfectPassCooldown = 0; } if (self.dashCooldown > 0) { self.dashCooldown -= 16; if (self.dashCooldown < 0) self.dashCooldown = 0; } if (self.falseShotCooldown > 0) { self.falseShotCooldown -= 16; if (self.falseShotCooldown < 0) self.falseShotCooldown = 0; } // Handle dribble stun end if (self.dribbleStunEnd > 0 && Date.now() > self.dribbleStunEnd) { // Restore stunned opponents for (var i = 0; i < self.dribbleStunnedOpponents.length; i++) { var opponent = self.dribbleStunnedOpponents[i]; opponent.stunned = false; opponent.originalSpeed = opponent.originalSpeed || opponent.speed; opponent.speed = opponent.originalSpeed; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } self.dribbleStunnedOpponents = []; self.dribbleStunEnd = 0; } // Handle dash duration if (self.dashActive && Date.now() > self.dashEndTime) { self.dashActive = false; self.speed = 4.5; // Return to normal speed } // Steal ball if player is inside opponent var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); if (playerDist < 80 && player.hasBall) { // Steal the ball player.hasBall = false; ball.active = true; // Ball moves away from player, toward Sae var stealDx = self.x - player.x; var stealDy = self.y - player.y; var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy); if (stealDist > 0) { ball.velocityX = stealDx / stealDist * 10; ball.velocityY = stealDy / stealDist * 10; } // Prevent immediate re-attachment ballDetachCooldown = ballDetachCooldownTime; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Handle devoured opponents restoration if (self.devouredEndTime > 0 && Date.now() > self.devouredEndTime) { // Restore devoured opponents for (var i = 0; i < self.devouredOpponents.length; i++) { var opponent = self.devouredOpponents[i]; opponent.speed = opponent.originalSpeed || opponent.speed * 2; opponent.devoured = false; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } self.devouredOpponents = []; self.devouredEndTime = 0; } // Half-circle dribbling logic if (self.isDribbling) { var elapsed = Date.now() - self.dribbleStartTime; var t = Math.min(elapsed / self.dribbleDuration, 1); // Interpolate angle for half-circle var angle = self.dribbleStartAngle + (self.dribbleEndAngle - self.dribbleStartAngle) * t; self.x = self.dribbleOriginX + Math.cos(angle) * self.dribbleRadius; self.y = self.dribbleOriginY + Math.sin(angle) * self.dribbleRadius; // Ball follows during dribble if (distance < 80) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } // When dribble completes, apply Devoured effect if (t >= 1) { self.isDribbling = false; self.applyDevouredEffect(self.dribbleStunRadius); } } // Zigzag dribbling logic if (self.isZigzagDribbling) { var elapsed = Date.now() - self.zigzagStartTime; var progress = Math.min(elapsed / self.zigzagDuration, 1); // Create zigzag pattern var zigzagAmplitude = 60; var zigzagFrequency = 6; var zigzagOffset = Math.sin(progress * Math.PI * zigzagFrequency) * zigzagAmplitude; // Move toward target with zigzag motion var targetDx = self.zigzagTargetX - self.zigzagStartX; var targetDy = self.zigzagTargetY - self.zigzagStartY; var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy); if (targetDist > 0) { // Calculate perpendicular vector for zigzag var perpX = -targetDy / targetDist; var perpY = targetDx / targetDist; // Apply zigzag movement self.x = self.zigzagStartX + targetDx * progress + perpX * zigzagOffset; self.y = self.zigzagStartY + targetDy * progress + perpY * zigzagOffset; // Ball follows during dribble if (distance < 100) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } } // End zigzag dribbling and apply Devoured effect if (progress >= 1) { self.isZigzagDribbling = false; self.applyDevouredEffect(self.zigzagDevourRadius); // Chain up to 6 zigzags, then pause self.zigzagCount = (self.zigzagCount || 0) + 1; if (self.zigzagCount < 6) { // Start another zigzag immediately self.isZigzagDribbling = true; self.zigzagStartTime = Date.now(); self.zigzagStartX = self.x; self.zigzagStartY = self.y; // Dribble toward player goal with reduced distance self.zigzagTargetX = self.x + (playerGoal.x - self.x) * 0.3 + (Math.random() - 0.5) * 80; self.zigzagTargetY = self.y + (playerGoal.y - self.y) * 0.3 + 50; } else { // Pause after 6 zigzags self.zigzagPaused = true; self.zigzagPauseStart = Date.now(); self.zigzagCount = 0; } } } // Zigzag pause logic if (self.zigzagPaused) { var pauseElapsed = Date.now() - self.zigzagPauseStart; if (pauseElapsed >= self.zigzagPauseDuration) { self.zigzagPaused = false; } } // Chop dribbling logic if (self.isChopDribbling) { var elapsed = Date.now() - self.chopStartTime; var progress = Math.min(elapsed / self.chopDuration, 1); // Quick chop movement - sharp direction change var chopPhase = progress < 0.5 ? progress * 2 : 1 - (progress - 0.5) * 2; var chopIntensity = Math.sin(chopPhase * Math.PI) * 80; // Move with chop motion var targetDx = self.chopTargetX - self.chopStartX; var targetDy = self.chopTargetY - self.chopStartY; var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy); if (targetDist > 0) { // Calculate perpendicular vector for chop var perpX = -targetDy / targetDist; var perpY = targetDx / targetDist; // Apply chop movement self.x = self.chopStartX + targetDx * progress + perpX * chopIntensity; self.y = self.chopStartY + targetDy * progress + perpY * chopIntensity; // Ball follows during dribble if (distance < 100) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } } // End chop dribbling and apply Devoured effect if (progress >= 1) { self.isChopDribbling = false; self.applyDevouredEffect(self.chopDevourRadius); } } // FLOW: If Sae is in Flow, perform continuous crescent (hilal) dribble and finish with a striker shot if (self.flowActive) { // Start Hilal dribble if not already started and not shot yet if (!self.flowHilalDribbling && !self.flowHilalShotDone && distance < 120) { self.flowHilalDribbling = true; self.flowHilalStartTime = Date.now(); self.flowHilalOriginX = self.x; self.flowHilalOriginY = self.y; // Each crescent alternates direction for visual effect var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x); var dir = self.flowHilalCount % 2 === 0 ? 1 : -1; self.flowHilalStartAngle = angleToGoal - dir * Math.PI / 2; self.flowHilalEndAngle = angleToGoal + dir * Math.PI / 2; self.flowHilalPhase = 0; } // Hilal dribble logic if (self.flowHilalDribbling && !self.flowHilalShotDone) { var elapsed = Date.now() - self.flowHilalStartTime; var t = Math.min(elapsed / self.flowHilalDuration, 1); var angle = self.flowHilalStartAngle + (self.flowHilalEndAngle - self.flowHilalStartAngle) * t; self.x = self.flowHilalOriginX + Math.cos(angle) * self.flowHilalRadius; self.y = self.flowHilalOriginY + Math.sin(angle) * self.flowHilalRadius; // Ball follows during dribble if (distance < 120) { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } // When crescent completes, start next or shoot if (t >= 1) { self.flowHilalDribbling = false; self.flowHilalCount++; // Visual effect for each crescent tween(self, { scaleX: 1.15, scaleY: 1.15, tint: 0xFF6600 }, { duration: 120, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 120 }); } }); // After enough crescents, shoot at goal if (self.flowHilalCount >= self.flowHilalMax) { // Striker shot: powerful, straight, with curve var goalDx = playerGoal.x - self.x; var goalDy = playerGoal.y - self.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { var curveAmount = 18 + Math.random() * 8; ball.velocityX = goalDx / goalDistance * 20 + curveAmount; ball.velocityY = goalDy / goalDistance * 20; ball.active = true; // Visual effect for shot tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xFF6600 }, { duration: 200, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); } self.flowHilalShotDone = true; // Reset after a short delay for next Flow LK.setTimeout(function () { self.flowActive = false; self.flowHilalCount = 0; self.flowHilalShotDone = false; }, 1200); } else { // Start next crescent immediately self.flowHilalDribbling = true; self.flowHilalStartTime = Date.now(); self.flowHilalOriginX = self.x; self.flowHilalOriginY = self.y; var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x); var dir = self.flowHilalCount % 2 === 0 ? 1 : -1; self.flowHilalStartAngle = angleToGoal - dir * Math.PI / 2; self.flowHilalEndAngle = angleToGoal + dir * Math.PI / 2; self.flowHilalPhase = 0; } } // Prevent other dribbling/shot logic during Flow return; } } // (Original code follows) // Sae behavior: pursue ball but stay in attacking areas if (distance < 600) { // Use dash if available and close to ball if (!self.dashActive && self.dashCooldown <= 0 && distance < 150) { self.dashActive = true; self.dashCooldown = self.dashCooldownTime; self.dashEndTime = Date.now() + self.dashDuration; self.speed = self.dashSpeed * 0.8; // Visual effect for dash tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0x00FFFF }, { duration: 150, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 150 }); } }); } // Move toward ball but avoid going too deep into player's half var pursuitSpeed = distance < 200 ? self.speed * 1.1 : self.speed * 0.9; // Limit Sae's movement to stay in upper half of field (y < 1500) var targetX = self.x + dx / distance * pursuitSpeed; var targetY = self.y + dy / distance * pursuitSpeed; if (targetY > 1500) { // Don't go too deep, stay in attacking position targetY = Math.min(targetY, 1500); } self.x = targetX; self.y = targetY; // Actions when close to ball if (distance < 80) { // Perfect Pass - find forward player to pass to if (self.perfectPassCooldown <= 0) { var nearestForward = null; var nearestDistance = Infinity; for (var i = 0; i < opponents.length; i++) { if (opponents[i].role === 'forward') { var forwardDx = opponents[i].x - self.x; var forwardDy = opponents[i].y - self.y; var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy); if (forwardDistance < nearestDistance && forwardDistance < self.perfectPassRange) { nearestDistance = forwardDistance; nearestForward = opponents[i]; } } } if (nearestForward) { // Execute Perfect Pass var passDx = nearestForward.x - ball.x; var passDy = nearestForward.y - ball.y; var passDist = Math.sqrt(passDx * passDx + passDy * passDy); if (passDist > 0) { ball.velocityX = passDx / passDist * 10; ball.velocityY = passDy / passDist * 10; ball.active = true; } self.perfectPassCooldown = self.perfectPassCooldownTime; // Visual effect for Perfect Pass tween(ball, { scaleX: 1.4, scaleY: 1.4, tint: 0x00FF00 }, { duration: 200, onFinish: function onFinish() { tween(ball, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); } } // Choose dribbling type randomly if no pass available if (!self.isDribbling && !self.isZigzagDribbling && !self.isChopDribbling && self.perfectPassCooldown > 0) { var dribbleType = Math.random(); if (dribbleType < 0.33) { // Half-circle dribbling self.isDribbling = true; self.dribbleStartTime = Date.now(); self.dribbleRadius = 100; // Calculate angle from Sae to player goal var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x); self.dribbleStartAngle = angleToGoal - Math.PI / 2; self.dribbleEndAngle = angleToGoal + Math.PI / 2; self.dribbleOriginX = self.x; self.dribbleOriginY = self.y; } else if (dribbleType < 0.66) { // Zigzag dribbling self.isZigzagDribbling = true; self.zigzagStartTime = Date.now(); self.zigzagStartX = self.x; self.zigzagStartY = self.y; // Dribble toward player goal with reduced distance self.zigzagTargetX = self.x + (playerGoal.x - self.x) * 0.3 + (Math.random() - 0.5) * 80; self.zigzagTargetY = self.y + (playerGoal.y - self.y) * 0.3 + 50; // Visual effect for zigzag start tween(self, { tint: 0xFF4500 }, { duration: 200, onFinish: function onFinish() { tween(self, { tint: 0xFFFFFF }, { duration: 200 }); } }); } else { // Chop dribbling self.isChopDribbling = true; self.chopStartTime = Date.now(); self.chopStartX = self.x; self.chopStartY = self.y; // Chop toward player goal (even shorter range) self.chopTargetX = self.x + (playerGoal.x - self.x) * 0.2 + (Math.random() - 0.5) * 30; self.chopTargetY = self.y + (playerGoal.y - self.y) * 0.2 + 20; // Visual effect for chop start tween(self, { tint: 0x32CD32 }, { duration: 150, onFinish: function onFinish() { tween(self, { tint: 0xFFFFFF }, { duration: 150 }); } }); } } // False shot ability if (self.falseShotCooldown <= 0 && !self.falseShotActive) { self.falseShotActive = true; self.falseShotCooldown = self.falseShotCooldownTime; // Fake shot motion - ball barely moves ball.velocityX = (Math.random() - 0.5) * 3; ball.velocityY = (Math.random() - 0.5) * 3; ball.active = true; // Visual effect for false shot tween(self, { scaleX: 1.2, scaleY: 1.2, tint: 0xFF6600 }, { duration: 300, onFinish: function onFinish() { // After fake, real shot toward goal var goalDx = playerGoal.x - ball.x; var goalDy = playerGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 14; ball.velocityY = goalDy / goalDistance * 14; ball.active = true; } tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); self.falseShotActive = false; } }); } } } else { // Sae is always active: maintain attacking midfield position var attackingMidfieldX = 1024 + (Math.random() - 0.5) * 300; var attackingMidfieldY = 1000 + Math.random() * 200; // Stay in upper midfield area var shotDx = attackingMidfieldX - self.x; var shotDy = attackingMidfieldY - self.y; var shotDist = Math.sqrt(shotDx * shotDx + shotDy * shotDy); if (shotDist > 50) { self.x += shotDx / shotDist * self.speed * 0.5; self.y += shotDy / shotDist * self.speed * 0.5; } // If far from ball but in shooting range, attempt long-range curved shot if (distance > 200 && distance < 700 && !self.isDribbling && !self.isZigzagDribbling && !self.isChopDribbling && !self.falseShotActive) { // Falsolu şut: apply curve by adding to X velocity var goalDx = playerGoal.x - ball.x; var goalDy = playerGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { var curveAmount = 12 + Math.random() * 8; // Stronger curve for long shot ball.velocityX = goalDx / goalDistance * 16 + curveAmount; ball.velocityY = goalDy / goalDistance * 16; ball.active = true; // Visual effect for long-range shot tween(self, { scaleX: 1.2, scaleY: 1.2, tint: 0xFF1493 }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } } } // Apply Devoured effect to nearby opponents self.applyDevouredEffect = function (radius) { var opponentsInRange = []; // Check all player team members if (typeof player !== 'undefined') { var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y)); if (playerDist <= radius) { opponentsInRange.push(player); } } if (typeof chigiri !== 'undefined') { var chigiriDist = Math.sqrt((chigiri.x - self.x) * (chigiri.x - self.x) + (chigiri.y - self.y) * (chigiri.y - self.y)); if (chigiriDist <= radius) { opponentsInRange.push(chigiri); } } if (typeof hiori !== 'undefined') { var hioriDist = Math.sqrt((hiori.x - self.x) * (hiori.x - self.x) + (hiori.y - self.y) * (hiori.y - self.y)); if (hioriDist <= radius) { opponentsInRange.push(hiori); } } if (typeof reo !== 'undefined') { var reoDist = Math.sqrt((reo.x - self.x) * (reo.x - self.x) + (reo.y - self.y) * (reo.y - self.y)); if (reoDist <= radius) { opponentsInRange.push(reo); } } if (typeof nagi !== 'undefined') { var nagiDist = Math.sqrt((nagi.x - self.x) * (nagi.x - self.x) + (nagi.y - self.y) * (nagi.y - self.y)); if (nagiDist <= radius) { opponentsInRange.push(nagi); } } // Apply Devoured effect to found opponents for (var i = 0; i < opponentsInRange.length; i++) { var opponent = opponentsInRange[i]; // 30% chance to apply full effect, otherwise just stun var isFullDevoured = Math.random() < 0.3; if (isFullDevoured) { // Full Devoured effect: severely reduce speed opponent.originalSpeed = opponent.speed; opponent.speed = opponent.speed * 0.2; // 20% speed opponent.devoured = true; self.devouredOpponents.push(opponent); // Visual effect for devoured opponent tween(opponent, { tint: 0x800080, // Purple tint for devoured scaleX: 0.8, scaleY: 0.8 }, { duration: 300 }); } else { // Just stun effect opponent.originalSpeed = opponent.speed; opponent.speed = 0; opponent.stunned = true; self.dribbleStunnedOpponents.push(opponent); // Visual effect for stunned opponent tween(opponent, { tint: 0xFFFF00, scaleX: 1.1, scaleY: 1.1 }, { duration: 200 }); } } if (opponentsInRange.length > 0) { self.devouredEndTime = Date.now() + self.devouredDuration; if (self.dribbleStunnedOpponents.length > 0) { self.dribbleStunEnd = Date.now() + self.dribbleStunDuration; } } }; }; return self; }); var SoccerBall = Container.expand(function () { var self = Container.call(this); var ballGraphics = self.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5 }); self.velocityX = 0; self.velocityY = 0; self.friction = 0.98; self.active = true; self.untouchable = false; // Ball can be made untouchable by goalkeepers self.update = function () { if (self.active) { self.x += self.velocityX; self.y += self.velocityY; self.velocityX *= self.friction; self.velocityY *= self.friction; // Check if ball has stopped moving and make it touchable again if (self.untouchable) { var ballSpeed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY); if (ballSpeed < 0.5) { // Ball has essentially stopped self.untouchable = false; } } // Keep ball in bounds with bounce effect if (self.x < 30) { self.x = 30; self.velocityX = Math.abs(self.velocityX) * 0.8; } if (self.x > 2018) { self.x = 2018; self.velocityX = -Math.abs(self.velocityX) * 0.8; } if (self.y < 30) { self.y = 30; self.velocityY = Math.abs(self.velocityY) * 0.8; } if (self.y > 2702) { self.y = 2702; self.velocityY = -Math.abs(self.velocityY) * 0.8; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x228B22 }); /**** * Game Code ****/ game.setBackgroundColor(0x228B22); // Game variables var opponents = []; var playerScore = 0; var opponentScore = 0; var dragNode = null; var maxStamina = 100; var currentStamina = 100; var staminaRegenRate = 0.2; var staminaDrainRate = 0.5; var lastPlayerX = 0; var lastPlayerY = 0; var shotChargeStart = 0; var isChargingShot = false; var shotPower = 0; var maxShotPower = 20; var minChargeTime = 2000; // 2 seconds minimum charge time var lastTouchX = 0; var lastTouchY = 0; var ballDetachCooldown = 0; var ballDetachCooldownTime = 1000; // 1 second cooldown // UI Elements var scoreTxt = new Text2('Player: 0 - Opponent: 0', { size: 80, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Stamina UI var staminaTxt = new Text2('Stamina: 100', { size: 60, fill: 0x00FF00 }); staminaTxt.anchor.set(0, 0); staminaTxt.x = 50; staminaTxt.y = 50; LK.gui.topLeft.addChild(staminaTxt); // Shot Power UI var shotPowerTxt = new Text2('Shot Power: 0%', { size: 60, fill: 0xFFFFFF }); shotPowerTxt.anchor.set(0, 0); shotPowerTxt.x = 50; shotPowerTxt.y = 130; LK.gui.topLeft.addChild(shotPowerTxt); // Game objects var player = game.addChild(new Player()); player.x = 1024; player.y = 2200; var ball = game.addChild(new SoccerBall()); ball.x = 1024; ball.y = 1366; // Player's goal (bottom) var playerGoal = game.addChild(new Goal()); playerGoal.x = 1024; playerGoal.y = 2600; // Opponent's goal (top) var opponentGoal = game.addChild(new Goal()); opponentGoal.x = 1024; opponentGoal.y = 300; // Create opponents with specific roles // Defense player (opponent) var defensePlayer = game.addChild(new DefensePlayer()); defensePlayer.x = 1024; defensePlayer.y = 600; defensePlayer.homeX = 1024; defensePlayer.homeY = 600; opponents.push(defensePlayer); // Chigiri - player team hybrid defender var chigiri = game.addChild(new ChigiriPlayer(0x3399ff)); chigiri.x = 800; chigiri.y = 2000; chigiri.homeX = 800; chigiri.homeY = 2000; // Hiori - player team creative midfielder var hiori = game.addChild(new HioriPlayer(0x6699ff)); hiori.x = 1200; hiori.y = 2000; hiori.homeX = 1200; hiori.homeY = 2000; // Reo - player team adaptive defender/attacker var reo = game.addChild(new ReoPlayer(0x000000)); reo.x = 600; reo.y = 1800; reo.homeX = 600; reo.homeY = 1800; // Nagi - player team striker with Fake Volley ability var nagi = game.addChild(new NagiPlayer(0x9966ff)); nagi.x = 1400; nagi.y = 2000; nagi.homeX = 1400; nagi.homeY = 2000; // Nagi starts without Flow // Meguru Bachira - player team creative winger with Roulette and Monster abilities var bachira = game.addChild(new MeguruBachiraPlayer(0x20B2AA)); bachira.x = 1000; bachira.y = 1900; bachira.homeX = 1000; bachira.homeY = 1900; // Midfield player var midfieldPlayer = game.addChild(new MidfieldPlayer()); midfieldPlayer.x = 1024; midfieldPlayer.y = 1000; midfieldPlayer.homeX = 1024; midfieldPlayer.homeY = 1000; opponents.push(midfieldPlayer); // (Removed ForwardPlayer - Nagi will be the forward/striker) // Sae - opponent team attacking midfielder with Perfect Pass and special abilities var sae = game.addChild(new SaePlayer(0xff6600)); sae.x = 1024; sae.y = 800; sae.homeX = 1024; sae.homeY = 800; opponents.push(sae); // Create goalkeepers var playerGoalkeeper = game.addChild(new Goalkeeper(true)); // Blue goalkeeper for player team playerGoalkeeper.x = 1024; playerGoalkeeper.y = 2500; // In front of player's goal playerGoalkeeper.originalX = 1024; playerGoalkeeper.originalY = 2500; var opponentGoalkeeper = game.addChild(new Goalkeeper(false)); // Red goalkeeper for opponent team opponentGoalkeeper.x = 1024; opponentGoalkeeper.y = 400; // In front of opponent's goal opponentGoalkeeper.originalX = 1024; opponentGoalkeeper.originalY = 400; // Ball kicking mechanics function kickBall(targetX, targetY, power) { var dx = targetX - ball.x; var dy = targetY - ball.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { var finalPower = power || Math.min(distance / 100, 15); // Properly detach ball from player first player.hasBall = false; ball.active = true; // Set cooldown to prevent immediate re-attachment ballDetachCooldown = ballDetachCooldownTime; // Apply velocity after detachment ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } } // Touch controls for player movement and ball kicking game.down = function (x, y, obj) { // Always update last touch position for shot direction lastTouchX = x; lastTouchY = y; var playerDistance = Math.sqrt((x - player.x) * (x - player.x) + (y - player.y) * (y - player.y)); var ballDistance = Math.sqrt((x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y)); if (playerDistance < ballDistance) { // Move player dragNode = player; } else { // Start charging shot if player has ball if (player.hasBall) { shotChargeStart = Date.now(); isChargingShot = true; shotPower = 0; } } }; game.move = function (x, y, obj) { // Always update last touch position for shot direction lastTouchX = x; lastTouchY = y; if (dragNode && currentStamina > 0) { var dx = x - dragNode.x; var dy = y - dragNode.y; var distance = Math.sqrt(dx * dx + dy * dy); // Slow down movement and consume stamina - even slower when having ball var moveSpeed = player.hasBall ? 0.15 : 0.2; dragNode.x += dx * moveSpeed; dragNode.y += dy * moveSpeed; // Consume stamina based on movement if (distance > 5) { currentStamina -= staminaDrainRate; if (currentStamina < 0) currentStamina = 0; } } }; game.up = function (x, y, obj) { dragNode = null; // Handle shot release if (isChargingShot && player.hasBall) { var chargeTime = Date.now() - shotChargeStart; if (chargeTime >= minChargeTime) { // Release charged shot - use last touch position for direction kickBall(lastTouchX, lastTouchY, shotPower); } else { // Not charged enough, no shot } isChargingShot = false; shotPower = 0; } }; // Goal scoring function function scoreGoal(isPlayer) { if (isPlayer) { playerScore++; } else { opponentScore++; } scoreTxt.setText('Player: ' + playerScore + ' - Opponent: ' + opponentScore); LK.getSound('goal').play(); // Reset ball position ball.x = 1024; ball.y = 1366; ball.velocityX = 0; ball.velocityY = 0; } // Formation and passing coordinator for strategic team play var ballCarrier = null; // Global ball carrier tracking var formationCoordinator = { currentFormation: 'attacking', ballCarrier: null, passingOptions: [], wingPlayers: [], supportPlayers: [], anticipationMap: {}, formation: { attacking: { chigiri: { x: 800, y: 1200, role: 'wing' }, hiori: { x: 1024, y: 1600, role: 'playmaker' }, reo: { x: 1400, y: 1400, role: 'support' }, nagi: { x: 1024, y: 900, role: 'striker' }, bachira: { x: 800, y: 1000, role: 'wing' } }, defensive: { chigiri: { x: 600, y: 1800, role: 'defense' }, hiori: { x: 1024, y: 1700, role: 'playmaker' }, reo: { x: 1400, y: 1800, role: 'defense' }, nagi: { x: 1024, y: 1200, role: 'striker' }, bachira: { x: 1000, y: 1500, role: 'support' } } }, updateFormation: function updateFormation() { // Switch formation based on ball position and game state var ballY = ball.y; var shouldAttack = ballY < 1500 || ballCarrier && ballCarrier.role === 'striker'; this.currentFormation = shouldAttack ? 'attacking' : 'defensive'; // Update each player's tactical position var teammates = [chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < teammates.length; i++) { if (teammates[i]) { var formationData = this.formation[this.currentFormation][teammates[i].role]; if (formationData) { // Dynamic Y: If ball is in midfield, keep some players in midfield, others on wings/defense if (ballY > 1000 && ballY < 2000) { // Ball in midfield: wings stay wide, playmaker stays central, striker stays high, support/defense drop back if (formationData.role === 'wing') { teammates[i].tacticalX = teammates[i].x < 1024 ? 400 : 1648; teammates[i].tacticalY = 1400; } else if (formationData.role === 'playmaker') { teammates[i].tacticalX = 1024; teammates[i].tacticalY = 1600; } else if (formationData.role === 'striker') { teammates[i].tacticalX = 1024; teammates[i].tacticalY = 900; } else if (formationData.role === 'support') { teammates[i].tacticalX = 1024; teammates[i].tacticalY = 1800; } else if (formationData.role === 'defense') { teammates[i].tacticalX = 1024; teammates[i].tacticalY = 2000; } teammates[i].tacticalRole = formationData.role; } else { // Use default formation teammates[i].tacticalX = formationData.x; teammates[i].tacticalY = formationData.y; teammates[i].tacticalRole = formationData.role; } } } } // Anticipation: Predict next ball position and update anticipationMap var anticipationSteps = 20; var predX = ball.x, predY = ball.y, predVX = ball.velocityX, predVY = ball.velocityY; for (var step = 0; step < anticipationSteps; step++) { predX += predVX; predY += predVY; predVX *= 0.98; predVY *= 0.98; // Clamp to field if (predX < 30) predX = 30; if (predX > 2018) predX = 2018; if (predY < 30) predY = 30; if (predY > 2702) predY = 2702; } this.anticipationMap = { x: predX, y: predY }; }, findPassingOptions: function findPassingOptions(ballHolder) { this.passingOptions = []; this.wingPlayers = []; this.supportPlayers = []; var teammates = [chigiri, hiori, reo, nagi, bachira, player]; for (var i = 0; i < teammates.length; i++) { if (teammates[i] && teammates[i] !== ballHolder) { var dist = Math.sqrt((teammates[i].x - ballHolder.x) * (teammates[i].x - ballHolder.x) + (teammates[i].y - ballHolder.y) * (teammates[i].y - ballHolder.y)); if (dist > 150 && dist < 700) { // allow longer passes for tactics var passQuality = this.calculatePassQuality(ballHolder, teammates[i]); // Anticipate if teammate will be open in next 20 frames var anticipationBonus = 0; if (this.anticipationMap) { var antDist = Math.sqrt((teammates[i].x - this.anticipationMap.x) * (teammates[i].x - this.anticipationMap.x) + (teammates[i].y - this.anticipationMap.y) * (teammates[i].y - this.anticipationMap.y)); if (antDist < 200) anticipationBonus += 20; } this.passingOptions.push({ player: teammates[i], distance: dist, quality: passQuality + anticipationBonus, angle: Math.atan2(teammates[i].y - ballHolder.y, teammates[i].x - ballHolder.x) }); // Categorize players for wing play if (teammates[i].tacticalRole === 'wing' || teammates[i].role === 'chigiri' || teammates[i].role === 'bachira') { this.wingPlayers.push(teammates[i]); } else { this.supportPlayers.push(teammates[i]); } } } } // Sort by pass quality this.passingOptions.sort(function (a, b) { return b.quality - a.quality; }); }, calculatePassQuality: function calculatePassQuality(from, to) { var baseQuality = 100; // Distance factor (closer = better, but not too close) var dist = Math.sqrt((to.x - from.x) * (to.x - from.x) + (to.y - from.y) * (to.y - from.y)); if (dist < 200) baseQuality -= 30; if (dist > 400) baseQuality -= 20; // Forward progress factor var forwardProgress = from.y - to.y; // Moving toward opponent goal if (forwardProgress > 0) baseQuality += forwardProgress * 0.1; // Wing position bonus if (to.x < 600 || to.x > 1400) baseQuality += 25; // Striker position bonus if (to.role === 'nagi' && to.y < 1000) baseQuality += 40; // Check for opponent interference for (var i = 0; i < opponents.length; i++) { var opp = opponents[i]; var oppDist = Math.sqrt((opp.x - to.x) * (opp.x - to.x) + (opp.y - to.y) * (opp.y - to.y)); if (oppDist < 120) baseQuality -= 35; } // Bonus for being in anticipated open space if (this.anticipationMap) { var antDist = Math.sqrt((to.x - this.anticipationMap.x) * (to.x - this.anticipationMap.x) + (to.y - this.anticipationMap.y) * (to.y - this.anticipationMap.y)); if (antDist < 150) baseQuality += 15; } return Math.max(baseQuality, 10); }, executeStrategicPass: function executeStrategicPass(ballHolder) { this.findPassingOptions(ballHolder); if (this.passingOptions.length === 0) return false; var chosenPass = null; // Team tactics: If a teammate is running into open space, prefer that pass for (var i = 0; i < this.passingOptions.length; i++) { var opt = this.passingOptions[i]; if (this.anticipationMap) { var antDist = Math.sqrt((opt.player.x - this.anticipationMap.x) * (opt.player.x - this.anticipationMap.x) + (opt.player.y - this.anticipationMap.y) * (opt.player.y - this.anticipationMap.y)); if (antDist < 120 && opt.quality > 60) { chosenPass = opt; break; } } } // Strategy selection based on field position and formation if (!chosenPass) { if (ballHolder.y > 1800) { // Defensive third - prioritize safe passes or wing play chosenPass = this.selectSafePass() || this.selectWingPass(); } else if (ballHolder.y > 1200) { // Middle third - mix of progressive and wing passes var strategy = Math.random(); if (strategy < 0.4) { chosenPass = this.selectProgressivePass(); } else if (strategy < 0.7) { chosenPass = this.selectWingPass(); } else { chosenPass = this.selectSafePass(); } } else { // Attacking third - prioritize progressive and through passes chosenPass = this.selectProgressivePass() || this.selectThroughPass(); } } if (chosenPass) { this.performPass(ballHolder, chosenPass); // Team tactics: If pass is to wing, have another support player run into box if (chosenPass.player.tacticalRole === 'wing') { var teammates = [chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < teammates.length; i++) { if (teammates[i] && teammates[i] !== chosenPass.player && teammates[i].tacticalRole !== 'wing') { // Move support player into box for cross teammates[i].tacticalX = 1024 + (Math.random() - 0.5) * 200; teammates[i].tacticalY = 600 + Math.random() * 200; } } } return true; } return false; }, selectSafePass: function selectSafePass() { var safeOptions = this.passingOptions.filter(function (option) { return option.quality > 70 && option.distance < 350; }); return safeOptions.length > 0 ? safeOptions[0] : null; }, selectWingPass: function selectWingPass() { var wingOptions = this.passingOptions.filter(function (option) { return (option.player.x < 600 || option.player.x > 1400) && option.quality > 50; }); return wingOptions.length > 0 ? wingOptions[0] : null; }, selectProgressivePass: function selectProgressivePass() { var progressiveOptions = this.passingOptions.filter(function (option) { return option.player.y < ball.y - 100 && option.quality > 60; }); return progressiveOptions.length > 0 ? progressiveOptions[0] : null; }, selectThroughPass: function selectThroughPass() { var throughOptions = this.passingOptions.filter(function (option) { return option.player.y < 1000 && option.quality > 40; }); return throughOptions.length > 0 ? throughOptions[0] : null; }, performPass: function performPass(from, passOption) { var target = passOption.player; var passPower = Math.min(passOption.distance / 40, 15); // Add some curve for style var curve = (Math.random() - 0.5) * 2; var dx = target.x - ball.x + curve; var dy = target.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { // Anticipate teammate's run: lead pass if moving var leadX = target.x, leadY = target.y; if (typeof target.lastX !== "undefined" && typeof target.lastY !== "undefined") { var vx = target.x - target.lastX; var vy = target.y - target.lastY; leadX += vx * 10; leadY += vy * 10; dx = leadX - ball.x + curve; dy = leadY - ball.y; dist = Math.sqrt(dx * dx + dy * dy); } ball.velocityX = dx / dist * passPower; ball.velocityY = dy / dist * passPower; ball.active = true; // Visual effect for strategic pass tween(ball, { scaleX: 1.2, scaleY: 1.2, tint: 0x00FF88 }, { duration: 300, onFinish: function onFinish() { tween(ball, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } } }; // Ball pursuit coordination system var ballPursuitCoordinator = { activePursuer: null, pursuitRange: 150, supportPositions: { wing: { x: 0, y: 0 }, defense: { x: 0, y: 0 }, support: { x: 0, y: 0 } }, updatePursuit: function updatePursuit() { var teammates = [chigiri, hiori, reo, nagi, bachira]; var ballToTeammateDistances = []; // Update formation first formationCoordinator.updateFormation(); // Calculate distances and find closest available teammate for (var i = 0; i < teammates.length; i++) { if (teammates[i]) { var dist = Math.sqrt((teammates[i].x - ball.x) * (teammates[i].x - ball.x) + (teammates[i].y - ball.y) * (teammates[i].y - ball.y)); ballToTeammateDistances.push({ player: teammates[i], distance: dist, isCurrentPursuer: teammates[i] === this.activePursuer }); } } // Sort by distance ballToTeammateDistances.sort(function (a, b) { return a.distance - b.distance; }); // Assign active pursuer (closest player within pursuit range) var newPursuer = null; for (var i = 0; i < ballToTeammateDistances.length; i++) { var candidate = ballToTeammateDistances[i]; if (candidate.distance < 800) { // Only pursue if reasonably close newPursuer = candidate.player; break; } } // Update active pursuer this.activePursuer = newPursuer; // Calculate support positions this.calculateSupportPositions(); // Assign roles to non-pursuing players for (var i = 0; i < teammates.length; i++) { if (teammates[i] && teammates[i] !== this.activePursuer) { teammates[i].coordinatedRole = this.assignSupportRole(teammates[i]); } else if (teammates[i] === this.activePursuer) { teammates[i].coordinatedRole = 'pursuer'; } } }, calculateSupportPositions: function calculateSupportPositions() { // Wing position - stay wide for passes this.supportPositions.wing.x = ball.x < 1024 ? ball.x + 400 : ball.x - 400; this.supportPositions.wing.y = ball.y + 100; // Defense position - stay back for protection this.supportPositions.defense.x = ball.x + (1024 - ball.x) * 0.3; this.supportPositions.defense.y = Math.max(ball.y + 300, 1800); // Support position - intermediate position for backup this.supportPositions.support.x = ball.x + (Math.random() - 0.5) * 200; this.supportPositions.support.y = ball.y + 200; }, assignSupportRole: function assignSupportRole(player) { // Assign roles based on player characteristics and position if (player.role === 'chigiri') { return Math.random() < 0.7 ? 'wing' : 'defense'; } else if (player.role === 'reo') { return player.isDefensiveMode ? 'defense' : 'support'; } else if (player.role === 'hiori') { return 'support'; } else if (player.role === 'nagi') { return 'striker'; } else if (player.role === 'bachira') { return Math.random() < 0.8 ? 'wing' : 'support'; } return 'support'; }, getSupportPosition: function getSupportPosition(role) { return this.supportPositions[role] || this.supportPositions.support; } }; // Special dribbling system for ball carriers every 5 seconds var specialDribbleSystem = { lastActivation: 0, activationInterval: 5000, // 5 seconds activePlayers: {}, initializePlayer: function initializePlayer(player) { if (!this.activePlayers[player.role]) { this.activePlayers[player.role] = { player: player, lastDribble: 0, isPerformingDribble: false, dribbleType: this.getDribbleType(player.role), dribbleStartTime: 0, dribbleDuration: 1500, effectApplied: false }; } }, getDribbleType: function getDribbleType(role) { switch (role) { case 'chigiri': return 'lightning_dash'; // Stun effect case 'hiori': return 'winter_wave'; // Slow effect case 'reo': return 'chameleon_shift'; // Stun effect case 'nagi': return 'trap_control'; // Slow effect case 'bachira': return 'monster_devour'; // Devoured effect case 'sae': return 'crescent_slice'; // Devoured effect case 'defense': return 'tackle_sweep'; // Stun effect case 'midfield': return 'curve_spiral'; // Slow effect case 'forward': return 'power_rush'; // Stun effect default: return 'basic_dribble'; // Stun effect } }, checkActivation: function checkActivation() { var currentTime = Date.now(); if (currentTime - this.lastActivation >= this.activationInterval) { this.lastActivation = currentTime; return true; } return false; }, performSpecialDribble: function performSpecialDribble(player, dribbleData) { if (dribbleData.isPerformingDribble) return; dribbleData.isPerformingDribble = true; dribbleData.dribbleStartTime = Date.now(); dribbleData.effectApplied = false; dribbleData.lastDribble = Date.now(); // Visual effect based on dribble type var effectColor = this.getDribbleColor(dribbleData.dribbleType); tween(player, { scaleX: 1.3, scaleY: 1.3, tint: effectColor }, { duration: 200, onFinish: function onFinish() { tween(player, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); // Apply movement pattern this.applyDribbleMovement(player, dribbleData); }, getDribbleColor: function getDribbleColor(dribbleType) { switch (dribbleType) { case 'lightning_dash': return 0xFFFF00; // Yellow case 'winter_wave': return 0x87CEEB; // Light blue case 'chameleon_shift': return 0xFF00FF; // Magenta case 'trap_control': return 0x9966FF; // Purple case 'monster_devour': return 0xFF0000; // Red case 'crescent_slice': return 0xFF6600; // Orange case 'tackle_sweep': return 0x808080; // Gray case 'curve_spiral': return 0x00FF00; // Green case 'power_rush': return 0x0000FF; // Blue default: return 0xFFFFFF; // White } }, applyDribbleMovement: function applyDribbleMovement(player, dribbleData) { var originalX = player.x; var originalY = player.y; switch (dribbleData.dribbleType) { case 'lightning_dash': // Quick forward dash var dashDistance = 120; var targetX = player.x + (Math.random() - 0.5) * 40; var targetY = player.y - dashDistance; tween(player, { x: targetX, y: targetY }, { duration: 300, easing: tween.easeOut }); break; case 'winter_wave': // Circular motion creating waves var radius = 80; var startAngle = Math.random() * Math.PI * 2; for (var i = 0; i < 3; i++) { LK.setTimeout(function (angle) { return function () { if (dribbleData.isPerformingDribble) { var waveX = originalX + Math.cos(angle) * radius; var waveY = originalY + Math.sin(angle) * radius; tween(player, { x: waveX, y: waveY }, { duration: 200 }); } }; }(startAngle + i * Math.PI / 3), i * 200); } break; case 'chameleon_shift': // Teleport-like movement var shiftDistance = 100; var shiftAngle = Math.random() * Math.PI * 2; var shiftX = player.x + Math.cos(shiftAngle) * shiftDistance; var shiftY = player.y + Math.sin(shiftAngle) * shiftDistance; player.alpha = 0.3; tween(player, { x: shiftX, y: shiftY, alpha: 1 }, { duration: 400, easing: tween.easeInOut }); break; case 'trap_control': // Slow spinning motion var spinRadius = 60; for (var i = 0; i < 4; i++) { LK.setTimeout(function (step) { return function () { if (dribbleData.isPerformingDribble) { var angle = step * Math.PI / 2; var spinX = originalX + Math.cos(angle) * spinRadius; var spinY = originalY + Math.sin(angle) * spinRadius; tween(player, { x: spinX, y: spinY }, { duration: 300 }); } }; }(i), i * 300); } break; case 'monster_devour': // Erratic zigzag movement var zigzagSteps = 5; for (var i = 0; i < zigzagSteps; i++) { LK.setTimeout(function (step) { return function () { if (dribbleData.isPerformingDribble) { var zigX = originalX + (step % 2 === 0 ? -60 : 60); var zigY = originalY - step * 20; tween(player, { x: zigX, y: zigY }, { duration: 150 }); } }; }(i), i * 150); } break; case 'crescent_slice': // Crescent-shaped movement var crescentRadius = 90; for (var i = 0; i < 4; i++) { LK.setTimeout(function (step) { return function () { if (dribbleData.isPerformingDribble) { var angle = -Math.PI / 2 + step * Math.PI / 3; var crescentX = originalX + Math.cos(angle) * crescentRadius; var crescentY = originalY + Math.sin(angle) * crescentRadius; tween(player, { x: crescentX, y: crescentY }, { duration: 250 }); } }; }(i), i * 250); } break; default: // Basic dribble - simple side step var stepX = player.x + (Math.random() - 0.5) * 80; tween(player, { x: stepX }, { duration: 500 }); break; } // Schedule effect application LK.setTimeout(function () { if (dribbleData.isPerformingDribble && !dribbleData.effectApplied) { specialDribbleSystem.applyDribbleEffect(player, dribbleData); dribbleData.effectApplied = true; } }, 700); // End dribble LK.setTimeout(function () { dribbleData.isPerformingDribble = false; }, dribbleData.dribbleDuration); }, applyDribbleEffect: function applyDribbleEffect(player, dribbleData) { var effectRadius = 150; var affectedOpponents = []; // Find opponents in range if (player.role === 'sae' || player.role === 'defense' || player.role === 'midfield' || player.role === 'forward') { // Opponent affecting player team var playerTeam = [player, chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < playerTeam.length; i++) { if (playerTeam[i] && playerTeam[i] !== player) { var dist = Math.sqrt((playerTeam[i].x - player.x) * (playerTeam[i].x - player.x) + (playerTeam[i].y - player.y) * (playerTeam[i].y - player.y)); if (dist <= effectRadius) { affectedOpponents.push(playerTeam[i]); } } } } else { // Player team affecting opponents for (var i = 0; i < opponents.length; i++) { if (opponents[i]) { var dist = Math.sqrt((opponents[i].x - player.x) * (opponents[i].x - player.x) + (opponents[i].y - player.y) * (opponents[i].y - player.y)); if (dist <= effectRadius) { affectedOpponents.push(opponents[i]); } } } } // Apply effects based on dribble type for (var i = 0; i < affectedOpponents.length; i++) { var opponent = affectedOpponents[i]; this.applyEffectToOpponent(opponent, dribbleData.dribbleType); } }, applyEffectToOpponent: function applyEffectToOpponent(opponent, dribbleType) { // Stun effects if (dribbleType === 'lightning_dash' || dribbleType === 'chameleon_shift' || dribbleType === 'tackle_sweep' || dribbleType === 'power_rush') { opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; tween(opponent, { tint: 0xFFFF00, scaleX: 1.1, scaleY: 1.1 }, { duration: 300 }); LK.setTimeout(function () { opponent.stunned = false; opponent.speed = opponent.originalSpeed || 3; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); }, 2000); } // Slow effects else if (dribbleType === 'winter_wave' || dribbleType === 'trap_control' || dribbleType === 'curve_spiral') { opponent.originalSpeed = opponent.speed; opponent.speed = opponent.speed * 0.3; opponent.slowed = true; tween(opponent, { tint: 0x87CEEB, scaleX: 0.9, scaleY: 0.9 }, { duration: 300 }); LK.setTimeout(function () { opponent.slowed = false; opponent.speed = opponent.originalSpeed || 3; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); }, 3000); } // Devoured effects else if (dribbleType === 'monster_devour' || dribbleType === 'crescent_slice') { opponent.originalSpeed = opponent.speed; opponent.speed = opponent.speed * 0.2; opponent.devoured = true; tween(opponent, { tint: 0x800080, scaleX: 0.8, scaleY: 0.8 }, { duration: 300 }); LK.setTimeout(function () { opponent.devoured = false; opponent.speed = opponent.originalSpeed || 3; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); }, 4000); } }, update: function update() { if (!this.checkActivation()) return; // Check all ball carriers and activate special dribbles var allPlayers = [player, chigiri, hiori, reo, nagi, bachira].concat(opponents); for (var i = 0; i < allPlayers.length; i++) { var currentPlayer = allPlayers[i]; if (currentPlayer && currentPlayer.hasBall) { this.initializePlayer(currentPlayer); var dribbleData = this.activePlayers[currentPlayer.role]; if (dribbleData && !dribbleData.isPerformingDribble) { this.performSpecialDribble(currentPlayer, dribbleData); } } } } }; // Game update loop game.update = function () { // Update special dribbling system specialDribbleSystem.update(); // Update ball carrier tracking ballCarrier = null; if (player.hasBall) { ballCarrier = player; } else { var teammates = [chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < teammates.length; i++) { if (teammates[i] && teammates[i].hasBall) { ballCarrier = teammates[i]; break; } } if (!ballCarrier) { for (var i = 0; i < opponents.length; i++) { if (opponents[i] && opponents[i].hasBall) { ballCarrier = opponents[i]; break; } } } } // Update ball pursuit coordination ballPursuitCoordinator.updatePursuit(); // Update ball detach cooldown if (ballDetachCooldown > 0) { ballDetachCooldown -= 16; // Approximately 60 FPS if (ballDetachCooldown < 0) ballDetachCooldown = 0; } // Check if player is close to ball for interaction (only if cooldown expired and ball is not untouchable) var playerToBallDistance = Math.sqrt((player.x - ball.x) * (player.x - ball.x) + (player.y - ball.y) * (player.y - ball.y)); if (playerToBallDistance < 80 && !player.hasBall && ballDetachCooldown <= 0 && !ball.untouchable) { player.hasBall = true; ball.active = false; ball.velocityX = 0; ball.velocityY = 0; } // If player has ball, make it stick to player if (player.hasBall && ball.active === false) { ball.x = player.x; ball.y = player.y - 50; } // Stamina regeneration when not moving var playerMoving = Math.abs(player.x - lastPlayerX) > 1 || Math.abs(player.y - lastPlayerY) > 1; if (!playerMoving && currentStamina < maxStamina) { currentStamina += staminaRegenRate; if (currentStamina > maxStamina) currentStamina = maxStamina; } // Update last position lastPlayerX = player.x; lastPlayerY = player.y; // Update stamina UI var staminaPercent = Math.round(currentStamina / maxStamina * 100); staminaTxt.setText('Stamina: ' + staminaPercent); // Change color based on stamina level if (staminaPercent > 50) { staminaTxt.fill = 0x00FF00; } else if (staminaPercent > 25) { staminaTxt.fill = 0xFFFF00; } else { staminaTxt.fill = 0xFF0000; } // Update shot charging if (isChargingShot && player.hasBall) { var chargeTime = Date.now() - shotChargeStart; if (chargeTime >= minChargeTime) { // Calculate shot power based on charge time var chargeProgress = Math.min((chargeTime - minChargeTime) / 1000, 1); // 1 second after minimum for max power shotPower = 5 + chargeProgress * maxShotPower; // Minimum 5, maximum 25 power var powerPercent = Math.round(chargeProgress * 100); shotPowerTxt.setText('Shot Power: ' + powerPercent + '%'); shotPowerTxt.fill = 0x00FF00; // Automatically kick ball forward when shot power reaches 100% if (powerPercent >= 100) { shotPower = 5 + maxShotPower; // Keep at maximum power shotPowerTxt.setText('Shot Power: 100%'); shotPowerTxt.fill = 0x00FF00; // Auto-kick ball forward when power reaches 100% var forwardX = player.x; var forwardY = player.y - 300; // Kick forward (up the field) kickBall(forwardX, forwardY, shotPower); isChargingShot = false; shotPower = 0; } } else { // Not charged enough yet var timeLeft = Math.ceil((minChargeTime - chargeTime) / 1000); shotPowerTxt.setText('Hold for ' + timeLeft + 's'); shotPowerTxt.fill = 0xFFFF00; } } else { shotPowerTxt.setText('Shot Power: 0%'); shotPowerTxt.fill = 0xFFFFFF; } // Check player goal scoring (opponent scores) if (ball.y > playerGoal.y - 50 && ball.x > playerGoal.x - 200 && ball.x < playerGoal.x + 200) { scoreGoal(false); LK.effects.flashObject(playerGoal, 0xFF0000, 500); } // Check opponent goal scoring (player scores) if (ball.y < opponentGoal.y + 50 && ball.x > opponentGoal.x - 200 && ball.x < opponentGoal.x + 200) { scoreGoal(true); LK.effects.flashObject(opponentGoal, 0x00FF00, 500); } // Check win condition if (playerScore >= 5) { LK.showYouWin(); } if (opponentScore >= 5) { LK.showGameOver(); } // Enhanced AI positioning based on ball location for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; // Defensive/attacking stabilization: ensure all AI are always moving and using abilities if (typeof opponent.stunned !== "undefined" && opponent.stunned) { // If stunned, skip movement for this frame continue; } // If opponent is out of bounds, bring them back in if (opponent.x < 30) opponent.x = 30; if (opponent.x > 2018) opponent.x = 2018; if (opponent.y < 30) opponent.y = 30; if (opponent.y > 2702) opponent.y = 2702; // If opponent is not moving, nudge toward home position if (typeof opponent.lastX === "undefined") opponent.lastX = opponent.x; if (typeof opponent.lastY === "undefined") opponent.lastY = opponent.y; var moved = Math.abs(opponent.x - opponent.lastX) > 1 || Math.abs(opponent.y - opponent.lastY) > 1; if (!moved) { // Nudge toward home position if stuck var homeDx = (opponent.homeX || 1024) - opponent.x; var homeDy = (opponent.homeY || 1366) - opponent.y; var homeDist = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDist > 1) { opponent.x += homeDx / homeDist * (opponent.speed || 2.5) * 0.5; opponent.y += homeDy / homeDist * (opponent.speed || 2.5) * 0.5; } } opponent.lastX = opponent.x; opponent.lastY = opponent.y; // If opponent has a special ability/cooldown, ensure it is ticking down if (typeof opponent.update === "function") { // Call their update to ensure abilities and movement are handled opponent.update(); } } // Stabilize player team as well (chigiri, hiori, reo, nagi, bachira) var teammates = [chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < teammates.length; i++) { var mate = teammates[i]; if (!mate) continue; if (typeof mate.stunned !== "undefined" && mate.stunned) continue; if (mate.x < 30) mate.x = 30; if (mate.x > 2018) mate.x = 2018; if (mate.y < 30) mate.y = 30; if (mate.y > 2702) mate.y = 2702; if (typeof mate.lastX === "undefined") mate.lastX = mate.x; if (typeof mate.lastY === "undefined") mate.lastY = mate.y; var moved = Math.abs(mate.x - mate.lastX) > 1 || Math.abs(mate.y - mate.lastY) > 1; if (!moved) { var homeDx = (mate.homeX || 1024) - mate.x; var homeDy = (mate.homeY || 1366) - mate.y; var homeDist = Math.sqrt(homeDx * homeDx + homeDy * homeDy); if (homeDist > 1) { mate.x += homeDx / homeDist * (mate.speed || 3) * 0.5; mate.y += homeDy / homeDist * (mate.speed || 3) * 0.5; } } mate.lastX = mate.x; mate.lastY = mate.y; if (typeof mate.update === "function") { mate.update(); } } // Ensure all characters are always actively playing, using abilities, moving, defending, attacking, and helping recover the ball // All role-based AI and abilities are handled in each player's update method };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ChigiriPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && opponentGraphics) {
opponentGraphics.tint = colorOverride;
}
self.speed = 5;
self.role = 'chigiri';
self.homeX = 800;
self.homeY = 2000;
self.maxDistance = 600;
self.hasBall = false;
self.isSpeedBursting = false;
self.speedBurstTarget = null;
self.speedBurstCooldown = 0;
self.speedBurstCooldownTime = 8000; // 8 seconds
self.normalSpeed = 5;
self.burstSpeed = 12;
self.stamina = 100;
self.maxStamina = 100;
self.staminaDrainRate = 0.8;
self.staminaRegenRate = 0.3;
self.lowStaminaSpeed = 1.5; // Very slow when stamina is low
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, hiori, reo, nagi, bachira, chigiri];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Update speed burst cooldown
if (self.speedBurstCooldown > 0) {
self.speedBurstCooldown -= 16;
if (self.speedBurstCooldown < 0) self.speedBurstCooldown = 0;
}
// Steal ball if opponent is inside area
var nearestOpponent = null;
var nearestDistance = Infinity;
for (var i = 0; i < opponents.length; i++) {
var opponentDist = Math.sqrt((opponents[i].x - self.x) * (opponents[i].x - self.x) + (opponents[i].y - self.y) * (opponents[i].y - self.y));
if (opponentDist < nearestDistance) {
nearestDistance = opponentDist;
nearestOpponent = opponents[i];
}
}
if (nearestOpponent && nearestDistance < 80 && nearestOpponent.hasBall) {
// Steal the ball
nearestOpponent.hasBall = false;
ball.active = true;
// Ball moves toward Chigiri
var stealDx = self.x - nearestOpponent.x;
var stealDy = self.y - nearestOpponent.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 8;
ball.velocityY = stealDy / stealDist * 8;
}
ballDetachCooldown = ballDetachCooldownTime;
// Stun the opponent for 2 seconds after stealing ball
nearestOpponent.stunned = true;
nearestOpponent.originalSpeed = nearestOpponent.speed;
nearestOpponent.speed = 0;
tween(nearestOpponent, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(nearestOpponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.setTimeout(function () {
nearestOpponent.stunned = false;
nearestOpponent.speed = nearestOpponent.originalSpeed || 2.5;
}, 2000);
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Coordinated ball pursuit - only chase if designated as active pursuer
if (self.coordinatedRole === 'pursuer' && distance < 800 && !self.isSpeedBursting) {
// Chase ball with hybrid intelligence - increased range and speed
var chaseSpeed = distance < 150 ? self.normalSpeed * 1.8 : self.normalSpeed * 1.5;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (self.coordinatedRole !== 'pursuer' && !self.isSpeedBursting) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.normalSpeed * 0.8;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
}
// Speed burst mechanic
if (!self.isSpeedBursting && self.speedBurstCooldown <= 0 && distance < 100) {
// Throw ball forward and start speed burst
var throwX = opponentGoal.x + (Math.random() - 0.5) * 200;
var throwY = opponentGoal.y + 200;
var throwDx = throwX - ball.x;
var throwDy = throwY - ball.y;
var throwDist = Math.sqrt(throwDx * throwDx + throwDy * throwDy);
if (throwDist > 0) {
ball.velocityX = throwDx / throwDist * 14;
ball.velocityY = throwDy / throwDist * 14;
}
// Set speed burst target and activate
self.speedBurstTarget = {
x: throwX,
y: throwY
};
self.isSpeedBursting = true;
self.speedBurstCooldown = self.speedBurstCooldownTime; // Start cooldown
// Visual effect for speed burst
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
if (self.isSpeedBursting && self.speedBurstTarget) {
// Move at burst speed toward target
var burstDx = self.speedBurstTarget.x - self.x;
var burstDy = self.speedBurstTarget.y - self.y;
var burstDist = Math.sqrt(burstDx * burstDx + burstDy * burstDy);
if (burstDist > 30) {
self.x += burstDx / burstDist * self.burstSpeed;
self.y += burstDy / burstDist * self.burstSpeed;
// Drain stamina during speed burst
self.stamina -= self.staminaDrainRate * 2; // Double drain during burst
if (self.stamina < 0) self.stamina = 0;
} else {
// Reached target, stop speed burst
self.isSpeedBursting = false;
self.speedBurstTarget = null;
}
} else {
// Stamina regeneration when not moving much
var currentSpeed = Math.sqrt((self.x - (self.lastMoveX || self.x)) * (self.x - (self.lastMoveX || self.x)) + (self.y - (self.lastMoveY || self.y)) * (self.y - (self.lastMoveY || self.y)));
if (currentSpeed < 2 && self.stamina < self.maxStamina) {
self.stamina += self.staminaRegenRate;
if (self.stamina > self.maxStamina) self.stamina = self.maxStamina;
}
self.lastMoveX = self.x;
self.lastMoveY = self.y;
// Determine current speed based on stamina
var currentMoveSpeed = self.stamina > 20 ? self.normalSpeed : self.lowStaminaSpeed;
// Normal hybrid behavior - enhanced ball pursuit
if (distance < 500) {
// Move toward ball with improved midfielder intelligence but adjust for stamina
var pursuitSpeed = distance < 200 ? currentMoveSpeed * 1.3 : currentMoveSpeed * 1.1;
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
// Drain stamina during pursuit
self.stamina -= self.staminaDrainRate * 0.5;
if (self.stamina < 0) self.stamina = 0;
// Coordinated strategic play when close to ball
if (distance < 80) {
// Try strategic passing first
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic pass executed
} else {
// Fallback to individual decision making
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [hiori, reo, nagi, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 30% chance to pass if teammate available, otherwise shoot
if (nearestTeammate && Math.random() < 0.3) {
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 10;
ball.velocityY = passDy / passDistance * 10;
}
} else {
// Shoot at goal
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 11;
ball.velocityY = goalDy / goalDistance * 11;
}
}
}
}
// Move to tactical position when not actively involved with ball
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 0.6;
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else {
// Return to defensive position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
// Use stamina-adjusted speed for returning home
var currentMoveSpeed = self.stamina > 20 ? self.normalSpeed : self.lowStaminaSpeed;
self.x += homeDx / homeDistance * currentMoveSpeed * 0.4;
self.y += homeDy / homeDistance * currentMoveSpeed * 0.4;
// Light stamina drain when returning to position
self.stamina -= self.staminaDrainRate * 0.3;
if (self.stamina < 0) self.stamina = 0;
}
}
}
};
// 1v1 dribble move logic for Chigiri
self.lastX = self.lastX || self.x;
self.lastY = self.lastY || self.y;
self.lastDribbleAttempt = self.lastDribbleAttempt || 0;
self.isDribbling1v1 = self.isDribbling1v1 || false;
self.dribble1v1Start = self.dribble1v1Start || 0;
self.dribble1v1Duration = 350; // ms
self.dribble1v1TargetX = self.dribble1v1TargetX || 0;
self.dribble1v1TargetY = self.dribble1v1TargetY || 0;
var origUpdate = self.update;
self.update = function () {
// 1v1 dribble move: if close to opponent and moving toward them, trigger a dribble move
var closestOpponent = null;
var closestDist = Infinity;
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var dist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (dist < closestDist) {
closestDist = dist;
closestOpponent = opp;
}
}
if (!self.isDribbling1v1 && Date.now() - (self.lastDribbleAttempt || 0) > 1200 && closestDist < 110 && closestOpponent && !closestOpponent.stunned) {
// Check if moving toward opponent
var vx = self.x - (self.lastX || self.x);
var vy = self.y - (self.lastY || self.y);
var toOppX = closestOpponent.x - self.x;
var toOppY = closestOpponent.y - self.y;
var dot = vx * toOppX + vy * toOppY;
if (dot > 0) {
// Initiate dribble move: dash past opponent
self.isDribbling1v1 = true;
self.dribble1v1Start = Date.now();
self.lastDribbleAttempt = Date.now();
var oppDist = Math.sqrt(toOppX * toOppX + toOppY * toOppY);
var overshoot = 80;
self.dribble1v1TargetX = self.x + toOppX / oppDist * (oppDist + overshoot);
self.dribble1v1TargetY = self.y + toOppY / oppDist * (oppDist + overshoot);
// Stun the opponent briefly
closestOpponent.stunned = true;
closestOpponent.originalSpeed = closestOpponent.speed;
closestOpponent.speed = 0;
tween(closestOpponent, {
tint: 0xAAAAAA,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
onFinish: function onFinish() {
tween(closestOpponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.setTimeout(function () {
closestOpponent.stunned = false;
closestOpponent.speed = closestOpponent.originalSpeed || 2.5;
}, 1000);
// Visual effect for Chigiri
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
}
}
// Animate dribble move
if (self.isDribbling1v1) {
var t = Math.min((Date.now() - self.dribble1v1Start) / self.dribble1v1Duration, 1);
// Use linear interpolation for smooth movement, but only for the duration of the dribble
self.x = self.lastX + (self.dribble1v1TargetX - self.lastX) * t;
self.y = self.lastY + (self.dribble1v1TargetY - self.lastY) * t;
if (t >= 1) {
self.isDribbling1v1 = false;
}
} else {
// Only update lastX/lastY when not dribbling, to avoid jitter
self.lastX = self.x;
self.lastY = self.y;
}
origUpdate();
};
return self;
});
var DefensePlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && opponentGraphics) {
opponentGraphics.tint = colorOverride;
}
self.speed = 2.5;
self.role = 'defense';
self.homeX = 1024;
self.homeY = 600;
self.maxDistance = 300;
self.hasBall = false;
self.update = function () {
// Steal ball if player is inside opponent
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 80 && player.hasBall) {
// Steal the ball
player.hasBall = false;
ball.active = true;
// Ball moves away from player, toward defense
var stealDx = self.x - player.x;
var stealDy = self.y - player.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
// Prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Smart defensive positioning based on ball Y coordinate
if (ball.y > 1500) {
// Ball is in midfield/defense area - stay in position and intercept
var interceptX = 1024; // Stay central
var interceptY = Math.max(600, ball.y - 200); // Position between ball and goal
var interceptDx = interceptX - self.x;
var interceptDy = interceptY - self.y;
var interceptDist = Math.sqrt(interceptDx * interceptDx + interceptDy * interceptDy);
if (interceptDist > 50) {
self.x += interceptDx / interceptDist * self.speed * 0.6;
self.y += interceptDy / interceptDist * self.speed * 0.6;
}
}
// Defense priority: desperately try to get the ball
if (distance < 250) {
// Move toward ball - reduced chase range and speed
self.x += dx / distance * (self.speed * 0.8);
self.y += dy / distance * (self.speed * 0.8);
// Sliding tackle if player is close to defense
var playerDistance = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDistance < 120 && ball.active && !player.hasBall) {
// Slide tackle: dash toward ball and clear it away
var slideDx = ball.x - self.x;
var slideDy = ball.y - self.y;
var slideDist = Math.sqrt(slideDx * slideDx + slideDy * slideDist);
if (slideDist > 0) {
// Animate slide (quick move)
tween(self, {
x: self.x + slideDx / slideDist * 60,
y: self.y + slideDy / slideDist * 60
}, {
duration: 120,
easing: tween.cubicOut
});
// Clear ball far away from player
var clearX = ball.x < 1024 ? ball.x - 400 : ball.x + 400;
var clearY = ball.y - 500;
var clearDx = clearX - ball.x;
var clearDy = clearY - ball.y;
var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy);
if (clearDist > 0) {
ball.velocityX = clearDx / clearDist * 13;
ball.velocityY = clearDy / clearDist * 13;
}
}
}
// Aggressive ball stealing when player team has ball and is near goal
var ballCarrierNearGoal = false;
var allPlayerTeam = [player, chigiri, hiori, reo, nagi, bachira];
for (var p = 0; p < allPlayerTeam.length; p++) {
if (allPlayerTeam[p] && allPlayerTeam[p].hasBall && allPlayerTeam[p].y < 1000) {
ballCarrierNearGoal = true;
var carrierDist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y));
if (carrierDist < 150) {
// Steal ball from carrier
allPlayerTeam[p].hasBall = false;
ball.active = true;
// Ball moves toward defense
var stealDx = self.x - allPlayerTeam[p].x;
var stealDy = self.y - allPlayerTeam[p].y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 12;
ball.velocityY = stealDy / stealDist * 12;
}
ballDetachCooldown = ballDetachCooldownTime;
break;
}
}
}
// 1v1 defensive dribble when ball carrier approaches
if (!self.isDribbling1v1Defense) self.isDribbling1v1Defense = false;
if (!self.lastDefenseDribble) self.lastDefenseDribble = 0;
var closestPlayerTeammate = null;
var closestPlayerDist = Infinity;
for (var p = 0; p < allPlayerTeam.length; p++) {
if (allPlayerTeam[p]) {
var dist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y));
if (dist < closestPlayerDist) {
closestPlayerDist = dist;
closestPlayerTeammate = allPlayerTeam[p];
}
}
}
if (!self.isDribbling1v1Defense && Date.now() - self.lastDefenseDribble > 2000 && closestPlayerDist < 120 && closestPlayerTeammate) {
// Check if player is moving toward defense
var toPlayerX = closestPlayerTeammate.x - self.x;
var toPlayerY = closestPlayerTeammate.y - self.y;
if (toPlayerY < 0) {
// Player moving toward goal
// Defensive 1v1 dribble: block and steal
self.isDribbling1v1Defense = true;
self.lastDefenseDribble = Date.now();
// Move to block path
var blockX = self.x + toPlayerX * 0.5;
var blockY = self.y + toPlayerY * 0.5;
tween(self, {
x: blockX,
y: blockY,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
// Try to steal ball or slow down player
if (closestPlayerTeammate.hasBall) {
closestPlayerTeammate.hasBall = false;
ball.active = true;
ball.velocityX = (Math.random() - 0.5) * 8;
ball.velocityY = -5;
ballDetachCooldown = ballDetachCooldownTime;
}
// Stun player briefly
closestPlayerTeammate.stunned = true;
closestPlayerTeammate.originalSpeed = closestPlayerTeammate.speed;
closestPlayerTeammate.speed = closestPlayerTeammate.speed * 0.3;
LK.setTimeout(function () {
closestPlayerTeammate.stunned = false;
closestPlayerTeammate.speed = closestPlayerTeammate.originalSpeed || 3;
}, 1500);
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
self.isDribbling1v1Defense = false;
}
});
}
}
// Kick ball away from player goal if close enough
if (distance < 80) {
// Find nearest forward player
var nearestForward = null;
var nearestDistance = Infinity;
for (var i = 0; i < opponents.length; i++) {
if (opponents[i].role === 'forward') {
var forwardDx = opponents[i].x - self.x;
var forwardDy = opponents[i].y - self.y;
var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy);
if (forwardDistance < nearestDistance) {
nearestDistance = forwardDistance;
nearestForward = opponents[i];
}
}
}
// Pass to forward if found, otherwise clear as before
if (nearestForward) {
var passDx = nearestForward.x - ball.x;
var passDy = nearestForward.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 10;
ball.velocityY = passDy / passDistance * 10;
}
} else {
var kickAwayX = ball.x < 1024 ? ball.x - 200 : ball.x + 200;
var kickAwayY = ball.y - 300; // Kick upfield
var kickDx = kickAwayX - ball.x;
var kickDy = kickAwayY - ball.y;
var kickDistance = Math.sqrt(kickDx * kickDx + kickDy * kickDy);
if (kickDistance > 0) {
ball.velocityX = kickDx / kickDistance * 8;
ball.velocityY = kickDy / kickDistance * 8;
}
}
}
} else {
// Return to defensive position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.5;
self.y += homeDy / homeDistance * self.speed * 0.5;
}
}
};
return self;
});
var ForwardPlayer = Container.expand(function () {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2.8;
self.role = 'forward';
self.homeX = 1024;
self.homeY = 1400;
self.hasBall = false;
self.directShotCooldown = self.directShotCooldown || 0;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Steal ball if player is inside opponent
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 80 && player.hasBall) {
// Steal the ball
player.hasBall = false;
ball.active = true;
// Ball moves away from player, toward forward
var stealDx = self.x - player.x;
var stealDy = self.y - player.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
// Prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
}
// Defensive behavior when ball is in opponent's half and player team has ball
var allPlayerTeam = [player, chigiri, hiori, reo, nagi, bachira];
var playerTeamHasBall = false;
var ballCarrierDistance = Infinity;
var ballCarrier = null;
for (var p = 0; p < allPlayerTeam.length; p++) {
if (allPlayerTeam[p] && allPlayerTeam[p].hasBall) {
playerTeamHasBall = true;
var carrierDist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y));
if (carrierDist < ballCarrierDistance) {
ballCarrierDistance = carrierDist;
ballCarrier = allPlayerTeam[p];
}
break;
}
}
// If player team has ball and forward is close, try to steal
if (playerTeamHasBall && ballCarrier && ballCarrierDistance < 100) {
// Forward 1v1 defensive dribble
if (!self.lastDefensiveMove) self.lastDefensiveMove = 0;
if (Date.now() - self.lastDefensiveMove > 1500) {
self.lastDefensiveMove = Date.now();
// Quick tackle move
var tackleX = ballCarrier.x;
var tackleY = ballCarrier.y;
tween(self, {
x: tackleX,
y: tackleY,
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFFFF00
}, {
duration: 300,
onFinish: function onFinish() {
// Steal ball if still close
var finalDist = Math.sqrt((ballCarrier.x - self.x) * (ballCarrier.x - self.x) + (ballCarrier.y - self.y) * (ballCarrier.y - self.y));
if (finalDist < 80 && ballCarrier.hasBall) {
ballCarrier.hasBall = false;
ball.active = true;
ball.velocityX = (Math.random() - 0.5) * 6;
ball.velocityY = Math.random() * -8;
ballDetachCooldown = ballDetachCooldownTime;
}
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
}
// Handle direct shot cooldown
if (self.directShotCooldown > 0) {
self.directShotCooldown -= 16;
if (self.directShotCooldown < 0) self.directShotCooldown = 0;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Forward: attack and try to score - more aggressive
if (distance < 500) {
// Move toward ball with pursuit
var pursuitSpeed = distance < 200 ? self.speed * 1.5 : self.speed * 1.2;
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
// If close to ball, shoot at goal
if (distance < 80) {
// Direct Shot every 30s
if (self.directShotCooldown <= 0) {
var goalDx = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
// Direct Shot: fast and straight
ball.velocityX = goalDx / goalDistance * 12;
ball.velocityY = goalDy / goalDistance * 12;
}
self.directShotCooldown = 30000; // 30 seconds
} else {
// Normal shot
var goalDx = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 10;
ball.velocityY = goalDy / goalDistance * 10;
}
}
}
} else {
// Move toward attacking position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
// Goal area
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5
});
goalGraphics.alpha = 0.3;
// Left goalpost
var leftPost = self.addChild(LK.getAsset('goalpost', {
anchorX: 0.5,
anchorY: 0.5
}));
leftPost.x = -210;
leftPost.y = 0;
// Right goalpost
var rightPost = self.addChild(LK.getAsset('goalpost', {
anchorX: 0.5,
anchorY: 0.5
}));
rightPost.x = 210;
rightPost.y = 0;
return self;
});
var Goalkeeper = Container.expand(function (isPlayer) {
var self = Container.call(this);
var keeperGraphics = self.attachAsset(isPlayer ? 'player' : 'opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.isPlayer = isPlayer;
self.rushCooldown = 0;
self.rushCooldownTime = 20000; // 20 seconds
self.isRushing = false;
self.originalX = 0;
self.originalY = 0;
self.rushSpeed = 8;
self.update = function () {
// Update rush cooldown
if (self.rushCooldown > 0) {
self.rushCooldown -= 16; // Approximately 60 FPS
if (self.rushCooldown < 0) self.rushCooldown = 0;
}
// Check if should rush for ball
var ballDistance = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y));
if (!self.isRushing && self.rushCooldown <= 0 && ballDistance < 400) {
// Start rushing
self.isRushing = true;
self.rushCooldown = self.rushCooldownTime;
}
if (self.isRushing) {
// Rush toward ball
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0 && distance > 50) {
self.x += dx / distance * self.rushSpeed;
self.y += dy / distance * self.rushSpeed;
} else if (distance <= 50) {
// Catch ball if close enough and force away from all players
ball.velocityX = 0;
ball.velocityY = 0;
ball.x = self.x;
ball.y = self.y;
ball.active = false;
ball.untouchable = true; // Make ball untouchable during goalkeeper control
// Force ball away from any player who has it
player.hasBall = false;
var allPlayers = [chigiri, hiori, reo, nagi, bachira];
for (var p = 0; p < allPlayers.length; p++) {
if (allPlayers[p] && allPlayers[p].hasBall) {
allPlayers[p].hasBall = false;
}
}
// Also force away from opponents
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o].hasBall) {
opponents[o].hasBall = false;
}
}
// Return to original position first
self.isRushing = false;
tween(self, {
x: self.originalX,
y: self.originalY
}, {
duration: 1000,
onFinish: function onFinish() {
// After returning to position, throw ball to center field
ball.active = true;
var centerX = 1024;
var centerY = 1366;
var throwPower = 15;
var throwDx = centerX - ball.x;
var throwDy = centerY - ball.y;
var throwDistance = Math.sqrt(throwDx * throwDx + throwDy * throwDy);
if (throwDistance > 0) {
ball.velocityX = throwDx / throwDistance * throwPower;
ball.velocityY = throwDy / throwDistance * throwPower;
}
}
});
}
} else {
// Normal goalkeeper movement - stay near goal line
var goalCenterX = 1024;
var dx = goalCenterX - self.x;
if (Math.abs(dx) > 5) {
self.x += dx > 0 ? self.speed : -self.speed;
}
}
};
return self;
});
var HioriPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 4;
self.role = 'hiori';
self.homeX = 1200;
self.homeY = 2000;
self.hasBall = false;
// FLOW SYSTEM
self.flowActive = false;
self.flowDribbleCount = 0;
self.flowDribbleMax = 4; // 4 extra dribbles in Flow
self.flowText = null;
self.flowTriggerChecked = false; // To avoid retriggering
// Zig-zag dribbling variables
self.isDribbling = false;
self.dribbleStartTime = 0;
self.dribbleDuration = 2000; // 2 seconds - slower duration
self.dribbleSpeed = 6;
self.dribbleDirection = 1; // 1 for right, -1 for left
self.dribblePhase = 0; // 0-1 progress through dribble
self.dribbleStartX = 0;
self.dribbleStartY = 0;
self.dribbleTargetX = 0;
self.dribbleTargetY = 0;
// Zigzag pause variables
self.zigzagCount = 0;
self.zigzagPaused = false;
self.zigzagPauseStart = 0;
self.zigzagPauseDuration = 10000; // 10 seconds
// Winter Zone variables
self.winterZoneActive = false;
self.winterZoneRadius = 200;
self.winterZoneCooldown = 0;
self.winterZoneCooldownTime = 12000; // 12 seconds
self.winterZoneDuration = 0;
self.winterZoneMaxDuration = 10000; // 10 seconds - increased duration
self.slowedOpponents = [];
// Winter Zone visual
self.winterZoneVisual = null;
// Perfect pass variables
self.passChargingTime = 0;
self.perfectPassCooldown = 0;
self.perfectPassCooldownTime = 6000; // 6 seconds
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, reo, nagi, bachira, hiori];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// FLOW TRIGGER: If 2 goals conceded, activate Flow for Hiori (only once)
if (!self.flowActive && !self.flowTriggerChecked && typeof opponentScore !== "undefined" && opponentScore >= 2) {
self.flowActive = true;
self.flowDribbleCount = 0;
self.flowTriggerChecked = true;
// Show Flow text above Hiori
if (!self.flowText) {
self.flowText = self.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
}
// Remove Flow text if not in Flow
if (!self.flowActive && self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
// Steal ball from nearby opponents and stun them
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (oppDist < 80 && opponent.hasBall) {
// Steal the ball
opponent.hasBall = false;
ball.active = true;
// Ball moves toward Hiori
var stealDx = self.x - opponent.x;
var stealDy = self.y - opponent.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 8;
ball.velocityY = stealDy / stealDist * 8;
}
ballDetachCooldown = ballDetachCooldownTime;
// Stun the opponent for 2 seconds after stealing ball
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
tween(opponent, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.setTimeout(function () {
opponent.stunned = false;
opponent.speed = opponent.originalSpeed || 2.5;
}, 2000);
break;
}
}
// Update cooldowns
if (self.winterZoneCooldown > 0) {
self.winterZoneCooldown -= 16;
if (self.winterZoneCooldown < 0) self.winterZoneCooldown = 0;
}
if (self.perfectPassCooldown > 0) {
self.perfectPassCooldown -= 16;
if (self.perfectPassCooldown < 0) self.perfectPassCooldown = 0;
}
// FLOW: If in Flow, boost Winter Zone and dribbling
var flowWinterZoneRadius = self.flowActive ? 420 : 200;
var flowWinterZoneSlow = self.flowActive ? 0.12 : 0.3; // 12% speed in Flow
var flowWinterZoneDuration = self.flowActive ? 18000 : self.winterZoneMaxDuration; // 18s in Flow
var flowWinterZoneCooldown = self.flowActive ? 6000 : self.winterZoneCooldownTime; // 6s in Flow
// Winter Zone duration and effect
if (self.winterZoneActive) {
self.winterZoneDuration -= 16;
if (self.winterZoneDuration <= 0) {
self.winterZoneActive = false;
// Remove Winter Zone visual
if (self.winterZoneVisual) {
self.winterZoneVisual.destroy();
self.winterZoneVisual = null;
}
// Restore opponent speeds
for (var i = 0; i < self.slowedOpponents.length; i++) {
var opponent = self.slowedOpponents[i];
opponent.speed = opponent.originalSpeed || opponent.speed * 2;
}
self.slowedOpponents = [];
} else {
// Apply Winter Zone effect to nearby opponents
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= (self.winterZoneActive ? flowWinterZoneRadius : self.winterZoneRadius)) {
if (self.slowedOpponents.indexOf(opponent) === -1) {
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * (self.winterZoneActive ? flowWinterZoneSlow : 0.3); // Much slower in Flow
self.slowedOpponents.push(opponent);
}
}
}
}
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// FLOW: Dribbling logic
if (self.isDribbling) {
var elapsed = Date.now() - self.dribbleStartTime;
var progress = Math.min(elapsed / self.dribbleDuration, 1);
// Create zig-zag pattern - slower frequency for better visibility
var zigzagAmplitude = 80;
var zigzagFrequency = 4;
var zigzagOffset = Math.sin(progress * Math.PI * zigzagFrequency) * zigzagAmplitude;
// Move toward target with zig-zag motion - slower speed
var targetDx = self.dribbleTargetX - self.dribbleStartX;
var targetDy = self.dribbleTargetY - self.dribbleStartY;
var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDist > 0) {
// Calculate perpendicular vector for zig-zag
var perpX = -targetDy / targetDist;
var perpY = targetDx / targetDist;
// Apply zig-zag movement with slower speed
var slowSpeed = 0.6; // Reduced from 1.0 to make it slower
self.x = self.dribbleStartX + targetDx * progress * slowSpeed + perpX * zigzagOffset;
self.y = self.dribbleStartY + targetDy * progress * slowSpeed + perpY * zigzagOffset;
// Ball follows during dribble
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
}
// End dribbling
if (progress >= 1) {
self.isDribbling = false;
self.zigzagCount = (self.zigzagCount || 0) + 1;
// FLOW: If in Flow, allow up to 4 extra dribbles in a row
if (self.flowActive && self.flowDribbleCount < self.flowDribbleMax) {
self.flowDribbleCount++;
// Start another dribble immediately
self.isDribbling = true;
self.dribbleStartTime = Date.now();
self.dribbleStartX = self.x;
self.dribbleStartY = self.y;
// Dribble toward opponent goal, randomize a bit
self.dribbleTargetX = opponentGoal.x + (Math.random() - 0.5) * 300;
self.dribbleTargetY = opponentGoal.y + 200 + Math.random() * 100;
} else {
// After 4 zigzags, pause for 10 seconds
if (self.zigzagCount >= 4) {
self.zigzagPaused = true;
self.zigzagPauseStart = Date.now();
self.zigzagCount = 0;
}
// Reset Flow dribble count after sequence
if (self.flowActive) self.flowDribbleCount = 0;
}
}
} else {
// Coordinated ball pursuit - only chase if designated as active pursuer
if (self.coordinatedRole === 'pursuer' && distance < 700) {
var chaseSpeed = distance < 150 ? self.speed * 1.7 : self.speed * 1.4;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 700) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 0.7;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Handle zigzag pause
if (self.zigzagPaused) {
var pauseElapsed = Date.now() - self.zigzagPauseStart;
if (pauseElapsed >= self.zigzagPauseDuration) {
self.zigzagPaused = false;
}
}
// Start zig-zag dribbling when close to ball (only if not paused)
if (distance < 80 && !self.isDribbling && !self.zigzagPaused) {
self.isDribbling = true;
self.dribbleStartTime = Date.now();
self.dribbleStartX = self.x;
self.dribbleStartY = self.y;
// Dribble toward opponent goal
self.dribbleTargetX = opponentGoal.x + (Math.random() - 0.5) * 300;
self.dribbleTargetY = opponentGoal.y + 200;
// FLOW: If in Flow, reset dribble count for new sequence
if (self.flowActive) self.flowDribbleCount = 0;
}
}
}
// FLOW: Winter Zone is much bigger, lasts longer, slows more, and triggers more often
if (!self.winterZoneActive && self.winterZoneCooldown <= 0) {
var nearbyOpponents = 0;
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= flowWinterZoneRadius + 50) {
nearbyOpponents++;
}
}
if (nearbyOpponents >= 2) {
// Activate Winter Zone
self.winterZoneActive = true;
self.winterZoneDuration = flowWinterZoneDuration;
self.winterZoneCooldown = flowWinterZoneCooldown;
// Create Winter Zone visual area
if (self.winterZoneVisual) {
self.winterZoneVisual.destroy();
self.winterZoneVisual = null;
}
self.winterZoneVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self.flowActive ? 8 : 4,
scaleY: self.flowActive ? 8 : 4,
tint: 0x87CEEB
}));
self.winterZoneVisual.alpha = 0.3;
// Visual effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0x87CEEB
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
}
// Strategic playmaking when close to ball
if (distance < 80) {
// Hiori as playmaker prioritizes strategic passing
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic pass executed
} else {
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, reo, nagi, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// FLOW: If in Flow, perfect pass always goes directly to player, no curve, and is instant
if (self.perfectPassCooldown <= 0) {
// Look for player to pass to
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 600 && playerDist > 100) {
// Execute perfect pass
var passDx = player.x - ball.x;
var passDy = player.y - ball.y;
var passDist = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDist > 0) {
if (self.flowActive) {
// FLOW: Pass is instant and direct
ball.x = player.x;
ball.y = player.y - 50;
ball.velocityX = 0;
ball.velocityY = 0;
ball.active = false;
player.hasBall = true;
} else {
// Normal perfect pass with slight curve to avoid interception
var curveAmount = 3;
ball.velocityX = passDx / passDist * 12 + curveAmount;
ball.velocityY = passDy / passDist * 12;
ball.active = true;
}
self.perfectPassCooldown = self.perfectPassCooldownTime;
// Visual effect for perfect pass
tween(ball, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
}
});
}
}
} else if (nearestTeammate && Math.random() < 0.4) {
// 40% chance to pass to teammate (higher as playmaker)
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 9;
ball.velocityY = passDy / passDistance * 9;
}
} else {
// Shoot at goal
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 12;
ball.velocityY = goalDy / goalDistance * 12;
}
}
}
}
// Move to tactical position when supporting team
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 0.5;
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
// Return to home position when not actively playing
if (distance > 500) {
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
return self;
});
var MeguruBachiraPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 4.5;
self.role = 'bachira';
self.homeX = 1000;
self.homeY = 1900;
self.hasBall = false;
// FLOW SYSTEM for Bachira
self.flowActive = false;
self.flowTriggerChecked = false;
self.flowText = null;
// Monster Trans variables removed
// Roulette dribbling variables
self.rouletteActive = false;
self.rouletteStartTime = 0;
self.rouletteDuration = 1500; // 1.5 seconds
self.rouletteRotations = 3; // 3 full rotations
self.rouletteRadius = 80;
self.rouletteCooldown = 0;
self.rouletteCooldownTime = 4000; // 4 seconds
self.rouletteOriginX = 0;
self.rouletteOriginY = 0;
self.rouletteStunRadius = 120;
self.rouletteStunnedOpponents = [];
self.rouletteStunEnd = 0;
// Monster Dribbling variables
self.monsterDribblingActive = false;
self.monsterDribblingStartTime = 0;
self.monsterDribblingDuration = 2000; // 2 seconds
self.monsterDribblingSpeed = 8;
self.monsterDribblingCooldown = 0;
self.monsterDribblingCooldownTime = 6000; // 6 seconds
self.monsterDribblingStartX = 0;
self.monsterDribblingStartY = 0;
self.monsterDribblingTargetX = 0;
self.monsterDribblingTargetY = 0;
self.monsterDribblingStunRadius = 150;
self.monsterDribblingStunnedOpponents = [];
self.monsterDribblingStunEnd = 0;
// Rush to ball variables (after Roulette)
self.rushingToBall = false;
self.rushTargetX = 0;
self.rushTargetY = 0;
self.rushSpeed = 7;
self.readyToShoot = false;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// FLOW TRIGGER: Monster Trans for Bachira, random chance for Hiori, only one Flow at a time
if (!self.flowActive && !self.flowTriggerChecked) {
// Count Bachira and Hiori on field
var bachiraCount = typeof bachira !== "undefined" ? 1 : 0;
var hioriCount = typeof hiori !== "undefined" ? 1 : 0;
var totalFlowCandidates = bachiraCount + hioriCount;
var shouldFlow = false;
// If both Bachira and Hiori are present, randomly pick one for Flow
if (totalFlowCandidates > 1) {
// 50% chance for Bachira, 50% for Hiori
var pick = Math.random();
if (pick < 0.5) {
shouldFlow = true; // Bachira gets Flow
if (typeof hiori !== "undefined") {
hiori.flowActive = false;
hiori.flowTriggerChecked = true;
if (hiori.flowText) {
hiori.flowText.destroy();
hiori.flowText = null;
}
}
} else {
shouldFlow = false;
if (typeof hiori !== "undefined" && !hiori.flowActive && !hiori.flowTriggerChecked) {
hiori.flowActive = true;
hiori.flowTriggerChecked = true;
hiori.flowDribbleCount = 0;
// Show Flow text above Hiori
if (!hiori.flowText) {
hiori.flowText = hiori.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
hiori.flowText.anchor.set(0.5, 0.5);
hiori.flowText.x = 0;
hiori.flowText.y = -100;
hiori.flowText.alpha = 1;
if (hiori.flowText) {
// Defensive: only tween if flowText exists
tween(hiori.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
if (hiori.flowText) {
// Defensive: only tween if flowText still exists
tween(hiori.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
}
});
}
}
}
}
} else {
// Only Bachira or only Hiori present
shouldFlow = true;
}
if (shouldFlow) {
// Only set flowTriggerChecked, but do NOT activate Monster Trans/Flow yet
self.flowTriggerChecked = true;
// self.flowActive and self.monsterTransActive will be set when using Roulette or Monster Dribbling
}
}
// Remove Flow text and effects if not in Flow
if (!self.flowActive && self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
// Monster Trans visual cleanup removed
// Steal ball from nearby opponents and stun them
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (oppDist < 80 && opponent.hasBall) {
// Steal the ball
opponent.hasBall = false;
ball.active = true;
// Ball moves toward Bachira
var stealDx = self.x - opponent.x;
var stealDy = self.y - opponent.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 8;
ball.velocityY = stealDy / stealDist * 8;
}
ballDetachCooldown = ballDetachCooldownTime;
// Stun the opponent for 2 seconds after stealing ball
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
tween(opponent, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.setTimeout(function () {
opponent.stunned = false;
opponent.speed = opponent.originalSpeed || 2.5;
}, 2000);
break;
}
}
// Update cooldowns
if (self.rouletteCooldown > 0) {
self.rouletteCooldown -= 16;
if (self.rouletteCooldown < 0) self.rouletteCooldown = 0;
}
if (self.monsterDribblingCooldown > 0) {
// In Flow mode, no cooldowns
if (!self.flowActive) {
self.monsterDribblingCooldown -= 16;
if (self.monsterDribblingCooldown < 0) self.monsterDribblingCooldown = 0;
} else {
self.monsterDribblingCooldown = 0; // No cooldown in Flow
}
}
// Handle Roulette stun end
if (self.rouletteStunEnd > 0 && Date.now() > self.rouletteStunEnd) {
// Restore stunned opponents from Roulette
for (var i = 0; i < self.rouletteStunnedOpponents.length; i++) {
var opponent = self.rouletteStunnedOpponents[i];
opponent.stunned = false;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.rouletteStunnedOpponents = [];
self.rouletteStunEnd = 0;
}
// Handle Monster Dribbling stun end
if (self.monsterDribblingStunEnd > 0 && Date.now() > self.monsterDribblingStunEnd) {
// Restore stunned opponents from Monster Dribbling
for (var i = 0; i < self.monsterDribblingStunnedOpponents.length; i++) {
var opponent = self.monsterDribblingStunnedOpponents[i];
opponent.stunned = false;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.monsterDribblingStunnedOpponents = [];
self.monsterDribblingStunEnd = 0;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Roulette dribbling logic
if (self.rouletteActive) {
var elapsed = Date.now() - self.rouletteStartTime;
var t = Math.min(elapsed / self.rouletteDuration, 1);
// Create spinning motion around origin point
var angle = t * self.rouletteRotations * 2 * Math.PI;
self.x = self.rouletteOriginX + Math.cos(angle) * self.rouletteRadius;
self.y = self.rouletteOriginY + Math.sin(angle) * self.rouletteRadius;
// Ball follows during roulette
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// End roulette and apply stun effect
if (t >= 1) {
self.rouletteActive = false;
self.applyRouletteStun();
}
}
// Monster Dribbling logic
if (self.monsterDribblingActive) {
var elapsed = Date.now() - self.monsterDribblingStartTime;
var t = Math.min(elapsed / self.monsterDribblingDuration, 1);
// Fast, erratic movement toward target
var chaosAmplitude = 40;
var chaosFrequency = 8;
var chaosOffsetX = Math.sin(t * Math.PI * chaosFrequency) * chaosAmplitude;
var chaosOffsetY = Math.cos(t * Math.PI * chaosFrequency * 1.3) * chaosAmplitude;
// Move toward target with chaos
var targetDx = self.monsterDribblingTargetX - self.monsterDribblingStartX;
var targetDy = self.monsterDribblingTargetY - self.monsterDribblingStartY;
self.x = self.monsterDribblingStartX + targetDx * t + chaosOffsetX;
self.y = self.monsterDribblingStartY + targetDy * t + chaosOffsetY;
// Ball follows during monster dribbling
if (distance < 120) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// Stun all opponents passed by during Monster Dribbling (only in Flow/Monster Trans)
if (self.flowActive) {
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (oppDist <= self.monsterDribblingStunRadius && !opp.stunned) {
opp.stunned = true;
opp.originalSpeed = opp.speed;
opp.speed = 0;
self.monsterDribblingStunnedOpponents.push(opp);
tween(opp, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250
});
}
}
if (self.monsterDribblingStunnedOpponents.length > 0) {
self.monsterDribblingStunEnd = Date.now() + 4000;
}
}
// End monster dribbling and apply stun effect
if (t >= 1) {
self.monsterDribblingActive = false;
self.applyMonsterDribblingStun();
}
}
// Rush to ball after Roulette throw
if (self.rushingToBall) {
var rushDx = self.rushTargetX - self.x;
var rushDy = self.rushTargetY - self.y;
var rushDist = Math.sqrt(rushDx * rushDx + rushDy * rushDy);
if (rushDist > 30) {
// Rush toward the thrown ball location
self.x += rushDx / rushDist * self.rushSpeed;
self.y += rushDy / rushDist * self.rushSpeed;
// Stun all opponents passed by during Monster Trans rush (only in Flow)
if (self.flowActive) {
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (oppDist <= self.monsterDribblingStunRadius && !opp.stunned) {
opp.stunned = true;
opp.originalSpeed = opp.speed;
opp.speed = 0;
self.monsterDribblingStunnedOpponents.push(opp);
tween(opp, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250
});
}
}
if (self.monsterDribblingStunnedOpponents.length > 0) {
self.monsterDribblingStunEnd = Date.now() + 4000;
}
}
} else {
// Reached the target area, check if ball is nearby
var ballDist = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y));
if (ballDist < 100) {
// Ball is close, prepare to shoot
self.readyToShoot = true;
self.rushingToBall = false;
// Control the ball briefly
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
// Immediately shoot toward opponent goal
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDist = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDist > 0) {
ball.velocityX = goalDx / goalDist * 18; // Powerful shot
ball.velocityY = goalDy / goalDist * 18;
ball.active = true;
// Visual effect for powerful shot
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFF4500
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: colorOverride || 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Reset after shot
self.readyToShoot = false;
} else {
// Ball not found at target, stop rushing
self.rushingToBall = false;
}
}
} else if (!self.rouletteActive && !self.monsterDribblingActive && self.coordinatedRole === 'pursuer' && distance < 700) {
// Normal ball pursuit - more aggressive
var chaseSpeed = distance < 150 ? self.speed * 1.7 : self.speed * 1.4;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (!self.rouletteActive && !self.monsterDribblingActive && self.coordinatedRole !== 'pursuer' && distance < 700) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 0.8;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Creative wing play with tactical awareness
if (distance < 80) {
// Strategic passing consideration for wing play
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic pass executed - Bachira creates from wide
} else {
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, hiori, reo, nagi, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 20% chance to pass if teammate available, 30% for abilities, 50% for goal shot
var actionChoice = Math.random();
if (nearestTeammate && actionChoice < 0.2) {
// Pass to teammate
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 11;
ball.velocityY = passDy / passDistance * 11;
}
} else if (actionChoice < 0.5) {
// Use special abilities (existing code)
// Start abilities when close to ball
if (distance < 80) {
// Roulette (normal cooldown applies)
if (!self.rouletteActive && self.rouletteCooldown <= 0) {
var shouldUseRoulette = Math.random() < 0.4; // 40% chance
if (shouldUseRoulette) {
self.rouletteActive = true;
self.rouletteStartTime = Date.now();
self.rouletteOriginX = self.x;
self.rouletteOriginY = self.y;
// If Flow was triggered for Bachira, activate Flow now
if (self.flowTriggerChecked && !self.flowActive) {
self.flowActive = true;
// Show Flow text above Bachira
if (!self.flowText) {
self.flowText = self.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
}
if (!self.flowActive) {
self.rouletteCooldown = self.rouletteCooldownTime;
}
// Visual effect for roulette start
tween(self, {
tint: 0x00FFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
tint: colorOverride || 0xFFFFFF
}, {
duration: 100
});
}
});
}
}
// Monster Dribbling (no cooldown in Flow)
if (!self.monsterDribblingActive && (self.flowActive || self.monsterDribblingCooldown <= 0)) {
var shouldUseMonsterDribbling = Math.random() < 0.6; // 60% chance
if (shouldUseMonsterDribbling) {
self.monsterDribblingActive = true;
self.monsterDribblingStartTime = Date.now();
self.monsterDribblingStartX = self.x;
self.monsterDribblingStartY = self.y;
// Target toward opponent goal
self.monsterDribblingTargetX = opponentGoal.x + (Math.random() - 0.5) * 200;
self.monsterDribblingTargetY = opponentGoal.y + 300;
// If Flow was triggered for Bachira, activate Flow now
if (self.flowTriggerChecked && !self.flowActive) {
self.flowActive = true;
// Show Flow text above Bachira
if (!self.flowText) {
self.flowText = self.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
}
if (!self.flowActive) {
self.monsterDribblingCooldown = self.monsterDribblingCooldownTime;
}
// Visual effect for monster dribbling start
tween(self, {
tint: 0xFF0000,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: colorOverride || 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
}
}
}
} else {
// Shoot at goal if no abilities used
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 13;
ball.velocityY = goalDy / goalDistance * 13;
}
}
}
}
// Move to tactical wing position when not directly involved
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 0.7;
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else if (!self.rouletteActive && !self.monsterDribblingActive && distance > 500) {
// Return to home position when not actively playing
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
self.applyRouletteStun = function () {
var opponentsInRange = [];
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var opponentDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (opponentDist <= self.rouletteStunRadius) {
opponentsInRange.push(opponent);
}
}
// Stun opponents in range
for (var i = 0; i < opponentsInRange.length; i++) {
var opponent = opponentsInRange[i];
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0; // Completely stop them
self.rouletteStunnedOpponents.push(opponent);
// Visual effect for stunned opponent
tween(opponent, {
tint: 0x00FFFF,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200
});
}
if (opponentsInRange.length > 0) {
self.rouletteStunEnd = Date.now() + 1000; // 1 second stun
}
// Check if Bachira is in Flow and close to opponent goal for special message
if (self.flowActive && typeof opponentGoal !== "undefined") {
var goalDistance = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y));
if (goalDistance < 400) {
// Close to goal
// Create and display the special message
var goalMessage = game.addChild(new Text2('Futbolu seviyorum... İçimdeki Canavarla birlikte !', {
size: 64,
fill: 0xFF4500
}));
goalMessage.anchor.set(0.5, 0.5);
goalMessage.x = 1024; // Center of screen
goalMessage.y = 1000; // Middle of screen
goalMessage.alpha = 0;
// Animate message appearance
tween(goalMessage, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
onFinish: function onFinish() {
// Keep message visible for 3 seconds then fade out
LK.setTimeout(function () {
tween(goalMessage, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 800,
onFinish: function onFinish() {
goalMessage.destroy();
}
});
}, 3000);
}
});
}
}
// After Roulette, throw ball to edge and rush to it
self.throwBallToEdgeAndRush();
};
self.applyMonsterDribblingStun = function () {
var opponentsInRange = [];
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var opponentDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (opponentDist <= self.monsterDribblingStunRadius) {
opponentsInRange.push(opponent);
}
}
// Stun opponents in range
for (var i = 0; i < opponentsInRange.length; i++) {
var opponent = opponentsInRange[i];
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0; // Completely stop them
self.monsterDribblingStunnedOpponents.push(opponent);
// Visual effect for stunned opponent
tween(opponent, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250
});
}
if (opponentsInRange.length > 0) {
self.monsterDribblingStunEnd = Date.now() + 1000; // 1 second stun
}
};
self.throwBallToEdgeAndRush = function () {
// Determine which edge to throw to (left or right side of field)
var throwToLeft = self.x < 1024; // If Bachira is on left side, throw left; otherwise right
var edgeX = throwToLeft ? 200 : 1848; // Near left or right edge
var edgeY = self.y + (Math.random() - 0.5) * 200; // Slightly randomize Y position
// Ensure edge Y is within field bounds
if (edgeY < 300) edgeY = 300;
if (edgeY > 2400) edgeY = 2400;
// Throw ball to edge with moderate power
var throwDx = edgeX - ball.x;
var throwDy = edgeY - ball.y;
var throwDist = Math.sqrt(throwDx * throwDx + throwDy * throwDy);
if (throwDist > 0) {
ball.velocityX = throwDx / throwDist * 10;
ball.velocityY = throwDy / throwDist * 10;
ball.active = true;
}
// Set rush target and start rushing
self.rushTargetX = edgeX;
self.rushTargetY = edgeY;
self.rushingToBall = true;
self.readyToShoot = false;
// Visual effect for ball throw
tween(ball, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FFFF
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
};
return self;
});
var MidfieldPlayer = Container.expand(function () {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2.2;
self.role = 'midfield';
self.homeX = 1024;
self.homeY = 1000;
self.hasBall = false;
// Dribbling state
self.isDribbling = false;
self.dribbleStartTime = 0;
self.dribbleDuration = 1000;
self.dribbleRadius = 120;
self.dribbleStartAngle = 0;
self.dribbleEndAngle = 0;
self.dribbleOriginX = 0;
self.dribbleOriginY = 0;
self.hasDribbled = false;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Steal ball if player is inside opponent
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 80 && player.hasBall) {
// Steal the ball
player.hasBall = false;
ball.active = true;
// Ball moves away from player, toward midfield
var stealDx = self.x - player.x;
var stealDy = self.y - player.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
// Prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Smart midfield positioning based on ball location
if (ball.y > 1200) {
// Ball is in player's half - position defensively
var defensiveX = 1024;
var defensiveY = Math.max(1000, ball.y - 300);
var defDx = defensiveX - self.x;
var defDy = defensiveY - self.y;
var defDist = Math.sqrt(defDx * defDx + defDy * defDy);
if (defDist > 50) {
self.x += defDx / defDist * self.speed * 0.7;
self.y += defDy / defDist * self.speed * 0.7;
}
}
// Aggressive ball stealing from player team
var allPlayerTeam = [player, chigiri, hiori, reo, nagi, bachira];
for (var p = 0; p < allPlayerTeam.length; p++) {
if (allPlayerTeam[p] && allPlayerTeam[p].hasBall) {
var carrierDist = Math.sqrt((allPlayerTeam[p].x - self.x) * (allPlayerTeam[p].x - self.x) + (allPlayerTeam[p].y - self.y) * (allPlayerTeam[p].y - self.y));
if (carrierDist < 120) {
// Midfield tackle - steal ball
allPlayerTeam[p].hasBall = false;
ball.active = true;
var stealDx = self.x - allPlayerTeam[p].x;
var stealDy = self.y - allPlayerTeam[p].y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
ballDetachCooldown = ballDetachCooldownTime;
break;
}
}
}
// Midfield: get ball and pass to forward - more aggressive
if (distance < 400) {
// Move toward ball when chasing
var chaseSpeed = distance < 150 ? self.speed * 1.3 : self.speed * 1.1;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
// If close to ball, run away from player and pass to forward
if (distance < 80) {
// Move away from player for a moment
var awayDx = self.x - player.x;
var awayDy = self.y - player.y;
var awayDist = Math.sqrt(awayDx * awayDx + awayDy * awayDy);
if (awayDist > 0) {
tween(self, {
x: self.x + awayDx / awayDist * 120,
y: self.y + awayDy / awayDist * 120
}, {
duration: 200,
easing: tween.cubicOut
});
}
// Find nearest forward player
var nearestForward = null;
var nearestDistance = Infinity;
for (var i = 0; i < opponents.length; i++) {
if (opponents[i].role === 'forward') {
var forwardDx = opponents[i].x - self.x;
var forwardDy = opponents[i].y - self.y;
var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy);
if (forwardDistance < nearestDistance) {
nearestDistance = forwardDistance;
nearestForward = opponents[i];
}
}
}
// Pass to forward if found
if (nearestForward) {
// Perfect pass: ball goes directly to forward with good speed
var passDx = nearestForward.x - ball.x;
var passDy = nearestForward.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 10;
ball.velocityY = passDy / passDistance * 10;
}
} else {
// No forward to pass to: perform dribbling and curved shot
if (!self.isDribbling) {
// Start dribbling: move in a half-circle around the player
self.isDribbling = true;
self.dribbleStartTime = Date.now();
self.dribbleDuration = 1000; // 1 second
self.dribbleRadius = 120;
// Calculate angle from midfield to player
var angleToPlayer = Math.atan2(player.y - self.y, player.x - self.x);
self.dribbleStartAngle = angleToPlayer - Math.PI / 2;
self.dribbleEndAngle = angleToPlayer + Math.PI / 2;
self.dribbleOriginX = self.x;
self.dribbleOriginY = self.y;
self.hasDribbled = false;
}
if (self.isDribbling) {
var elapsed = Date.now() - self.dribbleStartTime;
var t = Math.min(elapsed / self.dribbleDuration, 1);
// Interpolate angle for half-circle
var angle = self.dribbleStartAngle + (self.dribbleEndAngle - self.dribbleStartAngle) * t;
self.x = self.dribbleOriginX + Math.cos(angle) * self.dribbleRadius;
self.y = self.dribbleOriginY + Math.sin(angle) * self.dribbleRadius;
// Ball follows midfield during dribble
if (distance < 80) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// When dribble completes, shoot with curve
if (t >= 1 && !self.hasDribbled) {
self.hasDribbled = true;
self.isDribbling = false;
// Falsolu şut: apply curve by adding to X velocity
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
// Add curve: right-footed, so curve to the right (positive X)
var curveAmount = 7;
ball.velocityX = goalDx / goalDistance * 12 + curveAmount;
ball.velocityY = goalDy / goalDistance * 12;
ball.active = true;
}
}
}
}
}
} else {
// Return to midfield position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.3;
self.y += homeDy / homeDistance * self.speed * 0.3;
}
}
};
return self;
});
var NagiPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 5.5; // Increased base speed for better movement
self.role = 'nagi';
self.homeX = 1400;
self.homeY = 2000;
self.hasBall = false;
// --- NEW NAGI ULTIMATE BALL CONTROL SYSTEM ---
self.ballControlRange = 200; // Massive ball control range
self.ballControlCooldown = 0;
self.ballControlCooldownTime = 3000; // 3 second cooldown
self.ballControlText = null;
// --- PERFECTED FAKE VOLLEY SYSTEM ---
self.fakeVolleyState = 0; // 0: ready, 1: fake motion, 2: real shot
self.fakeVolleyCombo = 0; // Track combo count
self.fakeVolleyMaxCombo = 3; // Maximum fake volleys before forced real shot
self.fakeVolleyStunRadius = 180; // Larger stun radius
self.fakeVolleyStunDuration = 1500; // 1.5 seconds stun
self.fakeVolleyStunnedOpponents = [];
self.fakeVolleyStunEnd = 0;
self.fakeVolleyCooldown = 0;
self.fakeVolleyCooldownTime = 4000; // 4 second cooldown
// --- REVOLVER SHOT SYSTEM ---
self.revolverActive = false;
self.revolverCount = 0;
self.revolverMaxShots = 5; // 5 rapid shots
self.revolverShotDelay = 300; // 300ms between shots
self.revolverLastShot = 0;
self.revolverCooldown = 0;
self.revolverCooldownTime = 15000; // 15 second cooldown
// --- GENIUS TRAP SYSTEM ---
self.geniusTrapRange = 400; // Ultra wide range
self.geniusTrapActive = false;
self.geniusTrapVisual = null;
self.geniusTrapCooldown = 0;
self.geniusTrapCooldownTime = 8000; // 8 second cooldown
// --- FLOW SYSTEM ---
self.flowActive = false;
self.flowTriggerChecked = false;
self.flowText = null;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// --- FLOW TRIGGER: Random Flow activation when close to ball ---
if (!self.flowActive && !self.flowTriggerChecked && Math.random() < 0.001) {
// 0.1% chance per frame
self.flowActive = true;
self.flowTriggerChecked = true;
// Show Flow text above Nagi
if (!self.flowText) {
self.flowText = self.addChild(new Text2('GENIUS FLOW', {
size: 48,
fill: 0x9966FF
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
// Flow lasts for 10 seconds
LK.setTimeout(function () {
self.flowActive = false;
if (self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
// Reset trigger after Flow ends
LK.setTimeout(function () {
self.flowTriggerChecked = false;
}, 30000); // Can trigger again after 30 seconds
}, 10000);
}
// Remove Flow text if not in Flow
if (!self.flowActive && self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
// Update cooldowns
if (self.ballControlCooldown > 0) {
self.ballControlCooldown -= 16;
if (self.ballControlCooldown < 0) self.ballControlCooldown = 0;
}
if (self.fakeVolleyCooldown > 0) {
self.fakeVolleyCooldown -= 16;
if (self.fakeVolleyCooldown < 0) self.fakeVolleyCooldown = 0;
}
if (self.revolverCooldown > 0) {
self.revolverCooldown -= 16;
if (self.revolverCooldown < 0) self.revolverCooldown = 0;
}
if (self.geniusTrapCooldown > 0) {
self.geniusTrapCooldown -= 16;
if (self.geniusTrapCooldown < 0) self.geniusTrapCooldown = 0;
}
// Handle stun end for fake volley
if (self.fakeVolleyStunEnd > 0 && Date.now() > self.fakeVolleyStunEnd) {
// Restore stunned opponents
for (var i = 0; i < self.fakeVolleyStunnedOpponents.length; i++) {
var opponent = self.fakeVolleyStunnedOpponents[i];
opponent.stunned = false;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.fakeVolleyStunnedOpponents = [];
self.fakeVolleyStunEnd = 0;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// --- ULTIMATE BALL CONTROL SYSTEM ---
// Automatically steal ball from ANY opponent within massive range
if (self.ballControlCooldown <= 0) {
var controlledBall = false;
// Check for opponents with ball within Ball Control range
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
if (opp.hasBall) {
var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (oppDist <= self.ballControlRange) {
// Instant ball steal from massive range
opp.hasBall = false;
ball.active = false;
ball.x = self.x;
ball.y = self.y + 40;
ball.velocityX = 0;
ball.velocityY = 0;
self.hasBall = true;
controlledBall = true;
// Show "GENIUS BALL CONTROL" text
if (!self.ballControlText) {
self.ballControlText = self.addChild(new Text2('GENIUS BALL CONTROL', {
size: 50,
fill: 0x9966FF
}));
self.ballControlText.anchor.set(0.5, 0.5);
self.ballControlText.x = 0;
self.ballControlText.y = -100;
tween(self.ballControlText, {
alpha: 1,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(self.ballControlText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
onFinish: function onFinish() {
if (self.ballControlText) {
self.ballControlText.destroy();
self.ballControlText = null;
}
}
});
}, 2000);
}
});
}
// Stun the opponent for longer duration
opp.stunned = true;
opp.originalSpeed = opp.speed;
opp.speed = 0;
tween(opp, {
tint: 0x9966FF,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
onFinish: function onFinish() {
tween(opp, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
});
LK.setTimeout(function () {
opp.stunned = false;
opp.speed = opp.originalSpeed || 2.5;
}, 3000); // 3 second stun
self.ballControlCooldown = self.ballControlCooldownTime;
break;
}
}
}
// Also control loose balls within range
if (!controlledBall && distance <= self.ballControlRange && ball.active && !self.hasBall) {
ball.active = false;
ball.x = self.x;
ball.y = self.y + 40;
ball.velocityX = 0;
ball.velocityY = 0;
self.hasBall = true;
self.ballControlCooldown = self.ballControlCooldownTime;
if (!self.ballControlText) {
self.ballControlText = self.addChild(new Text2('BALL CONTROL', {
size: 40,
fill: 0x9966FF
}));
self.ballControlText.anchor.set(0.5, 0.5);
self.ballControlText.x = 0;
self.ballControlText.y = -80;
tween(self.ballControlText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
LK.setTimeout(function () {
tween(self.ballControlText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
onFinish: function onFinish() {
if (self.ballControlText) {
self.ballControlText.destroy();
self.ballControlText = null;
}
}
});
}, 1500);
}
});
}
}
}
// --- GENIUS TRAP SYSTEM ---
// Show trap visual when ball is in range
if (distance <= self.geniusTrapRange && !self.geniusTrapVisual && !self.hasBall) {
self.geniusTrapVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
// Even larger visual
scaleY: 8,
tint: 0x9966FF
}));
self.geniusTrapVisual.alpha = 0.2;
} else if (distance > self.geniusTrapRange && self.geniusTrapVisual) {
self.geniusTrapVisual.destroy();
self.geniusTrapVisual = null;
}
// --- PERFECTED FAKE VOLLEY SYSTEM ---
if (self.hasBall && distance < 100 && self.fakeVolleyCooldown <= 0) {
if (self.fakeVolleyState === 0) {
// Start fake volley sequence
self.fakeVolleyState = 1;
self.fakeVolleyCombo++;
// Stun ALL nearby opponents with larger radius
var opponentsInRange = [];
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var opponentDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (opponentDist <= self.fakeVolleyStunRadius) {
opponentsInRange.push(opponent);
}
}
for (var i = 0; i < opponentsInRange.length; i++) {
var opponent = opponentsInRange[i];
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
self.fakeVolleyStunnedOpponents.push(opponent);
tween(opponent, {
tint: 0xFFFF00,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200
});
}
self.fakeVolleyStunEnd = Date.now() + self.fakeVolleyStunDuration;
// Fake shot motion - very minimal ball movement to show it's fake
ball.velocityX = (Math.random() - 0.5) * 1;
ball.velocityY = (Math.random() - 0.5) * 1;
ball.active = true;
LK.setTimeout(function () {
ball.active = false;
ball.x = self.x;
ball.y = self.y + 40;
ball.velocityX = 0;
ball.velocityY = 0;
self.hasBall = true;
}, 200);
// Visual effect for fake motion
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFFFF00
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
// Set up for real shot or continue faking
LK.setTimeout(function () {
if (self.fakeVolleyCombo >= self.fakeVolleyMaxCombo) {
// Must do real shot now
self.fakeVolleyState = 2;
} else {
// Can choose to fake again or shoot
self.fakeVolleyState = Math.random() < 0.6 ? 0 : 2; // 60% chance to fake again
}
}, 600);
} else if (self.fakeVolleyState === 2) {
// Real powerful shot
self.fakeVolleyState = 0;
self.fakeVolleyCombo = 0;
self.fakeVolleyCooldown = self.fakeVolleyCooldownTime;
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 18; // More powerful shot
ball.velocityY = goalDy / goalDistance * 18;
ball.active = true;
self.hasBall = false;
}
// Visual effect for real shot
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFF0000
}, {
duration: 400,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 400
});
}
});
}
}
// --- REVOLVER SHOT SYSTEM (Flow State) ---
if (self.flowActive && self.hasBall && !self.revolverActive && self.revolverCooldown <= 0) {
// Activate Revolver Shot sequence
self.revolverActive = true;
self.revolverCount = 0;
self.revolverLastShot = Date.now();
// Show Revolver text
var revolverText = self.addChild(new Text2('REVOLVER SHOT!', {
size: 60,
fill: 0xFF0000
}));
revolverText.anchor.set(0.5, 0.5);
revolverText.x = 0;
revolverText.y = -140;
tween(revolverText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
LK.setTimeout(function () {
revolverText.destroy();
}, 2000);
}
});
}
// Execute Revolver shots
if (self.revolverActive && Date.now() - self.revolverLastShot >= self.revolverShotDelay) {
self.revolverCount++;
self.revolverLastShot = Date.now();
// Fire shot toward goal
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
// Add slight randomness to each shot
var spread = (Math.random() - 0.5) * 100;
ball.velocityX = (goalDx + spread) / goalDistance * 20;
ball.velocityY = goalDy / goalDistance * 20;
ball.active = true;
}
// Visual effect for each shot
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFF6600
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 100
});
}
});
// End Revolver after all shots
if (self.revolverCount >= self.revolverMaxShots) {
self.revolverActive = false;
self.revolverCooldown = self.revolverCooldownTime;
self.hasBall = false;
}
}
// Normal movement and positioning
if (!self.revolverActive) {
// Coordinated ball pursuit - only chase if designated as active pursuer
if (self.coordinatedRole === 'pursuer' && distance < 700) {
var chaseSpeed = distance < 150 ? self.speed * 1.8 : self.speed * 1.5;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 700) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 1.1; // Faster support movement
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Strategic striker play when close to ball
if (distance < 80 && !self.hasBall) {
// Check for teammates to pass to if Nagi gets the ball
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, hiori, reo, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 15% chance to pass to teammate, 85% for abilities/shooting (ultra striker focus)
if (nearestTeammate && Math.random() < 0.15 && self.hasBall) {
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 15; // Stronger pass
ball.velocityY = passDy / passDistance * 15;
ball.active = true;
self.hasBall = false;
}
}
}
// Striker positioning - always look for goal scoring opportunities
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 1.0; // Full speed tactical movement
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else {
// Return to home position when not actively playing
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.6; // Faster return home
self.y += homeDy / homeDistance * self.speed * 0.6;
}
}
}
};
// Steal ball from nearby opponents (enhanced version)
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (oppDist < 90 && opponent.hasBall && self.ballControlCooldown <= 0) {
// Enhanced ball stealing
opponent.hasBall = false;
ball.active = false;
ball.x = self.x;
ball.y = self.y + 40;
ball.velocityX = 0;
ball.velocityY = 0;
self.hasBall = true;
self.ballControlCooldown = self.ballControlCooldownTime;
// Stun the opponent for 2 seconds after stealing ball
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
tween(opponent, {
tint: 0x9966FF,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 300,
onFinish: function onFinish() {
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
});
LK.setTimeout(function () {
opponent.stunned = false;
opponent.speed = opponent.originalSpeed || 2.5;
}, 2000);
break;
}
}
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.hasBall = false;
// 1v1 dribble move logic for player
self.lastX = self.lastX || self.x;
self.lastY = self.lastY || self.y;
self.lastDribbleAttempt = self.lastDribbleAttempt || 0;
self.isDribbling1v1 = self.isDribbling1v1 || false;
self.dribble1v1Start = self.dribble1v1Start || 0;
self.dribble1v1Duration = 400; // ms
self.dribble1v1TargetX = self.dribble1v1TargetX || 0;
self.dribble1v1TargetY = self.dribble1v1TargetY || 0;
self.update = function () {
// Check for ball stealing from opponents
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (oppDist < 80 && opponent.hasBall) {
// Steal the ball
opponent.hasBall = false;
ball.active = true;
// Ball moves toward Player
var stealDx = self.x - opponent.x;
var stealDy = self.y - opponent.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 8;
ball.velocityY = stealDy / stealDist * 8;
}
ballDetachCooldown = ballDetachCooldownTime;
// Stun the opponent for 2 seconds after stealing ball
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
tween(opponent, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.setTimeout(function () {
opponent.stunned = false;
opponent.speed = opponent.originalSpeed || 2.5;
}, 2000);
break;
}
}
// 1v1 dribble move: if player is close to an opponent and moving toward them, trigger a dribble move
var closestOpponent = null;
var closestDist = Infinity;
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var dist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (dist < closestDist) {
closestDist = dist;
closestOpponent = opp;
}
}
// Only allow dribble if not already dribbling, cooldown, and close enough
if (!self.isDribbling1v1 && Date.now() - (self.lastDribbleAttempt || 0) > 1200 && closestDist < 110 && closestOpponent && !closestOpponent.stunned) {
// Check if player is moving toward opponent
var vx = self.x - (self.lastX || self.x);
var vy = self.y - (self.lastY || self.y);
var toOppX = closestOpponent.x - self.x;
var toOppY = closestOpponent.y - self.y;
var dot = vx * toOppX + vy * toOppY;
if (dot > 0) {
// Initiate dribble move: dash past opponent
self.isDribbling1v1 = true;
self.dribble1v1Start = Date.now();
self.lastDribbleAttempt = Date.now();
// Target a point just past the opponent
var oppDist = Math.sqrt(toOppX * toOppX + toOppY * toOppY);
var overshoot = 80;
self.dribble1v1TargetX = self.x + toOppX / oppDist * (oppDist + overshoot);
self.dribble1v1TargetY = self.y + toOppY / oppDist * (oppDist + overshoot);
// Stun the opponent briefly
closestOpponent.stunned = true;
closestOpponent.originalSpeed = closestOpponent.speed;
closestOpponent.speed = 0;
tween(closestOpponent, {
tint: 0xAAAAAA,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200,
onFinish: function onFinish() {
tween(closestOpponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.setTimeout(function () {
closestOpponent.stunned = false;
closestOpponent.speed = closestOpponent.originalSpeed || 2.5;
}, 1000);
// Visual effect for player
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 120,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
}
}
// Animate dribble move
if (self.isDribbling1v1) {
var t = Math.min((Date.now() - self.dribble1v1Start) / self.dribble1v1Duration, 1);
// Use linear interpolation for smooth movement, but only for the duration of the dribble
self.x = self.lastX + (self.dribble1v1TargetX - self.lastX) * t;
self.y = self.lastY + (self.dribble1v1TargetY - self.lastY) * t;
if (t >= 1) {
self.isDribbling1v1 = false;
}
} else {
// Only update lastX/lastY when not dribbling, to avoid jitter
self.lastX = self.x;
self.lastY = self.y;
}
};
return self;
});
var ReoPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 4;
self.role = 'reo';
self.homeX = 600;
self.homeY = 1800;
self.hasBall = false;
// Mode switching variables
self.isDefensiveMode = true;
self.defensiveColor = 0x000000; // Black
self.attackingColor = 0xffa500; // Orange
self.modeSwitch = 0;
self.modeSwitchInterval = 5000; // 5 seconds
// Chameleon ability variables
self.chameleonCooldown = 0;
self.chameleonInterval = 30000; // 30 seconds
self.copiedStats = null;
self.originalSpeed = 4;
self.originalDribbling = 1;
self.originalShot = 1;
// Copied ability variables
self.copiedWinterZone = false;
self.copiedWinterZoneRadius = 200;
self.copiedWinterZoneCooldown = 0;
self.copiedWinterZoneActive = false;
self.copiedWinterZoneDuration = 0;
self.copiedWinterZoneMaxDuration = 8000;
self.copiedSlowedOpponents = [];
self.copiedWinterZoneVisual = null;
self.copiedSpeedBurst = false;
self.copiedSpeedBurstCooldown = 0;
self.copiedSpeedBurstActive = false;
self.copiedBurstSpeed = 10;
self.copiedNormalSpeed = 4;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Steal ball from nearby opponents and stun them
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var oppDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (oppDist < 80 && opponent.hasBall) {
// Steal the ball
opponent.hasBall = false;
ball.active = true;
// Ball moves toward Reo
var stealDx = self.x - opponent.x;
var stealDy = self.y - opponent.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 8;
ball.velocityY = stealDy / stealDist * 8;
}
ballDetachCooldown = ballDetachCooldownTime;
// Stun the opponent for 2 seconds after stealing ball
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
tween(opponent, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
onFinish: function onFinish() {
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
LK.setTimeout(function () {
opponent.stunned = false;
opponent.speed = opponent.originalSpeed || 2.5;
}, 2000);
break;
}
}
// Update mode switching
self.modeSwitch += 16;
if (self.modeSwitch >= self.modeSwitchInterval) {
self.modeSwitch = 0;
self.isDefensiveMode = !self.isDefensiveMode;
// Change color based on mode
if (self.isDefensiveMode) {
tween(playerGraphics, {
tint: self.defensiveColor
}, {
duration: 300
});
} else {
tween(playerGraphics, {
tint: self.attackingColor
}, {
duration: 300
});
}
}
// Update Chameleon cooldown
self.chameleonCooldown += 16;
if (self.chameleonCooldown >= self.chameleonInterval) {
self.chameleonCooldown = 0;
self.activateChameleon();
}
// Update copied abilities
if (self.copiedWinterZone) {
// Update Winter Zone cooldown
if (self.copiedWinterZoneCooldown > 0) {
self.copiedWinterZoneCooldown -= 16;
if (self.copiedWinterZoneCooldown < 0) self.copiedWinterZoneCooldown = 0;
}
// Winter Zone duration and effect
if (self.copiedWinterZoneActive) {
self.copiedWinterZoneDuration -= 16;
if (self.copiedWinterZoneDuration <= 0) {
self.copiedWinterZoneActive = false;
// Remove Winter Zone visual
if (self.copiedWinterZoneVisual) {
self.copiedWinterZoneVisual.destroy();
self.copiedWinterZoneVisual = null;
}
// Restore opponent speeds
for (var i = 0; i < self.copiedSlowedOpponents.length; i++) {
var opponent = self.copiedSlowedOpponents[i];
opponent.speed = opponent.originalSpeed || opponent.speed * 2;
}
self.copiedSlowedOpponents = [];
} else {
// Apply Winter Zone effect to nearby opponents
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= self.copiedWinterZoneRadius) {
if (self.copiedSlowedOpponents.indexOf(opponent) === -1) {
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * 0.3; // Slow to 30% speed
self.copiedSlowedOpponents.push(opponent);
}
}
}
}
}
// Activate Winter Zone when opponents get close
if (!self.copiedWinterZoneActive && self.copiedWinterZoneCooldown <= 0) {
var nearbyOpponents = 0;
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= self.copiedWinterZoneRadius + 50) {
nearbyOpponents++;
}
}
if (nearbyOpponents >= 2) {
// Activate Winter Zone
self.copiedWinterZoneActive = true;
self.copiedWinterZoneDuration = self.copiedWinterZoneMaxDuration;
self.copiedWinterZoneCooldown = 12000; // 12 seconds cooldown
// Create Winter Zone visual area
self.copiedWinterZoneVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4,
tint: 0x87CEEB
}));
self.copiedWinterZoneVisual.alpha = 0.3;
// Visual effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0x87CEEB
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor
}, {
duration: 200
});
}
});
}
}
}
if (self.copiedSpeedBurst) {
// Update Speed Burst cooldown
if (self.copiedSpeedBurstCooldown > 0) {
self.copiedSpeedBurstCooldown -= 16;
if (self.copiedSpeedBurstCooldown < 0) self.copiedSpeedBurstCooldown = 0;
}
// Speed burst when close to ball
if (!self.copiedSpeedBurstActive && self.copiedSpeedBurstCooldown <= 0 && distance < 100) {
self.copiedSpeedBurstActive = true;
self.copiedSpeedBurstCooldown = 8000; // 8 seconds cooldown
self.speed = self.copiedBurstSpeed;
// Visual effect for speed burst
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Speed burst lasts for 2 seconds
LK.setTimeout(function () {
self.copiedSpeedBurstActive = false;
self.speed = self.copiedNormalSpeed;
}, 2000);
}
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Mode-based behavior with coordination
if (self.isDefensiveMode) {
// Defensive mode: more active interception and ball pursuit
if (self.coordinatedRole === 'pursuer' && distance < 750) {
var defensiveSpeed = self.speed * 1.4;
self.x += dx / distance * defensiveSpeed;
self.y += dy / distance * defensiveSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 750) {
// Move to defensive support position
var supportPos = ballPursuitCoordinator.getSupportPosition('defense');
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 0.9;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Defensive tackle
if (distance < 80) {
var clearX = ball.x < 1024 ? ball.x - 300 : ball.x + 300;
var clearY = ball.y - 200;
var clearDx = clearX - ball.x;
var clearDy = clearY - ball.y;
var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy);
if (clearDist > 0) {
ball.velocityX = clearDx / clearDist * 8;
ball.velocityY = clearDy / clearDist * 8;
}
}
} else {
// Return to defensive position, but move more actively
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.7;
self.y += homeDy / homeDistance * self.speed * 0.7;
}
}
} else {
// Attacking mode: chase ball very aggressively and support attack
if (self.coordinatedRole === 'pursuer' && distance < 900) {
var attackSpeed = self.speed * 2.0;
self.x += dx / distance * attackSpeed;
self.y += dy / distance * attackSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 900) {
// Move to attacking support position
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 1.2;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Adaptive tactical play based on mode when close to ball
if (distance < 80) {
// Mode-based strategic decision making
if (self.isDefensiveMode) {
// Defensive mode - prioritize safe clearing and distribution
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic defensive pass executed
} else {
// Fallback defensive action
var clearX = ball.x < 1024 ? ball.x - 300 : ball.x + 300;
var clearY = ball.y - 200;
var clearDx = clearX - ball.x;
var clearDy = clearY - ball.y;
var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy);
if (clearDist > 0) {
ball.velocityX = clearDx / clearDist * 10;
ball.velocityY = clearDy / clearDist * 10;
}
}
} else {
// Attacking mode - focus on progressive play
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic attacking pass executed
} else {
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, hiori, nagi, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 450) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 35% chance to pass if teammate available, otherwise shoot
if (nearestTeammate && Math.random() < 0.35) {
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 12;
ball.velocityY = passDy / passDistance * 12;
}
} else {
// Attack shot
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 14;
ball.velocityY = goalDy / goalDistance * 14;
}
}
}
}
}
// Position based on tactical role and mode
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * (self.isDefensiveMode ? 0.6 : 0.8);
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else {
// If not near ball, move up to a supporting attacking position
var supportX = opponentGoal.x - 200;
var supportY = opponentGoal.y + 400;
var supportDx = supportX - self.x;
var supportDy = supportY - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 30) {
self.x += supportDx / supportDist * self.speed * 0.7;
self.y += supportDy / supportDist * self.speed * 0.7;
}
}
}
};
self.activateChameleon = function () {
// Find random NPC to copy stats from (including player team members)
var availableNPCs = [];
for (var i = 0; i < opponents.length; i++) {
availableNPCs.push(opponents[i]);
}
// Also add player team members with special abilities
if (typeof hiori !== 'undefined') availableNPCs.push(hiori);
if (typeof chigiri !== 'undefined') availableNPCs.push(chigiri);
if (typeof nagi !== 'undefined') availableNPCs.push(nagi);
if (availableNPCs.length > 0) {
var randomNPC = availableNPCs[Math.floor(Math.random() * availableNPCs.length)];
// Copy stats from random NPC
self.copiedStats = {
speed: randomNPC.speed || self.originalSpeed,
dribbling: randomNPC.dribbleSpeed || self.originalDribbling,
shot: randomNPC.burstSpeed || self.originalShot,
role: randomNPC.role || 'reo'
};
// Apply copied stats
self.speed = self.copiedStats.speed;
// Copy special abilities based on role
if (self.copiedStats.role === 'hiori') {
// Copy Winter Zone ability
self.copiedWinterZone = true;
self.copiedWinterZoneRadius = 200;
self.copiedWinterZoneCooldown = 0;
self.copiedWinterZoneActive = false;
self.copiedWinterZoneDuration = 0;
self.copiedWinterZoneMaxDuration = 8000; // Slightly shorter than original
self.copiedSlowedOpponents = [];
self.copiedWinterZoneVisual = null;
} else if (self.copiedStats.role === 'chigiri') {
// Copy Speed Burst ability
self.copiedSpeedBurst = true;
self.copiedSpeedBurstCooldown = 0;
self.copiedSpeedBurstActive = false;
self.copiedBurstSpeed = 10;
self.copiedNormalSpeed = self.speed;
} else {
// Reset copied abilities
self.copiedWinterZone = false;
self.copiedSpeedBurst = false;
}
// Visual effect for Chameleon activation
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xff00ff
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor
}, {
duration: 200
});
}
});
}
};
return self;
});
var SaePlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 3.2;
self.role = 'sae';
self.homeX = 1024;
self.homeY = 800;
self.hasBall = false;
// Perfect Pass variables
self.perfectPassRange = 800;
self.perfectPassCooldown = 0;
self.perfectPassCooldownTime = 8000; // 8 seconds
// Half-circle dribbling variables
self.isDribbling = false;
self.dribbleStartTime = 0;
self.dribbleDuration = 1500; // 1.5 seconds
self.dribbleRadius = 100;
self.dribbleStartAngle = 0;
self.dribbleEndAngle = 0;
self.dribbleOriginX = 0;
self.dribbleOriginY = 0;
self.dribbleStunRadius = 150;
self.dribbleStunDuration = 2000; // 2 seconds
self.dribbleStunnedOpponents = [];
self.dribbleStunEnd = 0;
// Zigzag dribbling variables
self.isZigzagDribbling = false;
self.zigzagStartTime = 0;
self.zigzagDuration = 2000; // 2 seconds
self.zigzagStartX = 0;
self.zigzagStartY = 0;
self.zigzagTargetX = 0;
self.zigzagTargetY = 0;
self.zigzagDevourRadius = 120;
// Zigzag chain variables
self.zigzagCount = 0;
self.zigzagPaused = false;
self.zigzagPauseStart = 0;
self.zigzagPauseDuration = 10000; // 10 seconds pause after 6 zigzags
// Chop dribbling variables
self.isChopDribbling = false;
self.chopStartTime = 0;
self.chopDuration = 1000; // 1 second
self.chopStartX = 0;
self.chopStartY = 0;
self.chopTargetX = 0;
self.chopTargetY = 0;
self.chopDevourRadius = 100;
// Devoured effect variables
self.devouredOpponents = [];
self.devouredEndTime = 0;
self.devouredDuration = 3000; // 3 seconds
// Dash ability variables
self.dashCooldown = 0;
self.dashCooldownTime = 10000; // 10 seconds
self.dashSpeed = 10;
self.dashDuration = 800; // 0.8 seconds
self.dashActive = false;
self.dashEndTime = 0;
// False shot variables
self.falseShotCooldown = 0;
self.falseShotCooldownTime = 12000; // 12 seconds
self.falseShotActive = false;
// FLOW SYSTEM for Sae
if (typeof self.flowActive === "undefined") {
self.flowActive = false;
self.flowTriggerChecked = false;
self.flowDribbleCount = 0;
self.flowDribbleMax = 999; // Unlimited while in Flow
self.flowText = null;
self.flowHilalDribbling = false;
self.flowHilalStartTime = 0;
self.flowHilalDuration = 1200; // ms per crescent
self.flowHilalOriginX = 0;
self.flowHilalOriginY = 0;
self.flowHilalRadius = 180;
self.flowHilalStartAngle = 0;
self.flowHilalEndAngle = 0;
self.flowHilalPhase = 0;
self.flowHilalCount = 0;
self.flowHilalMax = 4; // Number of crescents before shot
self.flowHilalShotDone = false;
}
// FLOW TRIGGER: If 2 goals scored by Sae, activate Flow (only once)
if (!self.flowActive && !self.flowTriggerChecked && typeof opponentScore !== "undefined" && opponentScore >= 2) {
self.flowActive = true;
self.flowDribbleCount = 0;
self.flowTriggerChecked = true;
self.flowHilalDribbling = false;
self.flowHilalCount = 0;
self.flowHilalShotDone = false;
// Show Flow text above Sae
if (!self.flowText) {
self.flowText = self.addChild(new Text2('FLOW', {
size: 48,
fill: 0xFF6600
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
}
// Remove Flow text if not in Flow
if (!self.flowActive && self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Update cooldowns
if (self.perfectPassCooldown > 0) {
self.perfectPassCooldown -= 16;
if (self.perfectPassCooldown < 0) self.perfectPassCooldown = 0;
}
if (self.dashCooldown > 0) {
self.dashCooldown -= 16;
if (self.dashCooldown < 0) self.dashCooldown = 0;
}
if (self.falseShotCooldown > 0) {
self.falseShotCooldown -= 16;
if (self.falseShotCooldown < 0) self.falseShotCooldown = 0;
}
// Handle dribble stun end
if (self.dribbleStunEnd > 0 && Date.now() > self.dribbleStunEnd) {
// Restore stunned opponents
for (var i = 0; i < self.dribbleStunnedOpponents.length; i++) {
var opponent = self.dribbleStunnedOpponents[i];
opponent.stunned = false;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.dribbleStunnedOpponents = [];
self.dribbleStunEnd = 0;
}
// Handle dash duration
if (self.dashActive && Date.now() > self.dashEndTime) {
self.dashActive = false;
self.speed = 4.5; // Return to normal speed
}
// Steal ball if player is inside opponent
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 80 && player.hasBall) {
// Steal the ball
player.hasBall = false;
ball.active = true;
// Ball moves away from player, toward Sae
var stealDx = self.x - player.x;
var stealDy = self.y - player.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
// Prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Handle devoured opponents restoration
if (self.devouredEndTime > 0 && Date.now() > self.devouredEndTime) {
// Restore devoured opponents
for (var i = 0; i < self.devouredOpponents.length; i++) {
var opponent = self.devouredOpponents[i];
opponent.speed = opponent.originalSpeed || opponent.speed * 2;
opponent.devoured = false;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.devouredOpponents = [];
self.devouredEndTime = 0;
}
// Half-circle dribbling logic
if (self.isDribbling) {
var elapsed = Date.now() - self.dribbleStartTime;
var t = Math.min(elapsed / self.dribbleDuration, 1);
// Interpolate angle for half-circle
var angle = self.dribbleStartAngle + (self.dribbleEndAngle - self.dribbleStartAngle) * t;
self.x = self.dribbleOriginX + Math.cos(angle) * self.dribbleRadius;
self.y = self.dribbleOriginY + Math.sin(angle) * self.dribbleRadius;
// Ball follows during dribble
if (distance < 80) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// When dribble completes, apply Devoured effect
if (t >= 1) {
self.isDribbling = false;
self.applyDevouredEffect(self.dribbleStunRadius);
}
}
// Zigzag dribbling logic
if (self.isZigzagDribbling) {
var elapsed = Date.now() - self.zigzagStartTime;
var progress = Math.min(elapsed / self.zigzagDuration, 1);
// Create zigzag pattern
var zigzagAmplitude = 60;
var zigzagFrequency = 6;
var zigzagOffset = Math.sin(progress * Math.PI * zigzagFrequency) * zigzagAmplitude;
// Move toward target with zigzag motion
var targetDx = self.zigzagTargetX - self.zigzagStartX;
var targetDy = self.zigzagTargetY - self.zigzagStartY;
var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDist > 0) {
// Calculate perpendicular vector for zigzag
var perpX = -targetDy / targetDist;
var perpY = targetDx / targetDist;
// Apply zigzag movement
self.x = self.zigzagStartX + targetDx * progress + perpX * zigzagOffset;
self.y = self.zigzagStartY + targetDy * progress + perpY * zigzagOffset;
// Ball follows during dribble
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
}
// End zigzag dribbling and apply Devoured effect
if (progress >= 1) {
self.isZigzagDribbling = false;
self.applyDevouredEffect(self.zigzagDevourRadius);
// Chain up to 6 zigzags, then pause
self.zigzagCount = (self.zigzagCount || 0) + 1;
if (self.zigzagCount < 6) {
// Start another zigzag immediately
self.isZigzagDribbling = true;
self.zigzagStartTime = Date.now();
self.zigzagStartX = self.x;
self.zigzagStartY = self.y;
// Dribble toward player goal with reduced distance
self.zigzagTargetX = self.x + (playerGoal.x - self.x) * 0.3 + (Math.random() - 0.5) * 80;
self.zigzagTargetY = self.y + (playerGoal.y - self.y) * 0.3 + 50;
} else {
// Pause after 6 zigzags
self.zigzagPaused = true;
self.zigzagPauseStart = Date.now();
self.zigzagCount = 0;
}
}
}
// Zigzag pause logic
if (self.zigzagPaused) {
var pauseElapsed = Date.now() - self.zigzagPauseStart;
if (pauseElapsed >= self.zigzagPauseDuration) {
self.zigzagPaused = false;
}
}
// Chop dribbling logic
if (self.isChopDribbling) {
var elapsed = Date.now() - self.chopStartTime;
var progress = Math.min(elapsed / self.chopDuration, 1);
// Quick chop movement - sharp direction change
var chopPhase = progress < 0.5 ? progress * 2 : 1 - (progress - 0.5) * 2;
var chopIntensity = Math.sin(chopPhase * Math.PI) * 80;
// Move with chop motion
var targetDx = self.chopTargetX - self.chopStartX;
var targetDy = self.chopTargetY - self.chopStartY;
var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDist > 0) {
// Calculate perpendicular vector for chop
var perpX = -targetDy / targetDist;
var perpY = targetDx / targetDist;
// Apply chop movement
self.x = self.chopStartX + targetDx * progress + perpX * chopIntensity;
self.y = self.chopStartY + targetDy * progress + perpY * chopIntensity;
// Ball follows during dribble
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
}
// End chop dribbling and apply Devoured effect
if (progress >= 1) {
self.isChopDribbling = false;
self.applyDevouredEffect(self.chopDevourRadius);
}
}
// FLOW: If Sae is in Flow, perform continuous crescent (hilal) dribble and finish with a striker shot
if (self.flowActive) {
// Start Hilal dribble if not already started and not shot yet
if (!self.flowHilalDribbling && !self.flowHilalShotDone && distance < 120) {
self.flowHilalDribbling = true;
self.flowHilalStartTime = Date.now();
self.flowHilalOriginX = self.x;
self.flowHilalOriginY = self.y;
// Each crescent alternates direction for visual effect
var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x);
var dir = self.flowHilalCount % 2 === 0 ? 1 : -1;
self.flowHilalStartAngle = angleToGoal - dir * Math.PI / 2;
self.flowHilalEndAngle = angleToGoal + dir * Math.PI / 2;
self.flowHilalPhase = 0;
}
// Hilal dribble logic
if (self.flowHilalDribbling && !self.flowHilalShotDone) {
var elapsed = Date.now() - self.flowHilalStartTime;
var t = Math.min(elapsed / self.flowHilalDuration, 1);
var angle = self.flowHilalStartAngle + (self.flowHilalEndAngle - self.flowHilalStartAngle) * t;
self.x = self.flowHilalOriginX + Math.cos(angle) * self.flowHilalRadius;
self.y = self.flowHilalOriginY + Math.sin(angle) * self.flowHilalRadius;
// Ball follows during dribble
if (distance < 120) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// When crescent completes, start next or shoot
if (t >= 1) {
self.flowHilalDribbling = false;
self.flowHilalCount++;
// Visual effect for each crescent
tween(self, {
scaleX: 1.15,
scaleY: 1.15,
tint: 0xFF6600
}, {
duration: 120,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 120
});
}
});
// After enough crescents, shoot at goal
if (self.flowHilalCount >= self.flowHilalMax) {
// Striker shot: powerful, straight, with curve
var goalDx = playerGoal.x - self.x;
var goalDy = playerGoal.y - self.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
var curveAmount = 18 + Math.random() * 8;
ball.velocityX = goalDx / goalDistance * 20 + curveAmount;
ball.velocityY = goalDy / goalDistance * 20;
ball.active = true;
// Visual effect for shot
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFF6600
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
self.flowHilalShotDone = true;
// Reset after a short delay for next Flow
LK.setTimeout(function () {
self.flowActive = false;
self.flowHilalCount = 0;
self.flowHilalShotDone = false;
}, 1200);
} else {
// Start next crescent immediately
self.flowHilalDribbling = true;
self.flowHilalStartTime = Date.now();
self.flowHilalOriginX = self.x;
self.flowHilalOriginY = self.y;
var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x);
var dir = self.flowHilalCount % 2 === 0 ? 1 : -1;
self.flowHilalStartAngle = angleToGoal - dir * Math.PI / 2;
self.flowHilalEndAngle = angleToGoal + dir * Math.PI / 2;
self.flowHilalPhase = 0;
}
}
// Prevent other dribbling/shot logic during Flow
return;
}
}
// (Original code follows)
// Sae behavior: pursue ball but stay in attacking areas
if (distance < 600) {
// Use dash if available and close to ball
if (!self.dashActive && self.dashCooldown <= 0 && distance < 150) {
self.dashActive = true;
self.dashCooldown = self.dashCooldownTime;
self.dashEndTime = Date.now() + self.dashDuration;
self.speed = self.dashSpeed * 0.8;
// Visual effect for dash
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FFFF
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
}
// Move toward ball but avoid going too deep into player's half
var pursuitSpeed = distance < 200 ? self.speed * 1.1 : self.speed * 0.9;
// Limit Sae's movement to stay in upper half of field (y < 1500)
var targetX = self.x + dx / distance * pursuitSpeed;
var targetY = self.y + dy / distance * pursuitSpeed;
if (targetY > 1500) {
// Don't go too deep, stay in attacking position
targetY = Math.min(targetY, 1500);
}
self.x = targetX;
self.y = targetY;
// Actions when close to ball
if (distance < 80) {
// Perfect Pass - find forward player to pass to
if (self.perfectPassCooldown <= 0) {
var nearestForward = null;
var nearestDistance = Infinity;
for (var i = 0; i < opponents.length; i++) {
if (opponents[i].role === 'forward') {
var forwardDx = opponents[i].x - self.x;
var forwardDy = opponents[i].y - self.y;
var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy);
if (forwardDistance < nearestDistance && forwardDistance < self.perfectPassRange) {
nearestDistance = forwardDistance;
nearestForward = opponents[i];
}
}
}
if (nearestForward) {
// Execute Perfect Pass
var passDx = nearestForward.x - ball.x;
var passDy = nearestForward.y - ball.y;
var passDist = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDist > 0) {
ball.velocityX = passDx / passDist * 10;
ball.velocityY = passDy / passDist * 10;
ball.active = true;
}
self.perfectPassCooldown = self.perfectPassCooldownTime;
// Visual effect for Perfect Pass
tween(ball, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0x00FF00
}, {
duration: 200,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
}
// Choose dribbling type randomly if no pass available
if (!self.isDribbling && !self.isZigzagDribbling && !self.isChopDribbling && self.perfectPassCooldown > 0) {
var dribbleType = Math.random();
if (dribbleType < 0.33) {
// Half-circle dribbling
self.isDribbling = true;
self.dribbleStartTime = Date.now();
self.dribbleRadius = 100;
// Calculate angle from Sae to player goal
var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x);
self.dribbleStartAngle = angleToGoal - Math.PI / 2;
self.dribbleEndAngle = angleToGoal + Math.PI / 2;
self.dribbleOriginX = self.x;
self.dribbleOriginY = self.y;
} else if (dribbleType < 0.66) {
// Zigzag dribbling
self.isZigzagDribbling = true;
self.zigzagStartTime = Date.now();
self.zigzagStartX = self.x;
self.zigzagStartY = self.y;
// Dribble toward player goal with reduced distance
self.zigzagTargetX = self.x + (playerGoal.x - self.x) * 0.3 + (Math.random() - 0.5) * 80;
self.zigzagTargetY = self.y + (playerGoal.y - self.y) * 0.3 + 50;
// Visual effect for zigzag start
tween(self, {
tint: 0xFF4500
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
} else {
// Chop dribbling
self.isChopDribbling = true;
self.chopStartTime = Date.now();
self.chopStartX = self.x;
self.chopStartY = self.y;
// Chop toward player goal (even shorter range)
self.chopTargetX = self.x + (playerGoal.x - self.x) * 0.2 + (Math.random() - 0.5) * 30;
self.chopTargetY = self.y + (playerGoal.y - self.y) * 0.2 + 20;
// Visual effect for chop start
tween(self, {
tint: 0x32CD32
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
}
}
// False shot ability
if (self.falseShotCooldown <= 0 && !self.falseShotActive) {
self.falseShotActive = true;
self.falseShotCooldown = self.falseShotCooldownTime;
// Fake shot motion - ball barely moves
ball.velocityX = (Math.random() - 0.5) * 3;
ball.velocityY = (Math.random() - 0.5) * 3;
ball.active = true;
// Visual effect for false shot
tween(self, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF6600
}, {
duration: 300,
onFinish: function onFinish() {
// After fake, real shot toward goal
var goalDx = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 14;
ball.velocityY = goalDy / goalDistance * 14;
ball.active = true;
}
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
self.falseShotActive = false;
}
});
}
}
} else {
// Sae is always active: maintain attacking midfield position
var attackingMidfieldX = 1024 + (Math.random() - 0.5) * 300;
var attackingMidfieldY = 1000 + Math.random() * 200; // Stay in upper midfield area
var shotDx = attackingMidfieldX - self.x;
var shotDy = attackingMidfieldY - self.y;
var shotDist = Math.sqrt(shotDx * shotDx + shotDy * shotDy);
if (shotDist > 50) {
self.x += shotDx / shotDist * self.speed * 0.5;
self.y += shotDy / shotDist * self.speed * 0.5;
}
// If far from ball but in shooting range, attempt long-range curved shot
if (distance > 200 && distance < 700 && !self.isDribbling && !self.isZigzagDribbling && !self.isChopDribbling && !self.falseShotActive) {
// Falsolu şut: apply curve by adding to X velocity
var goalDx = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
var curveAmount = 12 + Math.random() * 8; // Stronger curve for long shot
ball.velocityX = goalDx / goalDistance * 16 + curveAmount;
ball.velocityY = goalDy / goalDistance * 16;
ball.active = true;
// Visual effect for long-range shot
tween(self, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF1493
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
}
}
// Apply Devoured effect to nearby opponents
self.applyDevouredEffect = function (radius) {
var opponentsInRange = [];
// Check all player team members
if (typeof player !== 'undefined') {
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist <= radius) {
opponentsInRange.push(player);
}
}
if (typeof chigiri !== 'undefined') {
var chigiriDist = Math.sqrt((chigiri.x - self.x) * (chigiri.x - self.x) + (chigiri.y - self.y) * (chigiri.y - self.y));
if (chigiriDist <= radius) {
opponentsInRange.push(chigiri);
}
}
if (typeof hiori !== 'undefined') {
var hioriDist = Math.sqrt((hiori.x - self.x) * (hiori.x - self.x) + (hiori.y - self.y) * (hiori.y - self.y));
if (hioriDist <= radius) {
opponentsInRange.push(hiori);
}
}
if (typeof reo !== 'undefined') {
var reoDist = Math.sqrt((reo.x - self.x) * (reo.x - self.x) + (reo.y - self.y) * (reo.y - self.y));
if (reoDist <= radius) {
opponentsInRange.push(reo);
}
}
if (typeof nagi !== 'undefined') {
var nagiDist = Math.sqrt((nagi.x - self.x) * (nagi.x - self.x) + (nagi.y - self.y) * (nagi.y - self.y));
if (nagiDist <= radius) {
opponentsInRange.push(nagi);
}
}
// Apply Devoured effect to found opponents
for (var i = 0; i < opponentsInRange.length; i++) {
var opponent = opponentsInRange[i];
// 30% chance to apply full effect, otherwise just stun
var isFullDevoured = Math.random() < 0.3;
if (isFullDevoured) {
// Full Devoured effect: severely reduce speed
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * 0.2; // 20% speed
opponent.devoured = true;
self.devouredOpponents.push(opponent);
// Visual effect for devoured opponent
tween(opponent, {
tint: 0x800080,
// Purple tint for devoured
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300
});
} else {
// Just stun effect
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
opponent.stunned = true;
self.dribbleStunnedOpponents.push(opponent);
// Visual effect for stunned opponent
tween(opponent, {
tint: 0xFFFF00,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200
});
}
}
if (opponentsInRange.length > 0) {
self.devouredEndTime = Date.now() + self.devouredDuration;
if (self.dribbleStunnedOpponents.length > 0) {
self.dribbleStunEnd = Date.now() + self.dribbleStunDuration;
}
}
};
};
return self;
});
var SoccerBall = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.friction = 0.98;
self.active = true;
self.untouchable = false; // Ball can be made untouchable by goalkeepers
self.update = function () {
if (self.active) {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// Check if ball has stopped moving and make it touchable again
if (self.untouchable) {
var ballSpeed = Math.sqrt(self.velocityX * self.velocityX + self.velocityY * self.velocityY);
if (ballSpeed < 0.5) {
// Ball has essentially stopped
self.untouchable = false;
}
}
// Keep ball in bounds with bounce effect
if (self.x < 30) {
self.x = 30;
self.velocityX = Math.abs(self.velocityX) * 0.8;
}
if (self.x > 2018) {
self.x = 2018;
self.velocityX = -Math.abs(self.velocityX) * 0.8;
}
if (self.y < 30) {
self.y = 30;
self.velocityY = Math.abs(self.velocityY) * 0.8;
}
if (self.y > 2702) {
self.y = 2702;
self.velocityY = -Math.abs(self.velocityY) * 0.8;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x228B22
});
/****
* Game Code
****/
game.setBackgroundColor(0x228B22);
// Game variables
var opponents = [];
var playerScore = 0;
var opponentScore = 0;
var dragNode = null;
var maxStamina = 100;
var currentStamina = 100;
var staminaRegenRate = 0.2;
var staminaDrainRate = 0.5;
var lastPlayerX = 0;
var lastPlayerY = 0;
var shotChargeStart = 0;
var isChargingShot = false;
var shotPower = 0;
var maxShotPower = 20;
var minChargeTime = 2000; // 2 seconds minimum charge time
var lastTouchX = 0;
var lastTouchY = 0;
var ballDetachCooldown = 0;
var ballDetachCooldownTime = 1000; // 1 second cooldown
// UI Elements
var scoreTxt = new Text2('Player: 0 - Opponent: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// Stamina UI
var staminaTxt = new Text2('Stamina: 100', {
size: 60,
fill: 0x00FF00
});
staminaTxt.anchor.set(0, 0);
staminaTxt.x = 50;
staminaTxt.y = 50;
LK.gui.topLeft.addChild(staminaTxt);
// Shot Power UI
var shotPowerTxt = new Text2('Shot Power: 0%', {
size: 60,
fill: 0xFFFFFF
});
shotPowerTxt.anchor.set(0, 0);
shotPowerTxt.x = 50;
shotPowerTxt.y = 130;
LK.gui.topLeft.addChild(shotPowerTxt);
// Game objects
var player = game.addChild(new Player());
player.x = 1024;
player.y = 2200;
var ball = game.addChild(new SoccerBall());
ball.x = 1024;
ball.y = 1366;
// Player's goal (bottom)
var playerGoal = game.addChild(new Goal());
playerGoal.x = 1024;
playerGoal.y = 2600;
// Opponent's goal (top)
var opponentGoal = game.addChild(new Goal());
opponentGoal.x = 1024;
opponentGoal.y = 300;
// Create opponents with specific roles
// Defense player (opponent)
var defensePlayer = game.addChild(new DefensePlayer());
defensePlayer.x = 1024;
defensePlayer.y = 600;
defensePlayer.homeX = 1024;
defensePlayer.homeY = 600;
opponents.push(defensePlayer);
// Chigiri - player team hybrid defender
var chigiri = game.addChild(new ChigiriPlayer(0x3399ff));
chigiri.x = 800;
chigiri.y = 2000;
chigiri.homeX = 800;
chigiri.homeY = 2000;
// Hiori - player team creative midfielder
var hiori = game.addChild(new HioriPlayer(0x6699ff));
hiori.x = 1200;
hiori.y = 2000;
hiori.homeX = 1200;
hiori.homeY = 2000;
// Reo - player team adaptive defender/attacker
var reo = game.addChild(new ReoPlayer(0x000000));
reo.x = 600;
reo.y = 1800;
reo.homeX = 600;
reo.homeY = 1800;
// Nagi - player team striker with Fake Volley ability
var nagi = game.addChild(new NagiPlayer(0x9966ff));
nagi.x = 1400;
nagi.y = 2000;
nagi.homeX = 1400;
nagi.homeY = 2000;
// Nagi starts without Flow
// Meguru Bachira - player team creative winger with Roulette and Monster abilities
var bachira = game.addChild(new MeguruBachiraPlayer(0x20B2AA));
bachira.x = 1000;
bachira.y = 1900;
bachira.homeX = 1000;
bachira.homeY = 1900;
// Midfield player
var midfieldPlayer = game.addChild(new MidfieldPlayer());
midfieldPlayer.x = 1024;
midfieldPlayer.y = 1000;
midfieldPlayer.homeX = 1024;
midfieldPlayer.homeY = 1000;
opponents.push(midfieldPlayer);
// (Removed ForwardPlayer - Nagi will be the forward/striker)
// Sae - opponent team attacking midfielder with Perfect Pass and special abilities
var sae = game.addChild(new SaePlayer(0xff6600));
sae.x = 1024;
sae.y = 800;
sae.homeX = 1024;
sae.homeY = 800;
opponents.push(sae);
// Create goalkeepers
var playerGoalkeeper = game.addChild(new Goalkeeper(true)); // Blue goalkeeper for player team
playerGoalkeeper.x = 1024;
playerGoalkeeper.y = 2500; // In front of player's goal
playerGoalkeeper.originalX = 1024;
playerGoalkeeper.originalY = 2500;
var opponentGoalkeeper = game.addChild(new Goalkeeper(false)); // Red goalkeeper for opponent team
opponentGoalkeeper.x = 1024;
opponentGoalkeeper.y = 400; // In front of opponent's goal
opponentGoalkeeper.originalX = 1024;
opponentGoalkeeper.originalY = 400;
// Ball kicking mechanics
function kickBall(targetX, targetY, power) {
var dx = targetX - ball.x;
var dy = targetY - ball.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var finalPower = power || Math.min(distance / 100, 15);
// Properly detach ball from player first
player.hasBall = false;
ball.active = true;
// Set cooldown to prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
// Apply velocity after detachment
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
}
// Touch controls for player movement and ball kicking
game.down = function (x, y, obj) {
// Always update last touch position for shot direction
lastTouchX = x;
lastTouchY = y;
var playerDistance = Math.sqrt((x - player.x) * (x - player.x) + (y - player.y) * (y - player.y));
var ballDistance = Math.sqrt((x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y));
if (playerDistance < ballDistance) {
// Move player
dragNode = player;
} else {
// Start charging shot if player has ball
if (player.hasBall) {
shotChargeStart = Date.now();
isChargingShot = true;
shotPower = 0;
}
}
};
game.move = function (x, y, obj) {
// Always update last touch position for shot direction
lastTouchX = x;
lastTouchY = y;
if (dragNode && currentStamina > 0) {
var dx = x - dragNode.x;
var dy = y - dragNode.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Slow down movement and consume stamina - even slower when having ball
var moveSpeed = player.hasBall ? 0.15 : 0.2;
dragNode.x += dx * moveSpeed;
dragNode.y += dy * moveSpeed;
// Consume stamina based on movement
if (distance > 5) {
currentStamina -= staminaDrainRate;
if (currentStamina < 0) currentStamina = 0;
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
// Handle shot release
if (isChargingShot && player.hasBall) {
var chargeTime = Date.now() - shotChargeStart;
if (chargeTime >= minChargeTime) {
// Release charged shot - use last touch position for direction
kickBall(lastTouchX, lastTouchY, shotPower);
} else {
// Not charged enough, no shot
}
isChargingShot = false;
shotPower = 0;
}
};
// Goal scoring function
function scoreGoal(isPlayer) {
if (isPlayer) {
playerScore++;
} else {
opponentScore++;
}
scoreTxt.setText('Player: ' + playerScore + ' - Opponent: ' + opponentScore);
LK.getSound('goal').play();
// Reset ball position
ball.x = 1024;
ball.y = 1366;
ball.velocityX = 0;
ball.velocityY = 0;
}
// Formation and passing coordinator for strategic team play
var ballCarrier = null; // Global ball carrier tracking
var formationCoordinator = {
currentFormation: 'attacking',
ballCarrier: null,
passingOptions: [],
wingPlayers: [],
supportPlayers: [],
anticipationMap: {},
formation: {
attacking: {
chigiri: {
x: 800,
y: 1200,
role: 'wing'
},
hiori: {
x: 1024,
y: 1600,
role: 'playmaker'
},
reo: {
x: 1400,
y: 1400,
role: 'support'
},
nagi: {
x: 1024,
y: 900,
role: 'striker'
},
bachira: {
x: 800,
y: 1000,
role: 'wing'
}
},
defensive: {
chigiri: {
x: 600,
y: 1800,
role: 'defense'
},
hiori: {
x: 1024,
y: 1700,
role: 'playmaker'
},
reo: {
x: 1400,
y: 1800,
role: 'defense'
},
nagi: {
x: 1024,
y: 1200,
role: 'striker'
},
bachira: {
x: 1000,
y: 1500,
role: 'support'
}
}
},
updateFormation: function updateFormation() {
// Switch formation based on ball position and game state
var ballY = ball.y;
var shouldAttack = ballY < 1500 || ballCarrier && ballCarrier.role === 'striker';
this.currentFormation = shouldAttack ? 'attacking' : 'defensive';
// Update each player's tactical position
var teammates = [chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < teammates.length; i++) {
if (teammates[i]) {
var formationData = this.formation[this.currentFormation][teammates[i].role];
if (formationData) {
// Dynamic Y: If ball is in midfield, keep some players in midfield, others on wings/defense
if (ballY > 1000 && ballY < 2000) {
// Ball in midfield: wings stay wide, playmaker stays central, striker stays high, support/defense drop back
if (formationData.role === 'wing') {
teammates[i].tacticalX = teammates[i].x < 1024 ? 400 : 1648;
teammates[i].tacticalY = 1400;
} else if (formationData.role === 'playmaker') {
teammates[i].tacticalX = 1024;
teammates[i].tacticalY = 1600;
} else if (formationData.role === 'striker') {
teammates[i].tacticalX = 1024;
teammates[i].tacticalY = 900;
} else if (formationData.role === 'support') {
teammates[i].tacticalX = 1024;
teammates[i].tacticalY = 1800;
} else if (formationData.role === 'defense') {
teammates[i].tacticalX = 1024;
teammates[i].tacticalY = 2000;
}
teammates[i].tacticalRole = formationData.role;
} else {
// Use default formation
teammates[i].tacticalX = formationData.x;
teammates[i].tacticalY = formationData.y;
teammates[i].tacticalRole = formationData.role;
}
}
}
}
// Anticipation: Predict next ball position and update anticipationMap
var anticipationSteps = 20;
var predX = ball.x,
predY = ball.y,
predVX = ball.velocityX,
predVY = ball.velocityY;
for (var step = 0; step < anticipationSteps; step++) {
predX += predVX;
predY += predVY;
predVX *= 0.98;
predVY *= 0.98;
// Clamp to field
if (predX < 30) predX = 30;
if (predX > 2018) predX = 2018;
if (predY < 30) predY = 30;
if (predY > 2702) predY = 2702;
}
this.anticipationMap = {
x: predX,
y: predY
};
},
findPassingOptions: function findPassingOptions(ballHolder) {
this.passingOptions = [];
this.wingPlayers = [];
this.supportPlayers = [];
var teammates = [chigiri, hiori, reo, nagi, bachira, player];
for (var i = 0; i < teammates.length; i++) {
if (teammates[i] && teammates[i] !== ballHolder) {
var dist = Math.sqrt((teammates[i].x - ballHolder.x) * (teammates[i].x - ballHolder.x) + (teammates[i].y - ballHolder.y) * (teammates[i].y - ballHolder.y));
if (dist > 150 && dist < 700) {
// allow longer passes for tactics
var passQuality = this.calculatePassQuality(ballHolder, teammates[i]);
// Anticipate if teammate will be open in next 20 frames
var anticipationBonus = 0;
if (this.anticipationMap) {
var antDist = Math.sqrt((teammates[i].x - this.anticipationMap.x) * (teammates[i].x - this.anticipationMap.x) + (teammates[i].y - this.anticipationMap.y) * (teammates[i].y - this.anticipationMap.y));
if (antDist < 200) anticipationBonus += 20;
}
this.passingOptions.push({
player: teammates[i],
distance: dist,
quality: passQuality + anticipationBonus,
angle: Math.atan2(teammates[i].y - ballHolder.y, teammates[i].x - ballHolder.x)
});
// Categorize players for wing play
if (teammates[i].tacticalRole === 'wing' || teammates[i].role === 'chigiri' || teammates[i].role === 'bachira') {
this.wingPlayers.push(teammates[i]);
} else {
this.supportPlayers.push(teammates[i]);
}
}
}
}
// Sort by pass quality
this.passingOptions.sort(function (a, b) {
return b.quality - a.quality;
});
},
calculatePassQuality: function calculatePassQuality(from, to) {
var baseQuality = 100;
// Distance factor (closer = better, but not too close)
var dist = Math.sqrt((to.x - from.x) * (to.x - from.x) + (to.y - from.y) * (to.y - from.y));
if (dist < 200) baseQuality -= 30;
if (dist > 400) baseQuality -= 20;
// Forward progress factor
var forwardProgress = from.y - to.y; // Moving toward opponent goal
if (forwardProgress > 0) baseQuality += forwardProgress * 0.1;
// Wing position bonus
if (to.x < 600 || to.x > 1400) baseQuality += 25;
// Striker position bonus
if (to.role === 'nagi' && to.y < 1000) baseQuality += 40;
// Check for opponent interference
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var oppDist = Math.sqrt((opp.x - to.x) * (opp.x - to.x) + (opp.y - to.y) * (opp.y - to.y));
if (oppDist < 120) baseQuality -= 35;
}
// Bonus for being in anticipated open space
if (this.anticipationMap) {
var antDist = Math.sqrt((to.x - this.anticipationMap.x) * (to.x - this.anticipationMap.x) + (to.y - this.anticipationMap.y) * (to.y - this.anticipationMap.y));
if (antDist < 150) baseQuality += 15;
}
return Math.max(baseQuality, 10);
},
executeStrategicPass: function executeStrategicPass(ballHolder) {
this.findPassingOptions(ballHolder);
if (this.passingOptions.length === 0) return false;
var chosenPass = null;
// Team tactics: If a teammate is running into open space, prefer that pass
for (var i = 0; i < this.passingOptions.length; i++) {
var opt = this.passingOptions[i];
if (this.anticipationMap) {
var antDist = Math.sqrt((opt.player.x - this.anticipationMap.x) * (opt.player.x - this.anticipationMap.x) + (opt.player.y - this.anticipationMap.y) * (opt.player.y - this.anticipationMap.y));
if (antDist < 120 && opt.quality > 60) {
chosenPass = opt;
break;
}
}
}
// Strategy selection based on field position and formation
if (!chosenPass) {
if (ballHolder.y > 1800) {
// Defensive third - prioritize safe passes or wing play
chosenPass = this.selectSafePass() || this.selectWingPass();
} else if (ballHolder.y > 1200) {
// Middle third - mix of progressive and wing passes
var strategy = Math.random();
if (strategy < 0.4) {
chosenPass = this.selectProgressivePass();
} else if (strategy < 0.7) {
chosenPass = this.selectWingPass();
} else {
chosenPass = this.selectSafePass();
}
} else {
// Attacking third - prioritize progressive and through passes
chosenPass = this.selectProgressivePass() || this.selectThroughPass();
}
}
if (chosenPass) {
this.performPass(ballHolder, chosenPass);
// Team tactics: If pass is to wing, have another support player run into box
if (chosenPass.player.tacticalRole === 'wing') {
var teammates = [chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < teammates.length; i++) {
if (teammates[i] && teammates[i] !== chosenPass.player && teammates[i].tacticalRole !== 'wing') {
// Move support player into box for cross
teammates[i].tacticalX = 1024 + (Math.random() - 0.5) * 200;
teammates[i].tacticalY = 600 + Math.random() * 200;
}
}
}
return true;
}
return false;
},
selectSafePass: function selectSafePass() {
var safeOptions = this.passingOptions.filter(function (option) {
return option.quality > 70 && option.distance < 350;
});
return safeOptions.length > 0 ? safeOptions[0] : null;
},
selectWingPass: function selectWingPass() {
var wingOptions = this.passingOptions.filter(function (option) {
return (option.player.x < 600 || option.player.x > 1400) && option.quality > 50;
});
return wingOptions.length > 0 ? wingOptions[0] : null;
},
selectProgressivePass: function selectProgressivePass() {
var progressiveOptions = this.passingOptions.filter(function (option) {
return option.player.y < ball.y - 100 && option.quality > 60;
});
return progressiveOptions.length > 0 ? progressiveOptions[0] : null;
},
selectThroughPass: function selectThroughPass() {
var throughOptions = this.passingOptions.filter(function (option) {
return option.player.y < 1000 && option.quality > 40;
});
return throughOptions.length > 0 ? throughOptions[0] : null;
},
performPass: function performPass(from, passOption) {
var target = passOption.player;
var passPower = Math.min(passOption.distance / 40, 15);
// Add some curve for style
var curve = (Math.random() - 0.5) * 2;
var dx = target.x - ball.x + curve;
var dy = target.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
// Anticipate teammate's run: lead pass if moving
var leadX = target.x,
leadY = target.y;
if (typeof target.lastX !== "undefined" && typeof target.lastY !== "undefined") {
var vx = target.x - target.lastX;
var vy = target.y - target.lastY;
leadX += vx * 10;
leadY += vy * 10;
dx = leadX - ball.x + curve;
dy = leadY - ball.y;
dist = Math.sqrt(dx * dx + dy * dy);
}
ball.velocityX = dx / dist * passPower;
ball.velocityY = dy / dist * passPower;
ball.active = true;
// Visual effect for strategic pass
tween(ball, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00FF88
}, {
duration: 300,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
}
};
// Ball pursuit coordination system
var ballPursuitCoordinator = {
activePursuer: null,
pursuitRange: 150,
supportPositions: {
wing: {
x: 0,
y: 0
},
defense: {
x: 0,
y: 0
},
support: {
x: 0,
y: 0
}
},
updatePursuit: function updatePursuit() {
var teammates = [chigiri, hiori, reo, nagi, bachira];
var ballToTeammateDistances = [];
// Update formation first
formationCoordinator.updateFormation();
// Calculate distances and find closest available teammate
for (var i = 0; i < teammates.length; i++) {
if (teammates[i]) {
var dist = Math.sqrt((teammates[i].x - ball.x) * (teammates[i].x - ball.x) + (teammates[i].y - ball.y) * (teammates[i].y - ball.y));
ballToTeammateDistances.push({
player: teammates[i],
distance: dist,
isCurrentPursuer: teammates[i] === this.activePursuer
});
}
}
// Sort by distance
ballToTeammateDistances.sort(function (a, b) {
return a.distance - b.distance;
});
// Assign active pursuer (closest player within pursuit range)
var newPursuer = null;
for (var i = 0; i < ballToTeammateDistances.length; i++) {
var candidate = ballToTeammateDistances[i];
if (candidate.distance < 800) {
// Only pursue if reasonably close
newPursuer = candidate.player;
break;
}
}
// Update active pursuer
this.activePursuer = newPursuer;
// Calculate support positions
this.calculateSupportPositions();
// Assign roles to non-pursuing players
for (var i = 0; i < teammates.length; i++) {
if (teammates[i] && teammates[i] !== this.activePursuer) {
teammates[i].coordinatedRole = this.assignSupportRole(teammates[i]);
} else if (teammates[i] === this.activePursuer) {
teammates[i].coordinatedRole = 'pursuer';
}
}
},
calculateSupportPositions: function calculateSupportPositions() {
// Wing position - stay wide for passes
this.supportPositions.wing.x = ball.x < 1024 ? ball.x + 400 : ball.x - 400;
this.supportPositions.wing.y = ball.y + 100;
// Defense position - stay back for protection
this.supportPositions.defense.x = ball.x + (1024 - ball.x) * 0.3;
this.supportPositions.defense.y = Math.max(ball.y + 300, 1800);
// Support position - intermediate position for backup
this.supportPositions.support.x = ball.x + (Math.random() - 0.5) * 200;
this.supportPositions.support.y = ball.y + 200;
},
assignSupportRole: function assignSupportRole(player) {
// Assign roles based on player characteristics and position
if (player.role === 'chigiri') {
return Math.random() < 0.7 ? 'wing' : 'defense';
} else if (player.role === 'reo') {
return player.isDefensiveMode ? 'defense' : 'support';
} else if (player.role === 'hiori') {
return 'support';
} else if (player.role === 'nagi') {
return 'striker';
} else if (player.role === 'bachira') {
return Math.random() < 0.8 ? 'wing' : 'support';
}
return 'support';
},
getSupportPosition: function getSupportPosition(role) {
return this.supportPositions[role] || this.supportPositions.support;
}
};
// Special dribbling system for ball carriers every 5 seconds
var specialDribbleSystem = {
lastActivation: 0,
activationInterval: 5000,
// 5 seconds
activePlayers: {},
initializePlayer: function initializePlayer(player) {
if (!this.activePlayers[player.role]) {
this.activePlayers[player.role] = {
player: player,
lastDribble: 0,
isPerformingDribble: false,
dribbleType: this.getDribbleType(player.role),
dribbleStartTime: 0,
dribbleDuration: 1500,
effectApplied: false
};
}
},
getDribbleType: function getDribbleType(role) {
switch (role) {
case 'chigiri':
return 'lightning_dash';
// Stun effect
case 'hiori':
return 'winter_wave';
// Slow effect
case 'reo':
return 'chameleon_shift';
// Stun effect
case 'nagi':
return 'trap_control';
// Slow effect
case 'bachira':
return 'monster_devour';
// Devoured effect
case 'sae':
return 'crescent_slice';
// Devoured effect
case 'defense':
return 'tackle_sweep';
// Stun effect
case 'midfield':
return 'curve_spiral';
// Slow effect
case 'forward':
return 'power_rush';
// Stun effect
default:
return 'basic_dribble';
// Stun effect
}
},
checkActivation: function checkActivation() {
var currentTime = Date.now();
if (currentTime - this.lastActivation >= this.activationInterval) {
this.lastActivation = currentTime;
return true;
}
return false;
},
performSpecialDribble: function performSpecialDribble(player, dribbleData) {
if (dribbleData.isPerformingDribble) return;
dribbleData.isPerformingDribble = true;
dribbleData.dribbleStartTime = Date.now();
dribbleData.effectApplied = false;
dribbleData.lastDribble = Date.now();
// Visual effect based on dribble type
var effectColor = this.getDribbleColor(dribbleData.dribbleType);
tween(player, {
scaleX: 1.3,
scaleY: 1.3,
tint: effectColor
}, {
duration: 200,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
// Apply movement pattern
this.applyDribbleMovement(player, dribbleData);
},
getDribbleColor: function getDribbleColor(dribbleType) {
switch (dribbleType) {
case 'lightning_dash':
return 0xFFFF00;
// Yellow
case 'winter_wave':
return 0x87CEEB;
// Light blue
case 'chameleon_shift':
return 0xFF00FF;
// Magenta
case 'trap_control':
return 0x9966FF;
// Purple
case 'monster_devour':
return 0xFF0000;
// Red
case 'crescent_slice':
return 0xFF6600;
// Orange
case 'tackle_sweep':
return 0x808080;
// Gray
case 'curve_spiral':
return 0x00FF00;
// Green
case 'power_rush':
return 0x0000FF;
// Blue
default:
return 0xFFFFFF;
// White
}
},
applyDribbleMovement: function applyDribbleMovement(player, dribbleData) {
var originalX = player.x;
var originalY = player.y;
switch (dribbleData.dribbleType) {
case 'lightning_dash':
// Quick forward dash
var dashDistance = 120;
var targetX = player.x + (Math.random() - 0.5) * 40;
var targetY = player.y - dashDistance;
tween(player, {
x: targetX,
y: targetY
}, {
duration: 300,
easing: tween.easeOut
});
break;
case 'winter_wave':
// Circular motion creating waves
var radius = 80;
var startAngle = Math.random() * Math.PI * 2;
for (var i = 0; i < 3; i++) {
LK.setTimeout(function (angle) {
return function () {
if (dribbleData.isPerformingDribble) {
var waveX = originalX + Math.cos(angle) * radius;
var waveY = originalY + Math.sin(angle) * radius;
tween(player, {
x: waveX,
y: waveY
}, {
duration: 200
});
}
};
}(startAngle + i * Math.PI / 3), i * 200);
}
break;
case 'chameleon_shift':
// Teleport-like movement
var shiftDistance = 100;
var shiftAngle = Math.random() * Math.PI * 2;
var shiftX = player.x + Math.cos(shiftAngle) * shiftDistance;
var shiftY = player.y + Math.sin(shiftAngle) * shiftDistance;
player.alpha = 0.3;
tween(player, {
x: shiftX,
y: shiftY,
alpha: 1
}, {
duration: 400,
easing: tween.easeInOut
});
break;
case 'trap_control':
// Slow spinning motion
var spinRadius = 60;
for (var i = 0; i < 4; i++) {
LK.setTimeout(function (step) {
return function () {
if (dribbleData.isPerformingDribble) {
var angle = step * Math.PI / 2;
var spinX = originalX + Math.cos(angle) * spinRadius;
var spinY = originalY + Math.sin(angle) * spinRadius;
tween(player, {
x: spinX,
y: spinY
}, {
duration: 300
});
}
};
}(i), i * 300);
}
break;
case 'monster_devour':
// Erratic zigzag movement
var zigzagSteps = 5;
for (var i = 0; i < zigzagSteps; i++) {
LK.setTimeout(function (step) {
return function () {
if (dribbleData.isPerformingDribble) {
var zigX = originalX + (step % 2 === 0 ? -60 : 60);
var zigY = originalY - step * 20;
tween(player, {
x: zigX,
y: zigY
}, {
duration: 150
});
}
};
}(i), i * 150);
}
break;
case 'crescent_slice':
// Crescent-shaped movement
var crescentRadius = 90;
for (var i = 0; i < 4; i++) {
LK.setTimeout(function (step) {
return function () {
if (dribbleData.isPerformingDribble) {
var angle = -Math.PI / 2 + step * Math.PI / 3;
var crescentX = originalX + Math.cos(angle) * crescentRadius;
var crescentY = originalY + Math.sin(angle) * crescentRadius;
tween(player, {
x: crescentX,
y: crescentY
}, {
duration: 250
});
}
};
}(i), i * 250);
}
break;
default:
// Basic dribble - simple side step
var stepX = player.x + (Math.random() - 0.5) * 80;
tween(player, {
x: stepX
}, {
duration: 500
});
break;
}
// Schedule effect application
LK.setTimeout(function () {
if (dribbleData.isPerformingDribble && !dribbleData.effectApplied) {
specialDribbleSystem.applyDribbleEffect(player, dribbleData);
dribbleData.effectApplied = true;
}
}, 700);
// End dribble
LK.setTimeout(function () {
dribbleData.isPerformingDribble = false;
}, dribbleData.dribbleDuration);
},
applyDribbleEffect: function applyDribbleEffect(player, dribbleData) {
var effectRadius = 150;
var affectedOpponents = [];
// Find opponents in range
if (player.role === 'sae' || player.role === 'defense' || player.role === 'midfield' || player.role === 'forward') {
// Opponent affecting player team
var playerTeam = [player, chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < playerTeam.length; i++) {
if (playerTeam[i] && playerTeam[i] !== player) {
var dist = Math.sqrt((playerTeam[i].x - player.x) * (playerTeam[i].x - player.x) + (playerTeam[i].y - player.y) * (playerTeam[i].y - player.y));
if (dist <= effectRadius) {
affectedOpponents.push(playerTeam[i]);
}
}
}
} else {
// Player team affecting opponents
for (var i = 0; i < opponents.length; i++) {
if (opponents[i]) {
var dist = Math.sqrt((opponents[i].x - player.x) * (opponents[i].x - player.x) + (opponents[i].y - player.y) * (opponents[i].y - player.y));
if (dist <= effectRadius) {
affectedOpponents.push(opponents[i]);
}
}
}
}
// Apply effects based on dribble type
for (var i = 0; i < affectedOpponents.length; i++) {
var opponent = affectedOpponents[i];
this.applyEffectToOpponent(opponent, dribbleData.dribbleType);
}
},
applyEffectToOpponent: function applyEffectToOpponent(opponent, dribbleType) {
// Stun effects
if (dribbleType === 'lightning_dash' || dribbleType === 'chameleon_shift' || dribbleType === 'tackle_sweep' || dribbleType === 'power_rush') {
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
tween(opponent, {
tint: 0xFFFF00,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300
});
LK.setTimeout(function () {
opponent.stunned = false;
opponent.speed = opponent.originalSpeed || 3;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}, 2000);
}
// Slow effects
else if (dribbleType === 'winter_wave' || dribbleType === 'trap_control' || dribbleType === 'curve_spiral') {
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * 0.3;
opponent.slowed = true;
tween(opponent, {
tint: 0x87CEEB,
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 300
});
LK.setTimeout(function () {
opponent.slowed = false;
opponent.speed = opponent.originalSpeed || 3;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}, 3000);
}
// Devoured effects
else if (dribbleType === 'monster_devour' || dribbleType === 'crescent_slice') {
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * 0.2;
opponent.devoured = true;
tween(opponent, {
tint: 0x800080,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300
});
LK.setTimeout(function () {
opponent.devoured = false;
opponent.speed = opponent.originalSpeed || 3;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}, 4000);
}
},
update: function update() {
if (!this.checkActivation()) return;
// Check all ball carriers and activate special dribbles
var allPlayers = [player, chigiri, hiori, reo, nagi, bachira].concat(opponents);
for (var i = 0; i < allPlayers.length; i++) {
var currentPlayer = allPlayers[i];
if (currentPlayer && currentPlayer.hasBall) {
this.initializePlayer(currentPlayer);
var dribbleData = this.activePlayers[currentPlayer.role];
if (dribbleData && !dribbleData.isPerformingDribble) {
this.performSpecialDribble(currentPlayer, dribbleData);
}
}
}
}
};
// Game update loop
game.update = function () {
// Update special dribbling system
specialDribbleSystem.update();
// Update ball carrier tracking
ballCarrier = null;
if (player.hasBall) {
ballCarrier = player;
} else {
var teammates = [chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < teammates.length; i++) {
if (teammates[i] && teammates[i].hasBall) {
ballCarrier = teammates[i];
break;
}
}
if (!ballCarrier) {
for (var i = 0; i < opponents.length; i++) {
if (opponents[i] && opponents[i].hasBall) {
ballCarrier = opponents[i];
break;
}
}
}
}
// Update ball pursuit coordination
ballPursuitCoordinator.updatePursuit();
// Update ball detach cooldown
if (ballDetachCooldown > 0) {
ballDetachCooldown -= 16; // Approximately 60 FPS
if (ballDetachCooldown < 0) ballDetachCooldown = 0;
}
// Check if player is close to ball for interaction (only if cooldown expired and ball is not untouchable)
var playerToBallDistance = Math.sqrt((player.x - ball.x) * (player.x - ball.x) + (player.y - ball.y) * (player.y - ball.y));
if (playerToBallDistance < 80 && !player.hasBall && ballDetachCooldown <= 0 && !ball.untouchable) {
player.hasBall = true;
ball.active = false;
ball.velocityX = 0;
ball.velocityY = 0;
}
// If player has ball, make it stick to player
if (player.hasBall && ball.active === false) {
ball.x = player.x;
ball.y = player.y - 50;
}
// Stamina regeneration when not moving
var playerMoving = Math.abs(player.x - lastPlayerX) > 1 || Math.abs(player.y - lastPlayerY) > 1;
if (!playerMoving && currentStamina < maxStamina) {
currentStamina += staminaRegenRate;
if (currentStamina > maxStamina) currentStamina = maxStamina;
}
// Update last position
lastPlayerX = player.x;
lastPlayerY = player.y;
// Update stamina UI
var staminaPercent = Math.round(currentStamina / maxStamina * 100);
staminaTxt.setText('Stamina: ' + staminaPercent);
// Change color based on stamina level
if (staminaPercent > 50) {
staminaTxt.fill = 0x00FF00;
} else if (staminaPercent > 25) {
staminaTxt.fill = 0xFFFF00;
} else {
staminaTxt.fill = 0xFF0000;
}
// Update shot charging
if (isChargingShot && player.hasBall) {
var chargeTime = Date.now() - shotChargeStart;
if (chargeTime >= minChargeTime) {
// Calculate shot power based on charge time
var chargeProgress = Math.min((chargeTime - minChargeTime) / 1000, 1); // 1 second after minimum for max power
shotPower = 5 + chargeProgress * maxShotPower; // Minimum 5, maximum 25 power
var powerPercent = Math.round(chargeProgress * 100);
shotPowerTxt.setText('Shot Power: ' + powerPercent + '%');
shotPowerTxt.fill = 0x00FF00;
// Automatically kick ball forward when shot power reaches 100%
if (powerPercent >= 100) {
shotPower = 5 + maxShotPower; // Keep at maximum power
shotPowerTxt.setText('Shot Power: 100%');
shotPowerTxt.fill = 0x00FF00;
// Auto-kick ball forward when power reaches 100%
var forwardX = player.x;
var forwardY = player.y - 300; // Kick forward (up the field)
kickBall(forwardX, forwardY, shotPower);
isChargingShot = false;
shotPower = 0;
}
} else {
// Not charged enough yet
var timeLeft = Math.ceil((minChargeTime - chargeTime) / 1000);
shotPowerTxt.setText('Hold for ' + timeLeft + 's');
shotPowerTxt.fill = 0xFFFF00;
}
} else {
shotPowerTxt.setText('Shot Power: 0%');
shotPowerTxt.fill = 0xFFFFFF;
}
// Check player goal scoring (opponent scores)
if (ball.y > playerGoal.y - 50 && ball.x > playerGoal.x - 200 && ball.x < playerGoal.x + 200) {
scoreGoal(false);
LK.effects.flashObject(playerGoal, 0xFF0000, 500);
}
// Check opponent goal scoring (player scores)
if (ball.y < opponentGoal.y + 50 && ball.x > opponentGoal.x - 200 && ball.x < opponentGoal.x + 200) {
scoreGoal(true);
LK.effects.flashObject(opponentGoal, 0x00FF00, 500);
}
// Check win condition
if (playerScore >= 5) {
LK.showYouWin();
}
if (opponentScore >= 5) {
LK.showGameOver();
}
// Enhanced AI positioning based on ball location
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
// Defensive/attacking stabilization: ensure all AI are always moving and using abilities
if (typeof opponent.stunned !== "undefined" && opponent.stunned) {
// If stunned, skip movement for this frame
continue;
}
// If opponent is out of bounds, bring them back in
if (opponent.x < 30) opponent.x = 30;
if (opponent.x > 2018) opponent.x = 2018;
if (opponent.y < 30) opponent.y = 30;
if (opponent.y > 2702) opponent.y = 2702;
// If opponent is not moving, nudge toward home position
if (typeof opponent.lastX === "undefined") opponent.lastX = opponent.x;
if (typeof opponent.lastY === "undefined") opponent.lastY = opponent.y;
var moved = Math.abs(opponent.x - opponent.lastX) > 1 || Math.abs(opponent.y - opponent.lastY) > 1;
if (!moved) {
// Nudge toward home position if stuck
var homeDx = (opponent.homeX || 1024) - opponent.x;
var homeDy = (opponent.homeY || 1366) - opponent.y;
var homeDist = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDist > 1) {
opponent.x += homeDx / homeDist * (opponent.speed || 2.5) * 0.5;
opponent.y += homeDy / homeDist * (opponent.speed || 2.5) * 0.5;
}
}
opponent.lastX = opponent.x;
opponent.lastY = opponent.y;
// If opponent has a special ability/cooldown, ensure it is ticking down
if (typeof opponent.update === "function") {
// Call their update to ensure abilities and movement are handled
opponent.update();
}
}
// Stabilize player team as well (chigiri, hiori, reo, nagi, bachira)
var teammates = [chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < teammates.length; i++) {
var mate = teammates[i];
if (!mate) continue;
if (typeof mate.stunned !== "undefined" && mate.stunned) continue;
if (mate.x < 30) mate.x = 30;
if (mate.x > 2018) mate.x = 2018;
if (mate.y < 30) mate.y = 30;
if (mate.y > 2702) mate.y = 2702;
if (typeof mate.lastX === "undefined") mate.lastX = mate.x;
if (typeof mate.lastY === "undefined") mate.lastY = mate.y;
var moved = Math.abs(mate.x - mate.lastX) > 1 || Math.abs(mate.y - mate.lastY) > 1;
if (!moved) {
var homeDx = (mate.homeX || 1024) - mate.x;
var homeDy = (mate.homeY || 1366) - mate.y;
var homeDist = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDist > 1) {
mate.x += homeDx / homeDist * (mate.speed || 3) * 0.5;
mate.y += homeDy / homeDist * (mate.speed || 3) * 0.5;
}
}
mate.lastX = mate.x;
mate.lastY = mate.y;
if (typeof mate.update === "function") {
mate.update();
}
}
// Ensure all characters are always actively playing, using abilities, moving, defending, attacking, and helping recover the ball
// All role-based AI and abilities are handled in each player's update method
};