User prompt
Nagi Rework yap Revolver Fake Volley 2 Stage Fake Volley falan tekrar yap
User prompt
Nagi top etrafındayken sadece çalabilsin
User prompt
kaleye yakınlarsa biz dokunarak topu atmaları gereken yeri işaretleyelim Player takımı için !
User prompt
NERF NAGİ
User prompt
forward Opponent e Isagi yi koy Metavision ve Direct Shot bide Fade away diye özel bir Dribbling i olsun Player kalesine gitmeye çalışırken önüne biri gelirse onun üzerine dash atarak onu stunlasın ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Metavizyon Kaiser e hız versin ve GOL pozisyonunu görüp (yani mavi bir kareye gidecek) oradan Kaiser İmpact vuracak topu çalamayız Metavizyon dayken Kaiser den ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Kaiser e Metavision ekle !
User prompt
Kaiser e Metavision ekle !
User prompt
Gagamaru Kesin olarak her özelliği her şeyi etkilenmeden tutabilsin bide Kaiser Impact i Kaiser her yerden bizim kaleye kullanabilsin ve Kaiser Impact i kullanacağı zaman kaiser topu istediği yerden kendine alabilsin
User prompt
Aiku Dribbling yapanlardan şut çekenlerden 15 Saniye Cooldown la Topu alıp 5 saniye dokunulmazlık kazanıp Kaiser e verebiliyor olsun
User prompt
Kaiser e Emperor Dribble ekle Aiku ya Snake Defence ekle bizim Kaleye Gagamaru ekle
User prompt
Reo Nagi ye bir kere sadece Ego aşılasın sonra sadece Revolver Volley atsın 1. 2. 3. 4. 5. diye ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Nagi 1 kere ''EGO IGNITED!'' olsun ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Reo 1 kere Ego ıgnete yapsın sonra Nagi de
User prompt
Nagi rkip kalenin pozisyonunu görüp 5. Aşamaya geldiğinde kaleye doğru nişan alsın
User prompt
bide neden Yıkıcı şutta Nagi cooldown a giriyor ? düzelt
User prompt
Nagi nin Geri sayımını düzelt Fake Vole ler yani 4 tane attığı Fake Vole ler topuatacak gibi yapıp rakipten geri çekmesi olacak 5 e geldimi Durdurulamaz derecede hızlı bir şut atmalı ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Nagi nin Geri sayımını düzelt Fake Vole ler yani 4 tane attığı Fake Vole ler topuatacak gibi yapıp rakipten geri çekmesi olacak 5 e geldimi Durdurulamaz derecede hızlı bir şut atmalı tüm buglı yanlarını düzelt
User prompt
Barou 5 tane Chop dribble atsın ve orta sahaya geldimi Yıkıcı Şut vursun Player kalesine ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
tüm oyunculara Dribbling i olmayanlara Dribbling şut özelliği olmayana şut özelliği ver !
User prompt
her Opponent ve her bizim takımdan oyuncu için ayrı asset dosyaları oluştur ve hepsine farklı renkler ver ortalarına Player takımındansa Mavi Opponent ten ise kırmızı rengi koy
User prompt
Rakip takımdaki her oyuncu ya renkler ata ama ortalarına Kırmızı Rengini koy
User prompt
Please fix the bug: 'TypeError: Cannot use 'in' operator to search for 'tint' in undefined' in or related to this line: 'tween(playerTeam[i], {' Line Number: 371
User prompt
herkez Stun yemesin sadece Nagi nin önündeki kişi Stun yesin Fake Vole den ve ekranda 1. den başalyıp 5. e kadar say lütfen
User prompt
Nagi Gol atabileceği Kalenin yakınında bir yere gitsin her zaman
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var AikuPlayer = 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 = 4.2; self.role = 'aiku'; self.homeX = 1024; self.homeY = 500; self.hasBall = false; // Metavision abilities self.metavisionActive = false; self.metavisionCooldown = 0; self.metavisionCooldownTime = 15000; // 15 seconds self.metavisionDuration = 5000; // 5 seconds active self.metavisionEndTime = 0; self.interceptedPasses = 0; // Defensive wall ability self.defensiveWallActive = false; self.defensiveWallCooldown = 0; self.defensiveWallCooldownTime = 20000; // 20 seconds self.defensiveWallDuration = 8000; // 8 seconds self.defensiveWallEndTime = 0; self.defensiveWallRadius = 200; self.defensiveWallVisual = null; self.update = function () { // Update metavision cooldown if (self.metavisionCooldown > 0) { self.metavisionCooldown -= 16; if (self.metavisionCooldown < 0) self.metavisionCooldown = 0; } // Update defensive wall cooldown if (self.defensiveWallCooldown > 0) { self.defensiveWallCooldown -= 16; if (self.defensiveWallCooldown < 0) self.defensiveWallCooldown = 0; } // Handle metavision end if (self.metavisionActive && Date.now() > self.metavisionEndTime) { self.metavisionActive = false; } // Handle defensive wall end if (self.defensiveWallActive && Date.now() > self.defensiveWallEndTime) { self.defensiveWallActive = false; if (self.defensiveWallVisual) { self.defensiveWallVisual.destroy(); self.defensiveWallVisual = null; } } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Activate metavision when enemy players approach var enemyNearby = false; var playerTeam = [player, chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (enemyDist < 300) { enemyNearby = true; break; } } if (enemyNearby && !self.metavisionActive && self.metavisionCooldown <= 0) { self.metavisionActive = true; self.metavisionCooldown = self.metavisionCooldownTime; self.metavisionEndTime = Date.now() + self.metavisionDuration; // Visual effect for metavision tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0x00FFFF }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } // Intercept passes with metavision if (self.metavisionActive && ball.active && distance < 150) { // High chance to intercept the ball if (Math.random() < 0.8) { // 80% chance with metavision ball.velocityX = 0; ball.velocityY = 0; ball.x = self.x; ball.y = self.y; ball.active = false; self.hasBall = true; self.interceptedPasses++; // Clear ball from all players for (var i = 0; i < playerTeam.length; i++) { if (playerTeam[i] && typeof playerTeam[i].hasBall !== "undefined") { playerTeam[i].hasBall = false; } } } } // Activate defensive wall when multiple enemies nearby var nearbyEnemies = 0; for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (enemyDist < self.defensiveWallRadius) { nearbyEnemies++; } } if (nearbyEnemies >= 2 && !self.defensiveWallActive && self.defensiveWallCooldown <= 0) { self.defensiveWallActive = true; self.defensiveWallCooldown = self.defensiveWallCooldownTime; self.defensiveWallEndTime = Date.now() + self.defensiveWallDuration; // Create defensive wall visual self.defensiveWallVisual = self.addChild(LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 4, scaleY: 4, tint: 0xFF4500 })); self.defensiveWallVisual.alpha = 0.4; // Slow down nearby enemies for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (enemyDist < self.defensiveWallRadius) { if (!playerTeam[i].originalSpeed) playerTeam[i].originalSpeed = playerTeam[i].speed; playerTeam[i].speed = playerTeam[i].speed * 0.4; // 40% speed } } } // Apply defensive wall effect if (self.defensiveWallActive) { for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (enemyDist < self.defensiveWallRadius) { if (!playerTeam[i].originalSpeed) playerTeam[i].originalSpeed = playerTeam[i].speed; playerTeam[i].speed = playerTeam[i].speed * 0.4; // Keep at 40% speed } else if (playerTeam[i].originalSpeed) { playerTeam[i].speed = playerTeam[i].originalSpeed; playerTeam[i].originalSpeed = null; } } } else { // Restore speeds when defensive wall is not active for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i] || !playerTeam[i].originalSpeed) continue; playerTeam[i].speed = playerTeam[i].originalSpeed; playerTeam[i].originalSpeed = null; } } // Normal defensive behavior if (distance < 400) { var chaseSpeed = self.metavisionActive ? self.speed * 1.5 : self.speed * 1.1; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; if (distance < 80) { var clearX = ball.x < 1024 ? ball.x - 400 : ball.x + 400; var clearY = ball.y - 300; 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 * 12; ball.velocityY = clearDy / clearDist * 12; } } } 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; } } }; // Add shootBall method self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; return self; }); var BarouPlayer = 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 = 'barou'; self.homeX = 1024; self.homeY = 1200; self.hasBall = false; // Predator Eye abilities self.predatorEyeActive = false; self.predatorEyeCooldown = 0; self.predatorEyeCooldownTime = 12000; // 12 seconds self.predatorEyeDuration = 6000; // 6 seconds active self.predatorEyeEndTime = 0; self.predatorEyeTargets = []; // Feint abilities self.feintCooldown = 0; self.feintCooldownTime = 8000; // 8 seconds self.feintActive = false; self.feintStunRadius = 120; self.feintStunDuration = 2500; // 2.5 seconds self.feintStunnedOpponents = []; self.feintStunEnd = 0; // King Shot variables self.kingShotCooldown = 0; self.kingShotCooldownTime = 15000; // 15 seconds self.kingShotCharging = false; self.kingShotChargeTime = 0; self.kingShotMaxCharge = 2000; // 2 seconds charge self.update = function () { // Update cooldowns if (self.predatorEyeCooldown > 0) { self.predatorEyeCooldown -= 16; if (self.predatorEyeCooldown < 0) self.predatorEyeCooldown = 0; } if (self.feintCooldown > 0) { self.feintCooldown -= 16; if (self.feintCooldown < 0) self.feintCooldown = 0; } if (self.kingShotCooldown > 0) { self.kingShotCooldown -= 16; if (self.kingShotCooldown < 0) self.kingShotCooldown = 0; } // Handle predator eye end if (self.predatorEyeActive && Date.now() > self.predatorEyeEndTime) { self.predatorEyeActive = false; self.predatorEyeTargets = []; } // Handle feint stun end if (self.feintStunEnd > 0 && Date.now() > self.feintStunEnd) { for (var i = 0; i < self.feintStunnedOpponents.length; i++) { var opponent = self.feintStunnedOpponents[i]; opponent.stunned = false; if (opponent.originalSpeed) opponent.speed = opponent.originalSpeed; tween(opponent, { tint: 0xFFFFFF, scaleX: 1, scaleY: 1 }, { duration: 300 }); } self.feintStunnedOpponents = []; self.feintStunEnd = 0; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Activate Predator Eye when near enemy goal var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y)); if (goalDist < 400 && !self.predatorEyeActive && self.predatorEyeCooldown <= 0) { self.predatorEyeActive = true; self.predatorEyeCooldown = self.predatorEyeCooldownTime; self.predatorEyeEndTime = Date.now() + self.predatorEyeDuration; // Mark nearby player team as targets var playerTeam = [player, chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var targetDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (targetDist < 300) { self.predatorEyeTargets.push(playerTeam[i]); // Visual effect on targets tween(playerTeam[i], { tint: 0xFF0000 }, { duration: 200, onFinish: function onFinish() { tween(playerTeam[i], { tint: 0xFFFFFF }, { duration: 200 }); } }); } } // Visual effect for predator eye activation tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0xFF4500 }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } // Aggressive ball pursuit if (distance < 500) { var pursuitSpeed = self.predatorEyeActive ? self.speed * 1.3 : self.speed * 1.1; self.x += dx / distance * pursuitSpeed; self.y += dy / distance * pursuitSpeed; if (distance < 80) { // Use feint if available if (!self.feintActive && self.feintCooldown <= 0) { self.feintActive = true; self.feintCooldown = self.feintCooldownTime; // Stun nearby player team members var playerTeam = [player, chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var stunDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (stunDist <= self.feintStunRadius) { playerTeam[i].stunned = true; playerTeam[i].originalSpeed = playerTeam[i].speed; playerTeam[i].speed = 0; self.feintStunnedOpponents.push(playerTeam[i]); tween(playerTeam[i], { tint: 0xFFFF00, scaleX: 1.2, scaleY: 1.2 }, { duration: 250 }); } } self.feintStunEnd = Date.now() + self.feintStunDuration; self.feintActive = false; } // King Shot if near goal if (goalDist < 300 && self.kingShotCooldown <= 0) { self.kingShotCharging = true; self.kingShotChargeTime = Date.now(); self.kingShotCooldown = self.kingShotCooldownTime; // Visual charging effect tween(self, { scaleX: 1.5, scaleY: 1.5, tint: 0xFFD700 }, { duration: self.kingShotMaxCharge, onFinish: function onFinish() { // Execute King 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 * 25; // Extremely powerful ball.velocityY = goalDy / goalDistance * 25; ball.active = true; } tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); self.kingShotCharging = false; } }); } else { // Normal 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) { ball.velocityX = goalDx / goalDistance * 16; ball.velocityY = goalDy / goalDistance * 16; ball.active = true; } } } } else { // Return to 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.6; self.y += homeDy / homeDistance * self.speed * 0.6; } } }; // Add shootBall method self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; return self; }); 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 () { // 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; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Enhanced ball pursuit for hybrid player if (distance < 500 && !self.isSpeedBursting) { // Chase ball with hybrid intelligence var chaseSpeed = distance < 150 ? self.normalSpeed * 1.4 : self.normalSpeed * 1.1; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; } // 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; // Attack opponent goal if close to ball if (distance < 80) { var goalDx = opponentGoal.x - self.x; var goalDy = opponentGoal.y - self.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); var goalAngle = Math.atan2(goalDy, goalDx); var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x); var angleDiff = Math.abs(goalAngle - ballAngle); // Check if goal is within vision range (45 degrees) and shooting distance (500 pixels) if (goalDistance < 500 && angleDiff < Math.PI / 4) { // Use Shot skill toward opponent goal self.Shot(opponentGoal.x, opponentGoal.y, 14); } else { // Normal ball control toward goal var ballGoalDx = opponentGoal.x - ball.x; var ballGoalDy = opponentGoal.y - ball.y; var ballGoalDistance = Math.sqrt(ballGoalDx * ballGoalDx + ballGoalDy * ballGoalDy); if (ballGoalDistance > 0) { ball.velocityX = ballGoalDx / ballGoalDistance * 11; ball.velocityY = ballGoalDy / ballGoalDistance * 11; } } } } 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; } } } }; // Add shootBall method to ChigiriPlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 = 4; self.role = 'defense'; self.homeX = 1024; self.homeY = 600; self.maxDistance = 400; 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); // Defense priority: desperately try to get the ball if (distance < 400) { // Move aggressively toward ball - increased chase range self.x += dx / distance * (self.speed * 1.2); self.y += dy / distance * (self.speed * 1.2); // 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 * slideDy); 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; } } } // Check if opponent goal is within shooting range and use Shot skill if (distance < 80) { var goalDx = opponentGoal.x - self.x; var goalDy = opponentGoal.y - self.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); var goalAngle = Math.atan2(goalDy, goalDx); var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x); var angleDiff = Math.abs(goalAngle - ballAngle); // Check if goal is within vision range (60 degrees) and shooting distance (600 pixels) if (goalDistance < 600 && angleDiff < Math.PI / 3) { // Use Shot skill toward opponent goal self.Shot(opponentGoal.x, opponentGoal.y, 15); } else { // 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; } } }; // Add shootBall method to DefensePlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 = 3.5; self.role = 'forward'; self.homeX = 1024; self.homeY = 1400; self.hasBall = false; self.directShotCooldown = self.directShotCooldown || 0; 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 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; } // 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 if (distance < 400) { // Move toward ball with aggressive pursuit var pursuitSpeed = distance < 200 ? self.speed * 1.8 : self.speed * 1.3; self.x += dx / distance * pursuitSpeed; self.y += dy / distance * pursuitSpeed; // Check if opponent goal is within shooting range and use Shot skill if (distance < 80) { var goalDx = opponentGoal.x - self.x; var goalDy = opponentGoal.y - self.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); var goalAngle = Math.atan2(goalDy, goalDx); var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x); var angleDiff = Math.abs(goalAngle - ballAngle); // Check if goal is within vision range (45 degrees) and shooting distance (400 pixels) if (goalDistance < 400 && angleDiff < Math.PI / 4) { // Direct Shot every 30s if (self.directShotCooldown <= 0) { // Use Shot skill with high power self.Shot(opponentGoal.x, opponentGoal.y, 18); self.directShotCooldown = 30000; // 30 seconds } else { // Use normal Shot skill self.Shot(opponentGoal.x, opponentGoal.y, 10); } } else { // No clear shot to goal, move toward goal normally 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 * 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; } } }; // Add shootBall method to ForwardPlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 ball.velocityX = 0; ball.velocityY = 0; ball.x = self.x; ball.y = self.y; ball.active = false; player.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 () { // 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; } // 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 but check for goal proximity if (distance < 100) { // Check if too close to opponent goal while dribbling var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y)); if (goalDist < 250) { // Too close to goal, use Shot skill instead of continuing dribble self.Shot(opponentGoal.x, opponentGoal.y, 15); self.isDribbling = false; } else { 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 { // Normal ball pursuit if (distance < 400) { var chaseSpeed = distance < 150 ? self.speed * 1.3 : self.speed * 1.1; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; // 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 }); } }); } } // FLOW: If in Flow, perfect pass always goes directly to player, no curve, and is instant if (distance < 80 && 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 }); } }); } } } // 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; } } }; // Add shootBall method to HioriPlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; return self; }); var KaiserPlayer = 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.2; self.role = 'kaiser'; self.homeX = 1024; self.homeY = 900; self.hasBall = false; // Kaiser Impact variables self.kaiserImpactCooldown = 0; self.kaiserImpactCooldownTime = 12000; // 12 seconds self.kaiserImpactCharging = false; self.kaiserImpactChargeTime = 0; self.kaiserImpactMaxCharge = 1500; // 1.5 seconds charge // Magnus Flow variables self.magnusFlowActive = false; self.magnusFlowCooldown = 0; self.magnusFlowCooldownTime = 20000; // 20 seconds self.magnusFlowDuration = 10000; // 10 seconds active self.magnusFlowEndTime = 0; self.magnusFlowShotCount = 0; self.magnusFlowMaxShots = 3; // 3 special shots during Magnus Flow // Emperor Eye variables self.emperorEyeActive = false; self.emperorEyeCooldown = 0; self.emperorEyeCooldownTime = 15000; // 15 seconds self.emperorEyeDuration = 7000; // 7 seconds self.emperorEyeEndTime = 0; self.emperorEyeRange = 300; self.controlledOpponents = []; self.update = function () { // Update cooldowns if (self.kaiserImpactCooldown > 0) { self.kaiserImpactCooldown -= 16; if (self.kaiserImpactCooldown < 0) self.kaiserImpactCooldown = 0; } if (self.magnusFlowCooldown > 0) { self.magnusFlowCooldown -= 16; if (self.magnusFlowCooldown < 0) self.magnusFlowCooldown = 0; } if (self.emperorEyeCooldown > 0) { self.emperorEyeCooldown -= 16; if (self.emperorEyeCooldown < 0) self.emperorEyeCooldown = 0; } // Handle Magnus Flow end if (self.magnusFlowActive && Date.now() > self.magnusFlowEndTime) { self.magnusFlowActive = false; self.magnusFlowShotCount = 0; } // Handle Emperor Eye end if (self.emperorEyeActive && Date.now() > self.emperorEyeEndTime) { self.emperorEyeActive = false; // Release controlled opponents for (var i = 0; i < self.controlledOpponents.length; i++) { var opponent = self.controlledOpponents[i]; opponent.controlled = false; tween(opponent, { tint: 0xFFFFFF }, { duration: 300 }); } self.controlledOpponents = []; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Activate Emperor Eye when multiple enemies nearby var playerTeam = [player, chigiri, hiori, reo, nagi, bachira]; var nearbyEnemies = 0; for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (enemyDist < self.emperorEyeRange) { nearbyEnemies++; } } if (nearbyEnemies >= 2 && !self.emperorEyeActive && self.emperorEyeCooldown <= 0) { self.emperorEyeActive = true; self.emperorEyeCooldown = self.emperorEyeCooldownTime; self.emperorEyeEndTime = Date.now() + self.emperorEyeDuration; // Control nearby enemies for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (enemyDist < self.emperorEyeRange) { playerTeam[i].controlled = true; self.controlledOpponents.push(playerTeam[i]); tween(playerTeam[i], { tint: 0xFFD700 }, { duration: 300 }); } } // Visual effect for Emperor Eye tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xFFD700 }, { duration: 400, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } // Activate Magnus Flow when in attacking position var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y)); if (goalDist < 500 && !self.magnusFlowActive && self.magnusFlowCooldown <= 0 && distance < 120) { self.magnusFlowActive = true; self.magnusFlowCooldown = self.magnusFlowCooldownTime; self.magnusFlowEndTime = Date.now() + self.magnusFlowDuration; self.magnusFlowShotCount = 0; // Visual effect for Magnus Flow activation tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0x4169E1 }, { duration: 500, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } // Ball pursuit with enhanced abilities if (distance < 500) { var pursuitSpeed = self.emperorEyeActive ? self.speed * 1.3 : self.speed * 1.1; if (self.magnusFlowActive) pursuitSpeed *= 1.2; // Additional bonus during Magnus Flow self.x += dx / distance * pursuitSpeed; self.y += dy / distance * pursuitSpeed; if (distance < 80) { // Choose shot type based on active abilities if (self.magnusFlowActive && self.magnusFlowShotCount < self.magnusFlowMaxShots) { // Magnus Flow: Kaiser Impact with curve self.magnusFlowShotCount++; var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { // Magnus effect: powerful shot with extreme curve var magnusCurve = 15 + Math.random() * 10; // Strong curve ball.velocityX = goalDx / goalDistance * 24 + magnusCurve; ball.velocityY = goalDy / goalDistance * 24; ball.active = true; } // Visual effect for Magnus Flow shot tween(self, { scaleX: 1.5, scaleY: 1.5, tint: 0x4169E1 }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } else if (self.kaiserImpactCooldown <= 0) { // Normal Kaiser Impact self.kaiserImpactCharging = true; self.kaiserImpactChargeTime = Date.now(); self.kaiserImpactCooldown = self.kaiserImpactCooldownTime; // Visual charging effect tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0xFF4500 }, { duration: self.kaiserImpactMaxCharge, onFinish: function onFinish() { // Execute Kaiser Impact 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 * 20; // Very powerful ball.velocityY = goalDy / goalDistance * 20; ball.active = true; } tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); self.kaiserImpactCharging = false; } }); } else { // Normal 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) { ball.velocityX = goalDx / goalDistance * 16; ball.velocityY = goalDy / goalDistance * 16; ball.active = true; } } } } else { // Return to attacking 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.7; self.y += homeDy / homeDistance * self.speed * 0.7; } } }; // Add shootBall method self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 () { // 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 // 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 but check for goal proximity if (distance < 100) { // Check if too close to opponent goal while in roulette var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y)); if (goalDist < 250) { // Too close to goal, use Shot skill instead of continuing roulette self.Shot(opponentGoal.x, opponentGoal.y, 18); self.rouletteActive = false; } else { 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 && distance < 400) { // Normal ball pursuit var chaseSpeed = distance < 150 ? self.speed * 1.3 : self.speed * 1.1; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; // 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 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() + 3000; // 3 seconds 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() + 4000; // 4 seconds 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 }); } }); }; // Add shootBall method to MeguruBachiraPlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 = 3; 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 () { // 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); // Midfield: get ball and pass to forward if (distance < 350) { // Move toward ball with increased speed when chasing var chaseSpeed = distance < 150 ? self.speed * 1.5 : self.speed * 1.2; 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 but check for goal proximity if (distance < 80) { // Check if too close to player goal while dribbling var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y)); if (goalDist < 250) { // Too close to goal, use Shot skill instead of continuing dribble self.Shot(playerGoal.x, playerGoal.y, 12); self.isDribbling = false; self.hasDribbled = true; } else { ball.x = self.x; ball.y = self.y; ball.velocityX = 0; ball.velocityY = 0; } } // When dribble completes, check goal vision before shooting if (t >= 1 && !self.hasDribbled) { self.hasDribbled = true; self.isDribbling = false; // Check if opponent goal is within vision and range var goalDx = opponentGoal.x - self.x; var goalDy = opponentGoal.y - self.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); var goalAngle = Math.atan2(goalDy, goalDx); var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x); var angleDiff = Math.abs(goalAngle - ballAngle); // Check if goal is within vision range (60 degrees) and shooting distance (500 pixels) if (goalDistance < 500 && angleDiff < Math.PI / 3) { // Use Shot skill with curve self.Shot(opponentGoal.x, opponentGoal.y, 12); } else { // 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; } } }; // Add shootBall method to MidfieldPlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 = 4; self.role = 'nagi'; self.homeX = 1400; self.homeY = 2000; self.hasBall = false; // --- FLOW SYSTEM for Nagi --- self.flowActive = false; self.flowTriggerChecked = false; self.flowVolleyCount = 0; self.flowVolleyMax = 5; // 5 Revolver Fake Volleys in Flow self.flowText = null; // Fake Volley variables self.fakeVolleyState = 0; // 0: ready, 1: fake shot done, 2: real shot done self.fakeVolleyTarget = null; self.fakeVolleyStunRadius = 120; self.fakeVolleyStunDuration = 2000; // 2 seconds self.fakeVolleyStunnedOpponents = []; self.fakeVolleyStunEnd = 0; // 5 Staged Revolver Volley variables self.egoIgnited = false; self.revolverVolleyActive = false; self.revolverVolleyStage = 0; // 0-4 stages self.revolverVolleyCount = 0; self.revolverVolleyStunCount = 0; self.revolverVolleyStunRadius = 180; self.revolverVolleyStunDuration = 3000; // 3 seconds per stun self.revolverVolleyStunnedOpponents = []; self.revolverVolleyStunEnd = 0; self.revolverVolleyText = null; self.thankYouText = null; // Fake Attack variables self.fakeAttackCooldown = 0; self.fakeAttackCooldownTime = 5000; // Base 5 seconds, will be randomized self.fakeAttackStunRadius = 140; self.fakeAttackStunDuration = 0; // Will be randomized 5-10 seconds self.fakeAttackStunnedOpponents = []; self.fakeAttackStunEnd = 0; self.fakeAttackChance = 0.3; // 30% chance for shots to be fake self.fakeVolleyVisual = null; // Ball pull variables self.ballPullRange = 150; self.ballPullActive = false; self.ballPullVisual = null; self.ballPullCooldown = 0; self.ballPullCooldownTime = 5000; // 5 seconds // Ball control text self.ballControlText = null; self.update = function () { // --- FLOW TRIGGER: If 2 goals scored by Nagi's team, activate Flow for random teammate (including Nagi) --- if (!self.flowActive && !self.flowTriggerChecked && typeof playerScore !== "undefined" && playerScore >= 2) { self.flowTriggerChecked = true; // Pick a random teammate (Nagi, Hiori, Chigiri, Reo) to enter Flow var teammates = []; if (typeof nagi !== "undefined") teammates.push(nagi); if (typeof hiori !== "undefined") teammates.push(hiori); if (typeof chigiri !== "undefined") teammates.push(chigiri); if (typeof reo !== "undefined") teammates.push(reo); if (teammates.length > 0) { var idx = Math.floor(Math.random() * teammates.length); var chosen = teammates[idx]; if (chosen && typeof chosen.flowActive !== "undefined") { chosen.flowActive = true; chosen.flowVolleyCount = 0; // Show Flow text above chosen if (!chosen.flowText) { chosen.flowText = chosen.addChild(new Text2('FLOW', { size: 48, fill: 0x00FFFF })); chosen.flowText.anchor.set(0.5, 0.5); chosen.flowText.x = 0; chosen.flowText.y = -100; chosen.flowText.alpha = 1; tween(chosen.flowText, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 300, onFinish: function onFinish() { tween(chosen.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; } // --- Nagi Unstoppable Shot Cooldown Logic --- if (typeof self.nagiUnstoppableCooldown === "undefined") { self.nagiUnstoppableCooldown = 0; self.nagiUnstoppableCooldownTime = 150000; // 150 seconds in ms self.nagiUnstoppableLastShot = 0; self.nagiUnstoppableActive = false; self.nagiUnstoppableText = null; } // Reo's Ego Ignition - Check if close to Nagi and activate his Flow if (typeof nagi !== "undefined") { // Block Ego ignition if Nagi is in unstoppable cooldown if (nagi.nagiUnstoppableCooldown > Date.now()) { // Optionally, show a quick message that Nagi is in cooldown if (!nagi.nagiUnstoppableText) { nagi.nagiUnstoppableText = game.addChild(new Text2('Nagi Cooldown: ' + Math.ceil((nagi.nagiUnstoppableCooldown - Date.now()) / 1000) + 's', { size: 48, fill: 0xFF0000 })); nagi.nagiUnstoppableText.anchor.set(0.5, 0.5); nagi.nagiUnstoppableText.x = 1024; nagi.nagiUnstoppableText.y = 400; nagi.nagiUnstoppableText.alpha = 0.9; tween(nagi.nagiUnstoppableText, { alpha: 0 }, { duration: 1200, onFinish: function onFinish() { if (nagi.nagiUnstoppableText) { nagi.nagiUnstoppableText.destroy(); nagi.nagiUnstoppableText = null; } } }); } } else { var nagiDist = Math.sqrt((nagi.x - self.x) * (nagi.x - self.x) + (nagi.y - self.y) * (nagi.y - self.y)); var ballToNagiDist = Math.sqrt((ball.x - nagi.x) * (ball.x - nagi.x) + (ball.y - nagi.y) * (ball.y - nagi.y)); // If Reo is close to Nagi and ball is near Nagi, ignite Nagi's Ego if (nagiDist < 200 && ballToNagiDist < 150 && !nagi.revolverVolleyActive && !nagi.egoIgnited) { // Reo ignites Nagi's Ego nagi.egoIgnited = true; nagi.revolverVolleyActive = true; nagi.revolverVolleyStage = 0; nagi.revolverVolleyCount = 0; nagi.revolverVolleyStunCount = 0; // Visual effect for Ego ignition tween(nagi, { scaleX: 1.5, scaleY: 1.5, tint: 0x00FFFF }, { duration: 500, onFinish: function onFinish() { tween(nagi, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); // Show Ego ignition text var egoText = nagi.addChild(new Text2('EGO IGNITED!', { size: 60, fill: 0xFF0000 })); egoText.anchor.set(0.5, 0.5); egoText.x = 0; egoText.y = -120; tween(egoText, { alpha: 0, y: -180 }, { duration: 2000, onFinish: function onFinish() { egoText.destroy(); } }); LK.getSound('ego_ignition').play(); } } } // --- Nagi Unstoppable Shot Trigger --- if (self.hasBall && !self.nagiUnstoppableActive && self.nagiUnstoppableCooldown <= Date.now() && typeof opponentGoal !== "undefined") { // If Nagi is close enough to the opponent goal, fire the unstoppable shot var goalDx = opponentGoal.x - self.x; var goalDy = opponentGoal.y - self.y; var goalDist = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDist < 400) { // Show special unstoppable shot text var unstoppableText = game.addChild(new Text2('Yıkıcı Şut!', { size: 90, fill: 0xFF0000 })); unstoppableText.anchor.set(0.5, 0.5); unstoppableText.x = 1024; unstoppableText.y = 500; unstoppableText.alpha = 1; tween(unstoppableText, { alpha: 0 }, { duration: 1800, onFinish: function onFinish() { unstoppableText.destroy(); } }); // Visual effect for Nagi tween(self, { scaleX: 1.7, scaleY: 1.7, tint: 0xFFFFFF }, { duration: 400, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0x9966ff }, { duration: 400 }); } }); // Unstoppable shot: max power, cannot be blocked, ball is unstoppable for 2s ball.x = self.x; ball.y = self.y - 40; ball.velocityX = goalDx / goalDist * 40; ball.velocityY = goalDy / goalDist * 40; ball.active = true; self.hasBall = false; ball.unstoppableUntil = Date.now() + 2000; self.nagiUnstoppableActive = true; self.nagiUnstoppableCooldown = Date.now() + self.nagiUnstoppableCooldownTime; // End unstoppable state after 2s LK.setTimeout(function () { self.nagiUnstoppableActive = false; }, 2000); } } // --- Nagi Cooldown Restriction: Only allow stealing during cooldown --- if (self.nagiUnstoppableCooldown > Date.now()) { // If Nagi is in cooldown, prevent all actions except for stealing the ball from opponents // Stealing logic is already handled below (Ball Control section) // Prevent all other actions by returning early return; } // Update cooldowns 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; } if (self.ballPullCooldown > 0) { self.ballPullCooldown -= 16; if (self.ballPullCooldown < 0) self.ballPullCooldown = 0; } // Update Fake Attack cooldown if (self.fakeAttackCooldown > 0) { self.fakeAttackCooldown -= 16; if (self.fakeAttackCooldown < 0) self.fakeAttackCooldown = 0; } // Handle Fake Attack stun end if (self.fakeAttackStunEnd > 0 && Date.now() > self.fakeAttackStunEnd) { // Restore stunned opponents from Fake Attack for (var i = 0; i < self.fakeAttackStunnedOpponents.length; i++) { var opponent = self.fakeAttackStunnedOpponents[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.fakeAttackStunnedOpponents = []; self.fakeAttackStunEnd = 0; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Nagi Ball Control: Steal from opponent if close and not immune if (!self.nagiStealImmunity) self.nagiStealImmunity = 0; var canSteal = true; if (self.nagiStealImmunity > Date.now()) canSteal = false; // Check for opponent with ball in Ball Control range var stoleFromOpponent = false; if (canSteal) { 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 (opp.hasBall && oppDist < 120) { // Steal the ball from opponent opp.hasBall = false; ball.active = true; // Move ball to Nagi's feet ball.velocityX = 0; ball.velocityY = 0; tween(ball, { x: self.x, y: self.y + 40 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { ball.active = false; self.hasBall = true; } }); // Show "Ball Control" text if (!self.ballControlText) { self.ballControlText = self.addChild(new Text2('Ball Control', { size: 40, fill: 0xFFFFFF })); 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; } } }); }, 2000); } }); } // Set 3s immunity for Nagi after stealing self.nagiStealImmunity = Date.now() + 3000; stoleFromOpponent = true; break; } } } // If not stealing from opponent, allow normal ball control from loose ball if (!stoleFromOpponent && distance < 120 && ball.active && !self.hasBall && canSteal) { ball.velocityX = 0; ball.velocityY = 0; tween(ball, { x: self.x, y: self.y + 40 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { ball.active = false; self.hasBall = true; } }); if (!self.ballControlText) { self.ballControlText = self.addChild(new Text2('Ball Control', { size: 40, fill: 0xFFFFFF })); 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; } } }); }, 2000); } }); } } // --- 5 STAGED REVOLVER VOLLEY --- // Ensure Nagi always gets the ball for the 5 Staged Revolver Fake Volley if (self.revolverVolleyActive && self.egoIgnited) { // If Nagi does not have the ball, forcibly take control of the ball var nagiToBallDist = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y)); if (!self.hasBall && nagiToBallDist > 10) { // Instantly move ball to Nagi's feet and stop it ball.x = self.x; ball.y = self.y + 40; ball.velocityX = 0; ball.velocityY = 0; ball.active = false; self.hasBall = true; // Remove ball from any other player or opponent if (typeof player !== "undefined") player.hasBall = false; if (typeof chigiri !== "undefined") chigiri.hasBall = false; if (typeof hiori !== "undefined") hiori.hasBall = false; if (typeof reo !== "undefined") reo.hasBall = false; if (typeof bachira !== "undefined") bachira.hasBall = false; for (var i = 0; i < opponents.length; i++) { if (opponents[i] && typeof opponents[i].hasBall !== "undefined") opponents[i].hasBall = false; } } // Move Nagi away from crowd before starting - more complex positioning if (self.revolverVolleyStage === 0) { var crowdNearby = false; var allCharacters = [player, chigiri, hiori, reo, bachira]; var crowdCenter = { x: 0, y: 0, count: 0 }; for (var i = 0; i < opponents.length; i++) { allCharacters.push(opponents[i]); } // Check if crowd is nearby and calculate crowd center for (var i = 0; i < allCharacters.length; i++) { if (!allCharacters[i] || allCharacters[i] === self) continue; var charDist = Math.sqrt((allCharacters[i].x - self.x) * (allCharacters[i].x - self.x) + (allCharacters[i].y - self.y) * (allCharacters[i].y - self.y)); if (charDist < 300) { // Increased detection radius for better positioning crowdNearby = true; crowdCenter.x += allCharacters[i].x; crowdCenter.y += allCharacters[i].y; crowdCenter.count++; } } // Move away from crowd to clear space with intelligent positioning if (crowdNearby && crowdCenter.count > 0) { // Calculate average position of nearby crowd crowdCenter.x /= crowdCenter.count; crowdCenter.y /= crowdCenter.count; // Find direction away from crowd center var awayDx = self.x - crowdCenter.x; var awayDy = self.y - crowdCenter.y; var awayDist = Math.sqrt(awayDx * awayDx + awayDy * awayDy); // If too close to crowd center, move in a random direction if (awayDist < 50) { var randomAngle = Math.random() * Math.PI * 2; awayDx = Math.cos(randomAngle); awayDy = Math.sin(randomAngle); awayDist = 1; } if (awayDist > 0) { // Calculate clear position - move 250 pixels away from crowd center var clearDistance = 250; var clearX = self.x + awayDx / awayDist * clearDistance; var clearY = self.y + awayDy / awayDist * clearDistance; // Keep within field bounds clearX = Math.max(200, Math.min(1848, clearX)); clearY = Math.max(400, Math.min(2300, clearY)); // Smooth movement using tween for more elegant positioning var targetDx = clearX - self.x; var targetDy = clearY - self.y; var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy); if (targetDist > 30) { // Only move if significant distance // Use tween for smooth, professional movement tween(self, { x: clearX, y: clearY }, { duration: 800, easing: tween.easeOut }); // Also smoothly move ball with Nagi if (self.hasBall) { tween(ball, { x: clearX, y: clearY + 40 }, { duration: 800, easing: tween.easeOut }); } // Visual effect to show Nagi is positioning tween(self, { scaleX: 1.1, scaleY: 1.1, tint: 0x00FFFF }, { duration: 400, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 400 }); } }); return; // Wait until positioning is complete } } } // Show positioning message first, then "teşekkürler Reo..." when properly positioned if (!self.thankYouText) { // First show positioning message var positioningText = game.addChild(new Text2('Pozisyon alıyorum...', { size: 56, fill: 0xFFFF00 })); positioningText.anchor.set(0.5, 0.5); positioningText.x = 1024; // Center of screen positioningText.y = 700; // Upper middle of screen positioningText.alpha = 0; // Animate positioning message tween(positioningText, { alpha: 1, scaleX: 1.1, scaleY: 1.1 }, { duration: 500, onFinish: function onFinish() { // Keep visible for 1.5 seconds then fade out LK.setTimeout(function () { tween(positioningText, { alpha: 0, scaleX: 0.9, scaleY: 0.9 }, { duration: 400, onFinish: function onFinish() { positioningText.destroy(); // Now show the thank you message self.thankYouText = game.addChild(new Text2('teşekkürler Reo...', { size: 72, fill: 0x00FFFF })); self.thankYouText.anchor.set(0.5, 0.5); self.thankYouText.x = 1024; // Center of screen self.thankYouText.y = 800; // Middle of screen self.thankYouText.alpha = 0; // Animate thank you message appearance if (self.thankYouText) { tween(self.thankYouText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 800, onFinish: function onFinish() { // Keep message visible for 2 seconds then fade out LK.setTimeout(function () { if (self.thankYouText) { tween(self.thankYouText, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 600, onFinish: function onFinish() { if (self.thankYouText) { self.thankYouText.destroy(); self.thankYouText = null; } } }); } }, 2000); } }); } } }); }, 1500); } }); } } if (distance < 100) { // Stage progression: Each stage stuns opponents and increases power if (self.revolverVolleyStage < 5) { self.revolverVolleyStage++; self.revolverVolleyCount++; // Show stage number on screen for stages 1-4, and "5." for final stage var stageText = null; if (self.revolverVolleyStage < 5) { // Show stage number for fake volleys stageText = game.addChild(new Text2(self.revolverVolleyStage + '..', { size: 120, fill: 0xFF00FF })); } else { // Show "5." for final shot stageText = game.addChild(new Text2('5.', { size: 150, fill: 0xFF0000 })); } stageText.anchor.set(0.5, 0.5); stageText.x = 1024; // Center of screen stageText.y = 600; // Upper middle of screen stageText.alpha = 0; // Animate stage text tween(stageText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 400, onFinish: function onFinish() { // Keep visible for 1 second then fade LK.setTimeout(function () { tween(stageText, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, onFinish: function onFinish() { stageText.destroy(); } }); }, 1000); } }); // Update revolver volley text above Nagi if (!self.revolverVolleyText) { self.revolverVolleyText = self.addChild(new Text2('REVOLVER VOLLEY STAGE ' + self.revolverVolleyStage, { size: 40, fill: 0xFF00FF })); self.revolverVolleyText.anchor.set(0.5, 0.5); self.revolverVolleyText.x = 0; self.revolverVolleyText.y = -140; } else { self.revolverVolleyText.setText('REVOLVER VOLLEY STAGE ' + self.revolverVolleyStage); } // For stages 1-4, completely stop ALL opponents on the field if (self.revolverVolleyStage <= 4) { // Stop ALL opponents completely during fake volleys for (var i = 0; i < opponents.length; i++) { var opponent = opponents[i]; if (!opponent.stunned) { opponent.stunned = true; opponent.originalSpeed = opponent.originalSpeed || opponent.speed; opponent.speed = 0; // Completely stop them self.revolverVolleyStunnedOpponents.push(opponent); self.revolverVolleyStunCount++; // Visual effect for stunned opponent tween(opponent, { tint: 0xFF00FF, scaleX: 1.2, scaleY: 1.2 }, { duration: 300 }); } } } else { // Stage 5: Stun all opponents in expanding radius var stageStunRadius = self.revolverVolleyStunRadius + self.revolverVolleyStage * 50; 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 <= stageStunRadius) { opponentsInRange.push(opponent); } } // Apply stunning effect for (var i = 0; i < opponentsInRange.length; i++) { var opponent = opponentsInRange[i]; if (!opponent.stunned) { opponent.stunned = true; opponent.originalSpeed = opponent.speed; opponent.speed = 0; self.revolverVolleyStunnedOpponents.push(opponent); self.revolverVolleyStunCount++; // Visual effect for stunned opponent tween(opponent, { tint: 0xFF00FF, scaleX: 1.2, scaleY: 1.2 }, { duration: 300 }); } } } // Visual effect for each stage var stageColors = [0xFF0000, 0xFF4500, 0xFFFF00, 0x00FF00, 0x00FFFF]; tween(self, { scaleX: 1.3 + self.revolverVolleyStage * 0.1, scaleY: 1.3 + self.revolverVolleyStage * 0.1, tint: stageColors[self.revolverVolleyStage - 1] || 0xFFFFFF }, { duration: 400, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); self.revolverVolleyStunEnd = Date.now() + self.revolverVolleyStunDuration; // Stage 5: Final devastating shot if (self.revolverVolleyStage === 5) { // Show Turkish awakening message before final shot var awakeningText = game.addChild(new Text2('Artık uyandım... Bu benim gerçek gücüm!', { size: 72, fill: 0xFF0000 })); awakeningText.anchor.set(0.5, 0.5); awakeningText.x = 1024; // Center of screen awakeningText.y = 900; // Lower middle of screen awakeningText.alpha = 0; // Animate awakening message tween(awakeningText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 800, onFinish: function onFinish() { // Keep message visible for 2 seconds then fade out LK.setTimeout(function () { tween(awakeningText, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 600, onFinish: function onFinish() { awakeningText.destroy(); // Now perform the final shot after message // Create screen shake effect tween(game, { x: game.x + 20 }, { duration: 100, onFinish: function onFinish() { tween(game, { x: game.x - 40 }, { duration: 100, onFinish: function onFinish() { tween(game, { x: game.x + 20 }, { duration: 100 }); } }); } }); // Devastating shot toward goal (UNSTOPPABLE) // Always aim at the center of the opponent's goal for the final shot var targetGoalX = opponentGoal.x; var targetGoalY = opponentGoal.y; var goalDx = targetGoalX - self.x; var goalDy = targetGoalY - self.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance > 0) { // Unstoppable: ignore all blocks, max power, and ball cannot be stopped until goal ball.x = self.x; ball.y = self.y - 40; ball.velocityX = goalDx / goalDistance * 48; // Dramatically higher velocity for 5th stage ball.velocityY = goalDy / goalDistance * 48; ball.active = true; self.hasBall = false; // Mark ball as "unstoppable" for a short time (cannot be stopped by anyone) ball.unstoppableUntil = Date.now() + 1200; } LK.getSound('revolver_volley').play(); } }); }, 2000); } }); // Reset after completing 5 stages LK.setTimeout(function () { self.revolverVolleyActive = false; self.egoIgnited = false; self.revolverVolleyStage = 0; self.revolverVolleyCount = 0; self.revolverVolleyStunCount = 0; if (self.revolverVolleyText) { self.revolverVolleyText.destroy(); self.revolverVolleyText = null; } }, 4000); //{r7} // Extended timing for message } else { // For stages 1-4, perform fake shots with increasing intensity ball.velocityX = (Math.random() - 0.5) * (2 + self.revolverVolleyStage); ball.velocityY = (Math.random() - 0.5) * (2 + self.revolverVolleyStage); ball.active = true; } } } // Prevent normal volley logic during Revolver Volley return; } // Handle Revolver Volley stun restoration if (self.revolverVolleyStunEnd > 0 && Date.now() > self.revolverVolleyStunEnd) { for (var i = 0; i < self.revolverVolleyStunnedOpponents.length; i++) { var opponent = self.revolverVolleyStunnedOpponents[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.revolverVolleyStunnedOpponents = []; self.revolverVolleyStunEnd = 0; } // --- FLOW: 5 Revolver Fake Volley logic --- if (self.flowActive) { // If close to ball, perform up to 5 chained fake volleys (fake+real) in a row if (distance < 80 && self.flowVolleyCount < self.flowVolleyMax && self.fakeVolleyState === 0) { self.fakeVolleyState = 1; self.flowVolleyCount++; // Stun nearby opponents 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.1, scaleY: 1.1 }, { duration: 200 }); } self.fakeVolleyStunEnd = Date.now() + self.fakeVolleyStunDuration; // Fake shot - ball barely moves ball.velocityX = (Math.random() - 0.5) * 2; ball.velocityY = (Math.random() - 0.5) * 2; ball.active = true; tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xFFFF00 }, { duration: 200, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); } else if (self.flowActive && self.fakeVolleyState === 1) { // After fake, do real volley self.fakeVolleyState = 2; 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 * 16; ball.velocityY = goalDy / goalDistance * 16; ball.active = true; } tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0xFF0000 }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); // Reset fake volley state after a short delay, and chain next if not reached max LK.setTimeout(function () { self.fakeVolleyState = 0; // If not reached 5, chain next volley automatically (simulate rapid-fire) if (self.flowActive && self.flowVolleyCount < self.flowVolleyMax) { // Move Nagi slightly forward for next volley self.x += (opponentGoal.x - self.x) * 0.08; self.y += (opponentGoal.y - self.y) * 0.08; } else if (self.flowActive && self.flowVolleyCount >= self.flowVolleyMax) { // End Flow after 5 volleys self.flowActive = false; self.flowVolleyCount = 0; if (self.flowText) { self.flowText.destroy(); self.flowText = null; } } }, 500); } // Prevent normal volley logic during Flow return; } // After stealing, for 3s, Nagi cannot be stolen from and focuses on goal if (self.hasBall && self.nagiStealImmunity > Date.now()) { // Nagi focuses on goal: move toward opponent goal and shoot if close var goalDx = opponentGoal.x - self.x; var goalDy = opponentGoal.y - self.y; var goalDist = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDist > 0) { self.x += goalDx / goalDist * self.speed * 1.2; self.y += goalDy / goalDist * self.speed * 1.2; // If close to goal, shoot if (goalDist < 200) { ball.active = true; ball.velocityX = goalDx / goalDist * 16; ball.velocityY = goalDy / goalDist * 16; self.hasBall = false; // Remove immunity after shot self.nagiStealImmunity = 0; } } } else { // If Nagi doesn't have ball, always move to a position close to the opponent's goal // Find a spot within 200px of the goal center, but not inside the goal var goalAreaRadius = 200; var angle = Math.PI / 2 + Math.sin(Date.now() / 1200); // oscillate angle for realism var targetX = opponentGoal.x + goalAreaRadius * Math.cos(angle); var targetY = opponentGoal.y + 120 + goalAreaRadius * Math.abs(Math.sin(angle)); // always above the goal line // Clamp to field bounds targetX = Math.max(200, Math.min(1848, targetX)); targetY = Math.max(200, Math.min(600, targetY)); // keep Nagi near the top, but not inside the goal var moveDx = targetX - self.x; var moveDy = targetY - self.y; var moveDist = Math.sqrt(moveDx * moveDx + moveDy * moveDy); if (moveDist > 10) { self.x += moveDx / moveDist * self.speed * 0.8; self.y += moveDy / moveDist * self.speed * 0.8; } } // Ball pull ability - show range when ball is within pull range if (distance <= self.ballPullRange && !self.ballPullActive && self.ballPullCooldown <= 0) { if (!self.ballPullVisual) { // Create ball pull range visual self.ballPullVisual = self.addChild(LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3, tint: 0x00FF00 })); self.ballPullVisual.alpha = 0.2; } // Pull ball towards self if (distance > 30) { self.ballPullActive = true; var pullForce = 8; ball.velocityX = -dx / distance * pullForce; ball.velocityY = -dy / distance * pullForce; ball.active = true; self.ballPullCooldown = self.ballPullCooldownTime; // Visual effect for ball pull tween(self, { scaleX: 1.2, scaleY: 1.2, tint: 0x00FF00 }, { duration: 150, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 150 }); } }); } } else { // Remove ball pull visual if ball is out of range if (self.ballPullVisual) { self.ballPullVisual.destroy(); self.ballPullVisual = null; } self.ballPullActive = false; } // Normal ball pursuit if (distance < 400) { var chaseSpeed = distance < 150 ? self.speed * 1.2 : self.speed * 1.0; self.x += dx / distance * chaseSpeed; self.y += dy / distance * chaseSpeed; // Volley ability when close to ball with Fake Attack chance if (distance < 80) { // Check if this should be a Fake Attack var shouldFakeAttack = self.fakeAttackCooldown <= 0 && Math.random() < self.fakeAttackChance; if (shouldFakeAttack) { // FAKE ATTACK - stun opponents and barely move ball // Find opponents within Fake Attack stun 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.fakeAttackStunRadius) { 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.fakeAttackStunnedOpponents.push(opponent); // Visual effect for stunned opponent tween(opponent, { tint: 0xFF6600, // Orange tint for Fake Attack scaleX: 1.2, scaleY: 1.2 }, { duration: 250 }); } // Randomize stun duration (5-10 seconds) self.fakeAttackStunDuration = 5000 + Math.random() * 5000; self.fakeAttackStunEnd = Date.now() + self.fakeAttackStunDuration; // Randomize next cooldown (5-10 seconds) self.fakeAttackCooldownTime = 5000 + Math.random() * 5000; self.fakeAttackCooldown = self.fakeAttackCooldownTime; // Fake attack - ball barely moves ball.velocityX = (Math.random() - 0.5) * 3; ball.velocityY = (Math.random() - 0.5) * 3; ball.active = true; // Visual effect for Fake Attack tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0xFF6600 // Orange for Fake Attack }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } else if (self.fakeVolleyState === 0) { // First shot - FAKE VOLLEY self.fakeVolleyState = 1; // Find opponents within stun 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); } } // 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.fakeVolleyStunnedOpponents.push(opponent); // Visual effect for stunned opponent tween(opponent, { tint: 0xFFFF00, scaleX: 1.1, scaleY: 1.1 }, { duration: 200 }); } self.fakeVolleyStunEnd = Date.now() + self.fakeVolleyStunDuration; // Fake shot - ball barely moves to show it's fake ball.velocityX = (Math.random() - 0.5) * 2; ball.velocityY = (Math.random() - 0.5) * 2; ball.active = true; // Visual effect for fake shot tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xFFFF00 }, { duration: 200, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 200 }); } }); } else if (self.fakeVolleyState === 1) { // Second shot - REAL VOLLEY self.fakeVolleyState = 2; // Real powerful shot toward opponent 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 * 16; ball.velocityY = goalDy / goalDistance * 16; ball.active = true; } // Visual effect for real shot tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0xFF0000 }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); // Reset fake volley state after a delay LK.setTimeout(function () { self.fakeVolleyState = 0; }, 3000); } } } 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.4; self.y += homeDy / homeDistance * self.speed * 0.4; } } }; // Add shootBall method to NagiPlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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; // Add shootBall method to Player self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 () { // 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 if (self.isDefensiveMode) { // Defensive mode: more active interception and ball pursuit if (distance < 500) { var defensiveSpeed = self.speed * 1.1; self.x += dx / distance * defensiveSpeed; self.y += dy / distance * defensiveSpeed; // 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 (distance < 700) { var attackSpeed = self.speed * 1.7; self.x += dx / distance * attackSpeed; self.y += dy / distance * attackSpeed; // Attack shot if (distance < 80) { 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; } } } 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 }); } }); } }; // Add shootBall method to ReoPlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; return self; }); var RinPlayer = 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 = 4.8; self.role = 'rin'; self.homeX = 1024; self.homeY = 1000; self.hasBall = false; // Destroyer mode abilities self.destroyerModeActive = false; self.destroyerModeCooldown = 0; self.destroyerModeCooldownTime = 18000; // 18 seconds self.destroyerModeDuration = 8000; // 8 seconds active self.destroyerModeEndTime = 0; self.destroyerModeTargets = []; // Precision shot abilities self.precisionShotCooldown = 0; self.precisionShotCooldownTime = 10000; // 10 seconds self.precisionShotActive = false; // Spatial awareness abilities self.spatialAwarenessActive = false; self.spatialAwarenessCooldown = 0; self.spatialAwarenessCooldownTime = 15000; // 15 seconds self.spatialAwarenessDuration = 6000; // 6 seconds self.spatialAwarenessEndTime = 0; self.spatialAwarenessRadius = 250; self.update = function () { // Update cooldowns if (self.destroyerModeCooldown > 0) { self.destroyerModeCooldown -= 16; if (self.destroyerModeCooldown < 0) self.destroyerModeCooldown = 0; } if (self.precisionShotCooldown > 0) { self.precisionShotCooldown -= 16; if (self.precisionShotCooldown < 0) self.precisionShotCooldown = 0; } if (self.spatialAwarenessCooldown > 0) { self.spatialAwarenessCooldown -= 16; if (self.spatialAwarenessCooldown < 0) self.spatialAwarenessCooldown = 0; } // Handle destroyer mode end if (self.destroyerModeActive && Date.now() > self.destroyerModeEndTime) { self.destroyerModeActive = false; // Restore target speeds for (var i = 0; i < self.destroyerModeTargets.length; i++) { var target = self.destroyerModeTargets[i]; if (target.originalSpeed) target.speed = target.originalSpeed; tween(target, { tint: 0xFFFFFF }, { duration: 300 }); } self.destroyerModeTargets = []; } // Handle spatial awareness end if (self.spatialAwarenessActive && Date.now() > self.spatialAwarenessEndTime) { self.spatialAwarenessActive = false; } var dx = ball.x - self.x; var dy = ball.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Activate spatial awareness when enemies approach var playerTeam = [player, chigiri, hiori, reo, nagi, bachira]; var nearbyEnemies = 0; for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (enemyDist < self.spatialAwarenessRadius) { nearbyEnemies++; } } if (nearbyEnemies >= 2 && !self.spatialAwarenessActive && self.spatialAwarenessCooldown <= 0) { self.spatialAwarenessActive = true; self.spatialAwarenessCooldown = self.spatialAwarenessCooldownTime; self.spatialAwarenessEndTime = Date.now() + self.spatialAwarenessDuration; // Visual effect tween(self, { scaleX: 1.2, scaleY: 1.2, tint: 0x800080 }, { duration: 300, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } // Activate destroyer mode when frustrated (low ball control) if (!self.destroyerModeActive && self.destroyerModeCooldown <= 0 && distance > 200) { self.destroyerModeActive = true; self.destroyerModeCooldown = self.destroyerModeCooldownTime; self.destroyerModeEndTime = Date.now() + self.destroyerModeDuration; // Target all nearby player team members for (var i = 0; i < playerTeam.length; i++) { if (!playerTeam[i]) continue; var targetDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y)); if (targetDist < 350) { self.destroyerModeTargets.push(playerTeam[i]); playerTeam[i].originalSpeed = playerTeam[i].speed; playerTeam[i].speed = playerTeam[i].speed * 0.6; // 60% speed tween(playerTeam[i], { tint: 0x8B0000 }, { duration: 300 }); } } // Visual effect for destroyer mode tween(self, { scaleX: 1.4, scaleY: 1.4, tint: 0x8B0000 }, { duration: 500, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); } // Ball pursuit with enhanced abilities if (distance < 500) { var pursuitSpeed = self.destroyerModeActive ? self.speed * 1.4 : self.speed * 1.2; if (self.spatialAwarenessActive) pursuitSpeed *= 1.2; // Additional bonus self.x += dx / distance * pursuitSpeed; self.y += dy / distance * pursuitSpeed; if (distance < 80) { // Precision shot if available var goalDx = opponentGoal.x - ball.x; var goalDy = opponentGoal.y - ball.y; var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy); if (goalDistance < 400 && self.precisionShotCooldown <= 0) { self.precisionShotActive = true; self.precisionShotCooldown = self.precisionShotCooldownTime; // Precision shot - extremely accurate and powerful if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 22; // High power ball.velocityY = goalDy / goalDistance * 22; ball.active = true; } // Visual effect for precision shot tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0x00CED1 }, { duration: 400, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1, tint: 0xFFFFFF }, { duration: 300 }); } }); self.precisionShotActive = false; } else { // Normal shot toward goal if (goalDistance > 0) { ball.velocityX = goalDx / goalDistance * 15; ball.velocityY = goalDy / goalDistance * 15; 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.6; self.y += homeDy / homeDistance * self.speed * 0.6; } } }; // Add shootBall method self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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 = 4.5; 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 () { // 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 but check for goal proximity if (distance < 80) { // Check if too close to player goal while dribbling var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y)); if (goalDist < 250) { // Too close to goal, use Shot skill instead of continuing dribble self.Shot(playerGoal.x, playerGoal.y, 16); self.isDribbling = false; } else { 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 but check for goal proximity if (distance < 100) { // Check if too close to player goal while zigzag dribbling var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y)); if (goalDist < 250) { // Too close to goal, use Shot skill instead of continuing dribble self.Shot(playerGoal.x, playerGoal.y, 16); self.isZigzagDribbling = false; } else { 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 but check for goal proximity if (distance < 100) { // Check if too close to player goal while chop dribbling var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y)); if (goalDist < 250) { // Too close to goal, use Shot skill instead of continuing dribble self.Shot(playerGoal.x, playerGoal.y, 16); self.isChopDribbling = false; } else { 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 but check for goal proximity if (distance < 120) { // Check if too close to player goal while hilal dribbling var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y)); if (goalDist < 250) { // Too close to goal, use Shot skill instead of continuing dribble self.Shot(playerGoal.x, playerGoal.y, 20); self.flowHilalDribbling = false; self.flowHilalShotDone = true; } else { 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 aggressively but stay in attacking areas if (distance < 500) { // 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; // 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.4 : self.speed * 1.2; // 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 * 15; ball.velocityY = passDy / passDist * 15; 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 = 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; 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 = opponentGoal.x - ball.x; var goalDy = opponentGoal.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; } } }; }; // Add shootBall method to SaePlayer self.shootBall = function (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); self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / distance * finalPower; ball.velocityY = dy / distance * finalPower; LK.getSound('kick').play(); } }; // Add Shot skill self.Shot = function (targetX, targetY, power) { self.shootBall(targetX, targetY, power); }; // Add Pass skill self.Pass = function (targetPlayer) { if (!targetPlayer) return; var dx = targetPlayer.x - ball.x; var dy = targetPlayer.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } }; 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.update = function () { if (self.active) { self.x += self.velocityX; self.y += self.velocityY; self.velocityX *= self.friction; self.velocityY *= self.friction; // 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); // Forward player var forwardPlayer = game.addChild(new ForwardPlayer()); forwardPlayer.x = 1024; forwardPlayer.y = 1400; forwardPlayer.homeX = 1024; forwardPlayer.homeY = 1400; opponents.push(forwardPlayer); // 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); // Aiku - opponent team defensive leader with Metavision var aiku = game.addChild(new AikuPlayer(0x2E8B57)); aiku.x = 1024; aiku.y = 500; aiku.homeX = 1024; aiku.homeY = 500; opponents.push(aiku); // Barou - opponent team predator striker var barou = game.addChild(new BarouPlayer(0x8B0000)); barou.x = 1024; barou.y = 1200; barou.homeX = 1024; barou.homeY = 1200; opponents.push(barou); // Rin - opponent team destroyer midfielder var rin = game.addChild(new RinPlayer(0x800080)); rin.x = 1024; rin.y = 1000; rin.homeX = 1024; rin.homeY = 1000; opponents.push(rin); // Kaiser - opponent team emperor striker with dual shot abilities var kaiser = game.addChild(new KaiserPlayer(0x4169E1)); kaiser.x = 1024; kaiser.y = 900; kaiser.homeX = 1024; kaiser.homeY = 900; opponents.push(kaiser); // 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, ball kicking, and pass request 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)); var teammateClicked = null; // Check if a teammate was tapped (for pass request) var teammates = [chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < teammates.length; i++) { var mate = teammates[i]; if (!mate) continue; var dist = Math.sqrt((x - mate.x) * (x - mate.x) + (y - mate.y) * (y - mate.y)); if (dist < 90) { // Touch radius for teammate teammateClicked = mate; break; } } if (teammateClicked && !player.hasBall) { // Request pass from teammate teammateClicked.passRequested = true; // Show a quick visual indicator if (!teammateClicked.passRequestText) { teammateClicked.passRequestText = teammateClicked.addChild(new Text2('PAS!', { size: 36, fill: 0xFFFF00 })); teammateClicked.passRequestText.anchor.set(0.5, 0.5); teammateClicked.passRequestText.x = 0; teammateClicked.passRequestText.y = -90; teammateClicked.passRequestText.alpha = 1; tween(teammateClicked.passRequestText, { alpha: 0 }, { duration: 900, onFinish: function onFinish() { if (teammateClicked.passRequestText) { teammateClicked.passRequestText.destroy(); teammateClicked.passRequestText = null; } } }); } return; // Don't move player or start shot if pass requested } 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; } }; // --- PASS LOGIC for player and teammates --- // Each teammate will check if passRequested is set and if so, pass to player if possible function teammatePassLogic(teammate) { if (!teammate || typeof teammate.hasBall === "undefined") return; if (teammate.hasBall && teammate.passRequested) { // Find the teammate (including player) closest to the opponent goal var allTeammates = [player, chigiri, hiori, reo, nagi, bachira]; var closestMate = null; var closestDist = Infinity; for (var i = 0; i < allTeammates.length; i++) { var mate = allTeammates[i]; if (!mate || mate === teammate) continue; // Use distance to opponentGoal as metric var dGoal = Math.sqrt((mate.x - opponentGoal.x) * (mate.x - opponentGoal.x) + (mate.y - opponentGoal.y) * (mate.y - opponentGoal.y)); if (dGoal < closestDist) { closestDist = dGoal; closestMate = mate; } } // If no other teammate found, fallback to player if (!closestMate) closestMate = player; // Pass to the closest teammate to the opponent goal var dx = closestMate.x - ball.x; var dy = closestMate.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { teammate.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } teammate.passRequested = false; } } // 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; } // Game update loop game.update = function () { // 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) 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) { 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]; var ballThreatLevel = Math.sqrt((ball.x - 1024) * (ball.x - 1024) + (ball.y - 1366) * (ball.y - 1366)); // Adjust opponent aggression based on ball threat if (ballThreatLevel < 300) { // Ball is in center - all opponents become more aggressive if (opponent.role === 'defense') { opponent.speed = 4.5; opponent.maxDistance = 500; } else if (opponent.role === 'midfield') { opponent.speed = 3.5; } else if (opponent.role === 'forward') { opponent.speed = 4.2; } } else { // Ball is far - return to normal behavior if (opponent.role === 'defense') { opponent.speed = 4; opponent.maxDistance = 400; } else if (opponent.role === 'midfield') { opponent.speed = 3; } else if (opponent.role === 'forward') { opponent.speed = 3.5; } } } // --- PASS LOGIC for teammates and player --- // Teammates pass to player if requested var teammates = [chigiri, hiori, reo, nagi, bachira]; for (var i = 0; i < teammates.length; i++) { teammatePassLogic(teammates[i]); } // Player can pass to a tapped teammate if has ball and not charging shot if (player.hasBall && !isChargingShot) { // Find all teammates requesting a pass var requestingTeammates = []; for (var i = 0; i < teammates.length; i++) { var mate = teammates[i]; if (mate && mate.passRequested) { requestingTeammates.push(mate); } } if (requestingTeammates.length > 0) { // Pass to the requesting teammate closest to the opponent goal var closestMate = null; var closestDist = Infinity; for (var i = 0; i < requestingTeammates.length; i++) { var mate = requestingTeammates[i]; var dGoal = Math.sqrt((mate.x - opponentGoal.x) * (mate.x - opponentGoal.x) + (mate.y - opponentGoal.y) * (mate.y - opponentGoal.y)); if (dGoal < closestDist) { closestDist = dGoal; closestMate = mate; } } if (closestMate) { var dx = closestMate.x - ball.x; var dy = closestMate.y - ball.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { player.hasBall = false; ball.active = true; ballDetachCooldown = ballDetachCooldownTime; ball.velocityX = dx / dist * 10; ball.velocityY = dy / dist * 10; LK.getSound('kick').play(); } // Clear all pass requests for (var i = 0; i < requestingTeammates.length; i++) { requestingTeammates[i].passRequested = false; } } } } // Role-based AI is now handled in each player's update method // Defense, Midfield, and Forward players have their own specialized behaviors };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var AikuPlayer = 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 = 4.2;
self.role = 'aiku';
self.homeX = 1024;
self.homeY = 500;
self.hasBall = false;
// Metavision abilities
self.metavisionActive = false;
self.metavisionCooldown = 0;
self.metavisionCooldownTime = 15000; // 15 seconds
self.metavisionDuration = 5000; // 5 seconds active
self.metavisionEndTime = 0;
self.interceptedPasses = 0;
// Defensive wall ability
self.defensiveWallActive = false;
self.defensiveWallCooldown = 0;
self.defensiveWallCooldownTime = 20000; // 20 seconds
self.defensiveWallDuration = 8000; // 8 seconds
self.defensiveWallEndTime = 0;
self.defensiveWallRadius = 200;
self.defensiveWallVisual = null;
self.update = function () {
// Update metavision cooldown
if (self.metavisionCooldown > 0) {
self.metavisionCooldown -= 16;
if (self.metavisionCooldown < 0) self.metavisionCooldown = 0;
}
// Update defensive wall cooldown
if (self.defensiveWallCooldown > 0) {
self.defensiveWallCooldown -= 16;
if (self.defensiveWallCooldown < 0) self.defensiveWallCooldown = 0;
}
// Handle metavision end
if (self.metavisionActive && Date.now() > self.metavisionEndTime) {
self.metavisionActive = false;
}
// Handle defensive wall end
if (self.defensiveWallActive && Date.now() > self.defensiveWallEndTime) {
self.defensiveWallActive = false;
if (self.defensiveWallVisual) {
self.defensiveWallVisual.destroy();
self.defensiveWallVisual = null;
}
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Activate metavision when enemy players approach
var enemyNearby = false;
var playerTeam = [player, chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (enemyDist < 300) {
enemyNearby = true;
break;
}
}
if (enemyNearby && !self.metavisionActive && self.metavisionCooldown <= 0) {
self.metavisionActive = true;
self.metavisionCooldown = self.metavisionCooldownTime;
self.metavisionEndTime = Date.now() + self.metavisionDuration;
// Visual effect for metavision
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00FFFF
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Intercept passes with metavision
if (self.metavisionActive && ball.active && distance < 150) {
// High chance to intercept the ball
if (Math.random() < 0.8) {
// 80% chance with metavision
ball.velocityX = 0;
ball.velocityY = 0;
ball.x = self.x;
ball.y = self.y;
ball.active = false;
self.hasBall = true;
self.interceptedPasses++;
// Clear ball from all players
for (var i = 0; i < playerTeam.length; i++) {
if (playerTeam[i] && typeof playerTeam[i].hasBall !== "undefined") {
playerTeam[i].hasBall = false;
}
}
}
}
// Activate defensive wall when multiple enemies nearby
var nearbyEnemies = 0;
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (enemyDist < self.defensiveWallRadius) {
nearbyEnemies++;
}
}
if (nearbyEnemies >= 2 && !self.defensiveWallActive && self.defensiveWallCooldown <= 0) {
self.defensiveWallActive = true;
self.defensiveWallCooldown = self.defensiveWallCooldownTime;
self.defensiveWallEndTime = Date.now() + self.defensiveWallDuration;
// Create defensive wall visual
self.defensiveWallVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4,
tint: 0xFF4500
}));
self.defensiveWallVisual.alpha = 0.4;
// Slow down nearby enemies
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (enemyDist < self.defensiveWallRadius) {
if (!playerTeam[i].originalSpeed) playerTeam[i].originalSpeed = playerTeam[i].speed;
playerTeam[i].speed = playerTeam[i].speed * 0.4; // 40% speed
}
}
}
// Apply defensive wall effect
if (self.defensiveWallActive) {
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (enemyDist < self.defensiveWallRadius) {
if (!playerTeam[i].originalSpeed) playerTeam[i].originalSpeed = playerTeam[i].speed;
playerTeam[i].speed = playerTeam[i].speed * 0.4; // Keep at 40% speed
} else if (playerTeam[i].originalSpeed) {
playerTeam[i].speed = playerTeam[i].originalSpeed;
playerTeam[i].originalSpeed = null;
}
}
} else {
// Restore speeds when defensive wall is not active
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i] || !playerTeam[i].originalSpeed) continue;
playerTeam[i].speed = playerTeam[i].originalSpeed;
playerTeam[i].originalSpeed = null;
}
}
// Normal defensive behavior
if (distance < 400) {
var chaseSpeed = self.metavisionActive ? self.speed * 1.5 : self.speed * 1.1;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
if (distance < 80) {
var clearX = ball.x < 1024 ? ball.x - 400 : ball.x + 400;
var clearY = ball.y - 300;
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 * 12;
ball.velocityY = clearDy / clearDist * 12;
}
}
} 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;
}
}
};
// Add shootBall method
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
return self;
});
var BarouPlayer = 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 = 'barou';
self.homeX = 1024;
self.homeY = 1200;
self.hasBall = false;
// Predator Eye abilities
self.predatorEyeActive = false;
self.predatorEyeCooldown = 0;
self.predatorEyeCooldownTime = 12000; // 12 seconds
self.predatorEyeDuration = 6000; // 6 seconds active
self.predatorEyeEndTime = 0;
self.predatorEyeTargets = [];
// Feint abilities
self.feintCooldown = 0;
self.feintCooldownTime = 8000; // 8 seconds
self.feintActive = false;
self.feintStunRadius = 120;
self.feintStunDuration = 2500; // 2.5 seconds
self.feintStunnedOpponents = [];
self.feintStunEnd = 0;
// King Shot variables
self.kingShotCooldown = 0;
self.kingShotCooldownTime = 15000; // 15 seconds
self.kingShotCharging = false;
self.kingShotChargeTime = 0;
self.kingShotMaxCharge = 2000; // 2 seconds charge
self.update = function () {
// Update cooldowns
if (self.predatorEyeCooldown > 0) {
self.predatorEyeCooldown -= 16;
if (self.predatorEyeCooldown < 0) self.predatorEyeCooldown = 0;
}
if (self.feintCooldown > 0) {
self.feintCooldown -= 16;
if (self.feintCooldown < 0) self.feintCooldown = 0;
}
if (self.kingShotCooldown > 0) {
self.kingShotCooldown -= 16;
if (self.kingShotCooldown < 0) self.kingShotCooldown = 0;
}
// Handle predator eye end
if (self.predatorEyeActive && Date.now() > self.predatorEyeEndTime) {
self.predatorEyeActive = false;
self.predatorEyeTargets = [];
}
// Handle feint stun end
if (self.feintStunEnd > 0 && Date.now() > self.feintStunEnd) {
for (var i = 0; i < self.feintStunnedOpponents.length; i++) {
var opponent = self.feintStunnedOpponents[i];
opponent.stunned = false;
if (opponent.originalSpeed) opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.feintStunnedOpponents = [];
self.feintStunEnd = 0;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Activate Predator Eye when near enemy goal
var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y));
if (goalDist < 400 && !self.predatorEyeActive && self.predatorEyeCooldown <= 0) {
self.predatorEyeActive = true;
self.predatorEyeCooldown = self.predatorEyeCooldownTime;
self.predatorEyeEndTime = Date.now() + self.predatorEyeDuration;
// Mark nearby player team as targets
var playerTeam = [player, chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var targetDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (targetDist < 300) {
self.predatorEyeTargets.push(playerTeam[i]);
// Visual effect on targets
tween(playerTeam[i], {
tint: 0xFF0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(playerTeam[i], {
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
}
// Visual effect for predator eye activation
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFF4500
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Aggressive ball pursuit
if (distance < 500) {
var pursuitSpeed = self.predatorEyeActive ? self.speed * 1.3 : self.speed * 1.1;
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
if (distance < 80) {
// Use feint if available
if (!self.feintActive && self.feintCooldown <= 0) {
self.feintActive = true;
self.feintCooldown = self.feintCooldownTime;
// Stun nearby player team members
var playerTeam = [player, chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var stunDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (stunDist <= self.feintStunRadius) {
playerTeam[i].stunned = true;
playerTeam[i].originalSpeed = playerTeam[i].speed;
playerTeam[i].speed = 0;
self.feintStunnedOpponents.push(playerTeam[i]);
tween(playerTeam[i], {
tint: 0xFFFF00,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250
});
}
}
self.feintStunEnd = Date.now() + self.feintStunDuration;
self.feintActive = false;
}
// King Shot if near goal
if (goalDist < 300 && self.kingShotCooldown <= 0) {
self.kingShotCharging = true;
self.kingShotChargeTime = Date.now();
self.kingShotCooldown = self.kingShotCooldownTime;
// Visual charging effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0xFFD700
}, {
duration: self.kingShotMaxCharge,
onFinish: function onFinish() {
// Execute King 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 * 25; // Extremely powerful
ball.velocityY = goalDy / goalDistance * 25;
ball.active = true;
}
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
self.kingShotCharging = false;
}
});
} else {
// Normal 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) {
ball.velocityX = goalDx / goalDistance * 16;
ball.velocityY = goalDy / goalDistance * 16;
ball.active = true;
}
}
}
} else {
// Return to 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.6;
self.y += homeDy / homeDistance * self.speed * 0.6;
}
}
};
// Add shootBall method
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
return self;
});
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 () {
// 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;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Enhanced ball pursuit for hybrid player
if (distance < 500 && !self.isSpeedBursting) {
// Chase ball with hybrid intelligence
var chaseSpeed = distance < 150 ? self.normalSpeed * 1.4 : self.normalSpeed * 1.1;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
}
// 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;
// Attack opponent goal if close to ball
if (distance < 80) {
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
var goalAngle = Math.atan2(goalDy, goalDx);
var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x);
var angleDiff = Math.abs(goalAngle - ballAngle);
// Check if goal is within vision range (45 degrees) and shooting distance (500 pixels)
if (goalDistance < 500 && angleDiff < Math.PI / 4) {
// Use Shot skill toward opponent goal
self.Shot(opponentGoal.x, opponentGoal.y, 14);
} else {
// Normal ball control toward goal
var ballGoalDx = opponentGoal.x - ball.x;
var ballGoalDy = opponentGoal.y - ball.y;
var ballGoalDistance = Math.sqrt(ballGoalDx * ballGoalDx + ballGoalDy * ballGoalDy);
if (ballGoalDistance > 0) {
ball.velocityX = ballGoalDx / ballGoalDistance * 11;
ball.velocityY = ballGoalDy / ballGoalDistance * 11;
}
}
}
} 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;
}
}
}
};
// Add shootBall method to ChigiriPlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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 = 4;
self.role = 'defense';
self.homeX = 1024;
self.homeY = 600;
self.maxDistance = 400;
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);
// Defense priority: desperately try to get the ball
if (distance < 400) {
// Move aggressively toward ball - increased chase range
self.x += dx / distance * (self.speed * 1.2);
self.y += dy / distance * (self.speed * 1.2);
// 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 * slideDy);
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;
}
}
}
// Check if opponent goal is within shooting range and use Shot skill
if (distance < 80) {
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
var goalAngle = Math.atan2(goalDy, goalDx);
var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x);
var angleDiff = Math.abs(goalAngle - ballAngle);
// Check if goal is within vision range (60 degrees) and shooting distance (600 pixels)
if (goalDistance < 600 && angleDiff < Math.PI / 3) {
// Use Shot skill toward opponent goal
self.Shot(opponentGoal.x, opponentGoal.y, 15);
} else {
// 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;
}
}
};
// Add shootBall method to DefensePlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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 = 3.5;
self.role = 'forward';
self.homeX = 1024;
self.homeY = 1400;
self.hasBall = false;
self.directShotCooldown = self.directShotCooldown || 0;
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 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;
}
// 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
if (distance < 400) {
// Move toward ball with aggressive pursuit
var pursuitSpeed = distance < 200 ? self.speed * 1.8 : self.speed * 1.3;
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
// Check if opponent goal is within shooting range and use Shot skill
if (distance < 80) {
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
var goalAngle = Math.atan2(goalDy, goalDx);
var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x);
var angleDiff = Math.abs(goalAngle - ballAngle);
// Check if goal is within vision range (45 degrees) and shooting distance (400 pixels)
if (goalDistance < 400 && angleDiff < Math.PI / 4) {
// Direct Shot every 30s
if (self.directShotCooldown <= 0) {
// Use Shot skill with high power
self.Shot(opponentGoal.x, opponentGoal.y, 18);
self.directShotCooldown = 30000; // 30 seconds
} else {
// Use normal Shot skill
self.Shot(opponentGoal.x, opponentGoal.y, 10);
}
} else {
// No clear shot to goal, move toward goal normally
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 * 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;
}
}
};
// Add shootBall method to ForwardPlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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
ball.velocityX = 0;
ball.velocityY = 0;
ball.x = self.x;
ball.y = self.y;
ball.active = false;
player.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 () {
// 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;
}
// 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 but check for goal proximity
if (distance < 100) {
// Check if too close to opponent goal while dribbling
var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y));
if (goalDist < 250) {
// Too close to goal, use Shot skill instead of continuing dribble
self.Shot(opponentGoal.x, opponentGoal.y, 15);
self.isDribbling = false;
} else {
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 {
// Normal ball pursuit
if (distance < 400) {
var chaseSpeed = distance < 150 ? self.speed * 1.3 : self.speed * 1.1;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
// 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
});
}
});
}
}
// FLOW: If in Flow, perfect pass always goes directly to player, no curve, and is instant
if (distance < 80 && 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
});
}
});
}
}
}
// 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;
}
}
};
// Add shootBall method to HioriPlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
return self;
});
var KaiserPlayer = 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.2;
self.role = 'kaiser';
self.homeX = 1024;
self.homeY = 900;
self.hasBall = false;
// Kaiser Impact variables
self.kaiserImpactCooldown = 0;
self.kaiserImpactCooldownTime = 12000; // 12 seconds
self.kaiserImpactCharging = false;
self.kaiserImpactChargeTime = 0;
self.kaiserImpactMaxCharge = 1500; // 1.5 seconds charge
// Magnus Flow variables
self.magnusFlowActive = false;
self.magnusFlowCooldown = 0;
self.magnusFlowCooldownTime = 20000; // 20 seconds
self.magnusFlowDuration = 10000; // 10 seconds active
self.magnusFlowEndTime = 0;
self.magnusFlowShotCount = 0;
self.magnusFlowMaxShots = 3; // 3 special shots during Magnus Flow
// Emperor Eye variables
self.emperorEyeActive = false;
self.emperorEyeCooldown = 0;
self.emperorEyeCooldownTime = 15000; // 15 seconds
self.emperorEyeDuration = 7000; // 7 seconds
self.emperorEyeEndTime = 0;
self.emperorEyeRange = 300;
self.controlledOpponents = [];
self.update = function () {
// Update cooldowns
if (self.kaiserImpactCooldown > 0) {
self.kaiserImpactCooldown -= 16;
if (self.kaiserImpactCooldown < 0) self.kaiserImpactCooldown = 0;
}
if (self.magnusFlowCooldown > 0) {
self.magnusFlowCooldown -= 16;
if (self.magnusFlowCooldown < 0) self.magnusFlowCooldown = 0;
}
if (self.emperorEyeCooldown > 0) {
self.emperorEyeCooldown -= 16;
if (self.emperorEyeCooldown < 0) self.emperorEyeCooldown = 0;
}
// Handle Magnus Flow end
if (self.magnusFlowActive && Date.now() > self.magnusFlowEndTime) {
self.magnusFlowActive = false;
self.magnusFlowShotCount = 0;
}
// Handle Emperor Eye end
if (self.emperorEyeActive && Date.now() > self.emperorEyeEndTime) {
self.emperorEyeActive = false;
// Release controlled opponents
for (var i = 0; i < self.controlledOpponents.length; i++) {
var opponent = self.controlledOpponents[i];
opponent.controlled = false;
tween(opponent, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
self.controlledOpponents = [];
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Activate Emperor Eye when multiple enemies nearby
var playerTeam = [player, chigiri, hiori, reo, nagi, bachira];
var nearbyEnemies = 0;
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (enemyDist < self.emperorEyeRange) {
nearbyEnemies++;
}
}
if (nearbyEnemies >= 2 && !self.emperorEyeActive && self.emperorEyeCooldown <= 0) {
self.emperorEyeActive = true;
self.emperorEyeCooldown = self.emperorEyeCooldownTime;
self.emperorEyeEndTime = Date.now() + self.emperorEyeDuration;
// Control nearby enemies
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (enemyDist < self.emperorEyeRange) {
playerTeam[i].controlled = true;
self.controlledOpponents.push(playerTeam[i]);
tween(playerTeam[i], {
tint: 0xFFD700
}, {
duration: 300
});
}
}
// Visual effect for Emperor Eye
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFFD700
}, {
duration: 400,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Activate Magnus Flow when in attacking position
var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y));
if (goalDist < 500 && !self.magnusFlowActive && self.magnusFlowCooldown <= 0 && distance < 120) {
self.magnusFlowActive = true;
self.magnusFlowCooldown = self.magnusFlowCooldownTime;
self.magnusFlowEndTime = Date.now() + self.magnusFlowDuration;
self.magnusFlowShotCount = 0;
// Visual effect for Magnus Flow activation
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0x4169E1
}, {
duration: 500,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Ball pursuit with enhanced abilities
if (distance < 500) {
var pursuitSpeed = self.emperorEyeActive ? self.speed * 1.3 : self.speed * 1.1;
if (self.magnusFlowActive) pursuitSpeed *= 1.2; // Additional bonus during Magnus Flow
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
if (distance < 80) {
// Choose shot type based on active abilities
if (self.magnusFlowActive && self.magnusFlowShotCount < self.magnusFlowMaxShots) {
// Magnus Flow: Kaiser Impact with curve
self.magnusFlowShotCount++;
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
// Magnus effect: powerful shot with extreme curve
var magnusCurve = 15 + Math.random() * 10; // Strong curve
ball.velocityX = goalDx / goalDistance * 24 + magnusCurve;
ball.velocityY = goalDy / goalDistance * 24;
ball.active = true;
}
// Visual effect for Magnus Flow shot
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0x4169E1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
} else if (self.kaiserImpactCooldown <= 0) {
// Normal Kaiser Impact
self.kaiserImpactCharging = true;
self.kaiserImpactChargeTime = Date.now();
self.kaiserImpactCooldown = self.kaiserImpactCooldownTime;
// Visual charging effect
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFF4500
}, {
duration: self.kaiserImpactMaxCharge,
onFinish: function onFinish() {
// Execute Kaiser Impact
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 * 20; // Very powerful
ball.velocityY = goalDy / goalDistance * 20;
ball.active = true;
}
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
self.kaiserImpactCharging = false;
}
});
} else {
// Normal 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) {
ball.velocityX = goalDx / goalDistance * 16;
ball.velocityY = goalDy / goalDistance * 16;
ball.active = true;
}
}
}
} else {
// Return to attacking 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.7;
self.y += homeDy / homeDistance * self.speed * 0.7;
}
}
};
// Add shootBall method
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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 () {
// 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
// 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 but check for goal proximity
if (distance < 100) {
// Check if too close to opponent goal while in roulette
var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y));
if (goalDist < 250) {
// Too close to goal, use Shot skill instead of continuing roulette
self.Shot(opponentGoal.x, opponentGoal.y, 18);
self.rouletteActive = false;
} else {
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 && distance < 400) {
// Normal ball pursuit
var chaseSpeed = distance < 150 ? self.speed * 1.3 : self.speed * 1.1;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
// 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 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() + 3000; // 3 seconds 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() + 4000; // 4 seconds 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
});
}
});
};
// Add shootBall method to MeguruBachiraPlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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 = 3;
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 () {
// 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);
// Midfield: get ball and pass to forward
if (distance < 350) {
// Move toward ball with increased speed when chasing
var chaseSpeed = distance < 150 ? self.speed * 1.5 : self.speed * 1.2;
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 but check for goal proximity
if (distance < 80) {
// Check if too close to player goal while dribbling
var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y));
if (goalDist < 250) {
// Too close to goal, use Shot skill instead of continuing dribble
self.Shot(playerGoal.x, playerGoal.y, 12);
self.isDribbling = false;
self.hasDribbled = true;
} else {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
}
// When dribble completes, check goal vision before shooting
if (t >= 1 && !self.hasDribbled) {
self.hasDribbled = true;
self.isDribbling = false;
// Check if opponent goal is within vision and range
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
var goalAngle = Math.atan2(goalDy, goalDx);
var ballAngle = Math.atan2(ball.y - self.y, ball.x - self.x);
var angleDiff = Math.abs(goalAngle - ballAngle);
// Check if goal is within vision range (60 degrees) and shooting distance (500 pixels)
if (goalDistance < 500 && angleDiff < Math.PI / 3) {
// Use Shot skill with curve
self.Shot(opponentGoal.x, opponentGoal.y, 12);
} else {
// 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;
}
}
};
// Add shootBall method to MidfieldPlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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 = 4;
self.role = 'nagi';
self.homeX = 1400;
self.homeY = 2000;
self.hasBall = false;
// --- FLOW SYSTEM for Nagi ---
self.flowActive = false;
self.flowTriggerChecked = false;
self.flowVolleyCount = 0;
self.flowVolleyMax = 5; // 5 Revolver Fake Volleys in Flow
self.flowText = null;
// Fake Volley variables
self.fakeVolleyState = 0; // 0: ready, 1: fake shot done, 2: real shot done
self.fakeVolleyTarget = null;
self.fakeVolleyStunRadius = 120;
self.fakeVolleyStunDuration = 2000; // 2 seconds
self.fakeVolleyStunnedOpponents = [];
self.fakeVolleyStunEnd = 0;
// 5 Staged Revolver Volley variables
self.egoIgnited = false;
self.revolverVolleyActive = false;
self.revolverVolleyStage = 0; // 0-4 stages
self.revolverVolleyCount = 0;
self.revolverVolleyStunCount = 0;
self.revolverVolleyStunRadius = 180;
self.revolverVolleyStunDuration = 3000; // 3 seconds per stun
self.revolverVolleyStunnedOpponents = [];
self.revolverVolleyStunEnd = 0;
self.revolverVolleyText = null;
self.thankYouText = null;
// Fake Attack variables
self.fakeAttackCooldown = 0;
self.fakeAttackCooldownTime = 5000; // Base 5 seconds, will be randomized
self.fakeAttackStunRadius = 140;
self.fakeAttackStunDuration = 0; // Will be randomized 5-10 seconds
self.fakeAttackStunnedOpponents = [];
self.fakeAttackStunEnd = 0;
self.fakeAttackChance = 0.3; // 30% chance for shots to be fake
self.fakeVolleyVisual = null;
// Ball pull variables
self.ballPullRange = 150;
self.ballPullActive = false;
self.ballPullVisual = null;
self.ballPullCooldown = 0;
self.ballPullCooldownTime = 5000; // 5 seconds
// Ball control text
self.ballControlText = null;
self.update = function () {
// --- FLOW TRIGGER: If 2 goals scored by Nagi's team, activate Flow for random teammate (including Nagi) ---
if (!self.flowActive && !self.flowTriggerChecked && typeof playerScore !== "undefined" && playerScore >= 2) {
self.flowTriggerChecked = true;
// Pick a random teammate (Nagi, Hiori, Chigiri, Reo) to enter Flow
var teammates = [];
if (typeof nagi !== "undefined") teammates.push(nagi);
if (typeof hiori !== "undefined") teammates.push(hiori);
if (typeof chigiri !== "undefined") teammates.push(chigiri);
if (typeof reo !== "undefined") teammates.push(reo);
if (teammates.length > 0) {
var idx = Math.floor(Math.random() * teammates.length);
var chosen = teammates[idx];
if (chosen && typeof chosen.flowActive !== "undefined") {
chosen.flowActive = true;
chosen.flowVolleyCount = 0;
// Show Flow text above chosen
if (!chosen.flowText) {
chosen.flowText = chosen.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
chosen.flowText.anchor.set(0.5, 0.5);
chosen.flowText.x = 0;
chosen.flowText.y = -100;
chosen.flowText.alpha = 1;
tween(chosen.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(chosen.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;
}
// --- Nagi Unstoppable Shot Cooldown Logic ---
if (typeof self.nagiUnstoppableCooldown === "undefined") {
self.nagiUnstoppableCooldown = 0;
self.nagiUnstoppableCooldownTime = 150000; // 150 seconds in ms
self.nagiUnstoppableLastShot = 0;
self.nagiUnstoppableActive = false;
self.nagiUnstoppableText = null;
}
// Reo's Ego Ignition - Check if close to Nagi and activate his Flow
if (typeof nagi !== "undefined") {
// Block Ego ignition if Nagi is in unstoppable cooldown
if (nagi.nagiUnstoppableCooldown > Date.now()) {
// Optionally, show a quick message that Nagi is in cooldown
if (!nagi.nagiUnstoppableText) {
nagi.nagiUnstoppableText = game.addChild(new Text2('Nagi Cooldown: ' + Math.ceil((nagi.nagiUnstoppableCooldown - Date.now()) / 1000) + 's', {
size: 48,
fill: 0xFF0000
}));
nagi.nagiUnstoppableText.anchor.set(0.5, 0.5);
nagi.nagiUnstoppableText.x = 1024;
nagi.nagiUnstoppableText.y = 400;
nagi.nagiUnstoppableText.alpha = 0.9;
tween(nagi.nagiUnstoppableText, {
alpha: 0
}, {
duration: 1200,
onFinish: function onFinish() {
if (nagi.nagiUnstoppableText) {
nagi.nagiUnstoppableText.destroy();
nagi.nagiUnstoppableText = null;
}
}
});
}
} else {
var nagiDist = Math.sqrt((nagi.x - self.x) * (nagi.x - self.x) + (nagi.y - self.y) * (nagi.y - self.y));
var ballToNagiDist = Math.sqrt((ball.x - nagi.x) * (ball.x - nagi.x) + (ball.y - nagi.y) * (ball.y - nagi.y));
// If Reo is close to Nagi and ball is near Nagi, ignite Nagi's Ego
if (nagiDist < 200 && ballToNagiDist < 150 && !nagi.revolverVolleyActive && !nagi.egoIgnited) {
// Reo ignites Nagi's Ego
nagi.egoIgnited = true;
nagi.revolverVolleyActive = true;
nagi.revolverVolleyStage = 0;
nagi.revolverVolleyCount = 0;
nagi.revolverVolleyStunCount = 0;
// Visual effect for Ego ignition
tween(nagi, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0x00FFFF
}, {
duration: 500,
onFinish: function onFinish() {
tween(nagi, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
// Show Ego ignition text
var egoText = nagi.addChild(new Text2('EGO IGNITED!', {
size: 60,
fill: 0xFF0000
}));
egoText.anchor.set(0.5, 0.5);
egoText.x = 0;
egoText.y = -120;
tween(egoText, {
alpha: 0,
y: -180
}, {
duration: 2000,
onFinish: function onFinish() {
egoText.destroy();
}
});
LK.getSound('ego_ignition').play();
}
}
}
// --- Nagi Unstoppable Shot Trigger ---
if (self.hasBall && !self.nagiUnstoppableActive && self.nagiUnstoppableCooldown <= Date.now() && typeof opponentGoal !== "undefined") {
// If Nagi is close enough to the opponent goal, fire the unstoppable shot
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDist = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDist < 400) {
// Show special unstoppable shot text
var unstoppableText = game.addChild(new Text2('Yıkıcı Şut!', {
size: 90,
fill: 0xFF0000
}));
unstoppableText.anchor.set(0.5, 0.5);
unstoppableText.x = 1024;
unstoppableText.y = 500;
unstoppableText.alpha = 1;
tween(unstoppableText, {
alpha: 0
}, {
duration: 1800,
onFinish: function onFinish() {
unstoppableText.destroy();
}
});
// Visual effect for Nagi
tween(self, {
scaleX: 1.7,
scaleY: 1.7,
tint: 0xFFFFFF
}, {
duration: 400,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0x9966ff
}, {
duration: 400
});
}
});
// Unstoppable shot: max power, cannot be blocked, ball is unstoppable for 2s
ball.x = self.x;
ball.y = self.y - 40;
ball.velocityX = goalDx / goalDist * 40;
ball.velocityY = goalDy / goalDist * 40;
ball.active = true;
self.hasBall = false;
ball.unstoppableUntil = Date.now() + 2000;
self.nagiUnstoppableActive = true;
self.nagiUnstoppableCooldown = Date.now() + self.nagiUnstoppableCooldownTime;
// End unstoppable state after 2s
LK.setTimeout(function () {
self.nagiUnstoppableActive = false;
}, 2000);
}
}
// --- Nagi Cooldown Restriction: Only allow stealing during cooldown ---
if (self.nagiUnstoppableCooldown > Date.now()) {
// If Nagi is in cooldown, prevent all actions except for stealing the ball from opponents
// Stealing logic is already handled below (Ball Control section)
// Prevent all other actions by returning early
return;
}
// Update cooldowns
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;
}
if (self.ballPullCooldown > 0) {
self.ballPullCooldown -= 16;
if (self.ballPullCooldown < 0) self.ballPullCooldown = 0;
}
// Update Fake Attack cooldown
if (self.fakeAttackCooldown > 0) {
self.fakeAttackCooldown -= 16;
if (self.fakeAttackCooldown < 0) self.fakeAttackCooldown = 0;
}
// Handle Fake Attack stun end
if (self.fakeAttackStunEnd > 0 && Date.now() > self.fakeAttackStunEnd) {
// Restore stunned opponents from Fake Attack
for (var i = 0; i < self.fakeAttackStunnedOpponents.length; i++) {
var opponent = self.fakeAttackStunnedOpponents[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.fakeAttackStunnedOpponents = [];
self.fakeAttackStunEnd = 0;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Nagi Ball Control: Steal from opponent if close and not immune
if (!self.nagiStealImmunity) self.nagiStealImmunity = 0;
var canSteal = true;
if (self.nagiStealImmunity > Date.now()) canSteal = false;
// Check for opponent with ball in Ball Control range
var stoleFromOpponent = false;
if (canSteal) {
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 (opp.hasBall && oppDist < 120) {
// Steal the ball from opponent
opp.hasBall = false;
ball.active = true;
// Move ball to Nagi's feet
ball.velocityX = 0;
ball.velocityY = 0;
tween(ball, {
x: self.x,
y: self.y + 40
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
ball.active = false;
self.hasBall = true;
}
});
// Show "Ball Control" text
if (!self.ballControlText) {
self.ballControlText = self.addChild(new Text2('Ball Control', {
size: 40,
fill: 0xFFFFFF
}));
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;
}
}
});
}, 2000);
}
});
}
// Set 3s immunity for Nagi after stealing
self.nagiStealImmunity = Date.now() + 3000;
stoleFromOpponent = true;
break;
}
}
}
// If not stealing from opponent, allow normal ball control from loose ball
if (!stoleFromOpponent && distance < 120 && ball.active && !self.hasBall && canSteal) {
ball.velocityX = 0;
ball.velocityY = 0;
tween(ball, {
x: self.x,
y: self.y + 40
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
ball.active = false;
self.hasBall = true;
}
});
if (!self.ballControlText) {
self.ballControlText = self.addChild(new Text2('Ball Control', {
size: 40,
fill: 0xFFFFFF
}));
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;
}
}
});
}, 2000);
}
});
}
}
// --- 5 STAGED REVOLVER VOLLEY ---
// Ensure Nagi always gets the ball for the 5 Staged Revolver Fake Volley
if (self.revolverVolleyActive && self.egoIgnited) {
// If Nagi does not have the ball, forcibly take control of the ball
var nagiToBallDist = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y));
if (!self.hasBall && nagiToBallDist > 10) {
// Instantly move ball to Nagi's feet and stop it
ball.x = self.x;
ball.y = self.y + 40;
ball.velocityX = 0;
ball.velocityY = 0;
ball.active = false;
self.hasBall = true;
// Remove ball from any other player or opponent
if (typeof player !== "undefined") player.hasBall = false;
if (typeof chigiri !== "undefined") chigiri.hasBall = false;
if (typeof hiori !== "undefined") hiori.hasBall = false;
if (typeof reo !== "undefined") reo.hasBall = false;
if (typeof bachira !== "undefined") bachira.hasBall = false;
for (var i = 0; i < opponents.length; i++) {
if (opponents[i] && typeof opponents[i].hasBall !== "undefined") opponents[i].hasBall = false;
}
}
// Move Nagi away from crowd before starting - more complex positioning
if (self.revolverVolleyStage === 0) {
var crowdNearby = false;
var allCharacters = [player, chigiri, hiori, reo, bachira];
var crowdCenter = {
x: 0,
y: 0,
count: 0
};
for (var i = 0; i < opponents.length; i++) {
allCharacters.push(opponents[i]);
}
// Check if crowd is nearby and calculate crowd center
for (var i = 0; i < allCharacters.length; i++) {
if (!allCharacters[i] || allCharacters[i] === self) continue;
var charDist = Math.sqrt((allCharacters[i].x - self.x) * (allCharacters[i].x - self.x) + (allCharacters[i].y - self.y) * (allCharacters[i].y - self.y));
if (charDist < 300) {
// Increased detection radius for better positioning
crowdNearby = true;
crowdCenter.x += allCharacters[i].x;
crowdCenter.y += allCharacters[i].y;
crowdCenter.count++;
}
}
// Move away from crowd to clear space with intelligent positioning
if (crowdNearby && crowdCenter.count > 0) {
// Calculate average position of nearby crowd
crowdCenter.x /= crowdCenter.count;
crowdCenter.y /= crowdCenter.count;
// Find direction away from crowd center
var awayDx = self.x - crowdCenter.x;
var awayDy = self.y - crowdCenter.y;
var awayDist = Math.sqrt(awayDx * awayDx + awayDy * awayDy);
// If too close to crowd center, move in a random direction
if (awayDist < 50) {
var randomAngle = Math.random() * Math.PI * 2;
awayDx = Math.cos(randomAngle);
awayDy = Math.sin(randomAngle);
awayDist = 1;
}
if (awayDist > 0) {
// Calculate clear position - move 250 pixels away from crowd center
var clearDistance = 250;
var clearX = self.x + awayDx / awayDist * clearDistance;
var clearY = self.y + awayDy / awayDist * clearDistance;
// Keep within field bounds
clearX = Math.max(200, Math.min(1848, clearX));
clearY = Math.max(400, Math.min(2300, clearY));
// Smooth movement using tween for more elegant positioning
var targetDx = clearX - self.x;
var targetDy = clearY - self.y;
var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDist > 30) {
// Only move if significant distance
// Use tween for smooth, professional movement
tween(self, {
x: clearX,
y: clearY
}, {
duration: 800,
easing: tween.easeOut
});
// Also smoothly move ball with Nagi
if (self.hasBall) {
tween(ball, {
x: clearX,
y: clearY + 40
}, {
duration: 800,
easing: tween.easeOut
});
}
// Visual effect to show Nagi is positioning
tween(self, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0x00FFFF
}, {
duration: 400,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 400
});
}
});
return; // Wait until positioning is complete
}
}
}
// Show positioning message first, then "teşekkürler Reo..." when properly positioned
if (!self.thankYouText) {
// First show positioning message
var positioningText = game.addChild(new Text2('Pozisyon alıyorum...', {
size: 56,
fill: 0xFFFF00
}));
positioningText.anchor.set(0.5, 0.5);
positioningText.x = 1024; // Center of screen
positioningText.y = 700; // Upper middle of screen
positioningText.alpha = 0;
// Animate positioning message
tween(positioningText, {
alpha: 1,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 500,
onFinish: function onFinish() {
// Keep visible for 1.5 seconds then fade out
LK.setTimeout(function () {
tween(positioningText, {
alpha: 0,
scaleX: 0.9,
scaleY: 0.9
}, {
duration: 400,
onFinish: function onFinish() {
positioningText.destroy();
// Now show the thank you message
self.thankYouText = game.addChild(new Text2('teşekkürler Reo...', {
size: 72,
fill: 0x00FFFF
}));
self.thankYouText.anchor.set(0.5, 0.5);
self.thankYouText.x = 1024; // Center of screen
self.thankYouText.y = 800; // Middle of screen
self.thankYouText.alpha = 0;
// Animate thank you message appearance
if (self.thankYouText) {
tween(self.thankYouText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
onFinish: function onFinish() {
// Keep message visible for 2 seconds then fade out
LK.setTimeout(function () {
if (self.thankYouText) {
tween(self.thankYouText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 600,
onFinish: function onFinish() {
if (self.thankYouText) {
self.thankYouText.destroy();
self.thankYouText = null;
}
}
});
}
}, 2000);
}
});
}
}
});
}, 1500);
}
});
}
}
if (distance < 100) {
// Stage progression: Each stage stuns opponents and increases power
if (self.revolverVolleyStage < 5) {
self.revolverVolleyStage++;
self.revolverVolleyCount++;
// Show stage number on screen for stages 1-4, and "5." for final stage
var stageText = null;
if (self.revolverVolleyStage < 5) {
// Show stage number for fake volleys
stageText = game.addChild(new Text2(self.revolverVolleyStage + '..', {
size: 120,
fill: 0xFF00FF
}));
} else {
// Show "5." for final shot
stageText = game.addChild(new Text2('5.', {
size: 150,
fill: 0xFF0000
}));
}
stageText.anchor.set(0.5, 0.5);
stageText.x = 1024; // Center of screen
stageText.y = 600; // Upper middle of screen
stageText.alpha = 0;
// Animate stage text
tween(stageText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
onFinish: function onFinish() {
// Keep visible for 1 second then fade
LK.setTimeout(function () {
tween(stageText, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
onFinish: function onFinish() {
stageText.destroy();
}
});
}, 1000);
}
});
// Update revolver volley text above Nagi
if (!self.revolverVolleyText) {
self.revolverVolleyText = self.addChild(new Text2('REVOLVER VOLLEY STAGE ' + self.revolverVolleyStage, {
size: 40,
fill: 0xFF00FF
}));
self.revolverVolleyText.anchor.set(0.5, 0.5);
self.revolverVolleyText.x = 0;
self.revolverVolleyText.y = -140;
} else {
self.revolverVolleyText.setText('REVOLVER VOLLEY STAGE ' + self.revolverVolleyStage);
}
// For stages 1-4, completely stop ALL opponents on the field
if (self.revolverVolleyStage <= 4) {
// Stop ALL opponents completely during fake volleys
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
if (!opponent.stunned) {
opponent.stunned = true;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = 0; // Completely stop them
self.revolverVolleyStunnedOpponents.push(opponent);
self.revolverVolleyStunCount++;
// Visual effect for stunned opponent
tween(opponent, {
tint: 0xFF00FF,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300
});
}
}
} else {
// Stage 5: Stun all opponents in expanding radius
var stageStunRadius = self.revolverVolleyStunRadius + self.revolverVolleyStage * 50;
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 <= stageStunRadius) {
opponentsInRange.push(opponent);
}
}
// Apply stunning effect
for (var i = 0; i < opponentsInRange.length; i++) {
var opponent = opponentsInRange[i];
if (!opponent.stunned) {
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
self.revolverVolleyStunnedOpponents.push(opponent);
self.revolverVolleyStunCount++;
// Visual effect for stunned opponent
tween(opponent, {
tint: 0xFF00FF,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300
});
}
}
}
// Visual effect for each stage
var stageColors = [0xFF0000, 0xFF4500, 0xFFFF00, 0x00FF00, 0x00FFFF];
tween(self, {
scaleX: 1.3 + self.revolverVolleyStage * 0.1,
scaleY: 1.3 + self.revolverVolleyStage * 0.1,
tint: stageColors[self.revolverVolleyStage - 1] || 0xFFFFFF
}, {
duration: 400,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
self.revolverVolleyStunEnd = Date.now() + self.revolverVolleyStunDuration;
// Stage 5: Final devastating shot
if (self.revolverVolleyStage === 5) {
// Show Turkish awakening message before final shot
var awakeningText = game.addChild(new Text2('Artık uyandım... Bu benim gerçek gücüm!', {
size: 72,
fill: 0xFF0000
}));
awakeningText.anchor.set(0.5, 0.5);
awakeningText.x = 1024; // Center of screen
awakeningText.y = 900; // Lower middle of screen
awakeningText.alpha = 0;
// Animate awakening message
tween(awakeningText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 800,
onFinish: function onFinish() {
// Keep message visible for 2 seconds then fade out
LK.setTimeout(function () {
tween(awakeningText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 600,
onFinish: function onFinish() {
awakeningText.destroy();
// Now perform the final shot after message
// Create screen shake effect
tween(game, {
x: game.x + 20
}, {
duration: 100,
onFinish: function onFinish() {
tween(game, {
x: game.x - 40
}, {
duration: 100,
onFinish: function onFinish() {
tween(game, {
x: game.x + 20
}, {
duration: 100
});
}
});
}
});
// Devastating shot toward goal (UNSTOPPABLE)
// Always aim at the center of the opponent's goal for the final shot
var targetGoalX = opponentGoal.x;
var targetGoalY = opponentGoal.y;
var goalDx = targetGoalX - self.x;
var goalDy = targetGoalY - self.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
// Unstoppable: ignore all blocks, max power, and ball cannot be stopped until goal
ball.x = self.x;
ball.y = self.y - 40;
ball.velocityX = goalDx / goalDistance * 48; // Dramatically higher velocity for 5th stage
ball.velocityY = goalDy / goalDistance * 48;
ball.active = true;
self.hasBall = false;
// Mark ball as "unstoppable" for a short time (cannot be stopped by anyone)
ball.unstoppableUntil = Date.now() + 1200;
}
LK.getSound('revolver_volley').play();
}
});
}, 2000);
}
});
// Reset after completing 5 stages
LK.setTimeout(function () {
self.revolverVolleyActive = false;
self.egoIgnited = false;
self.revolverVolleyStage = 0;
self.revolverVolleyCount = 0;
self.revolverVolleyStunCount = 0;
if (self.revolverVolleyText) {
self.revolverVolleyText.destroy();
self.revolverVolleyText = null;
}
}, 4000); //{r7} // Extended timing for message
} else {
// For stages 1-4, perform fake shots with increasing intensity
ball.velocityX = (Math.random() - 0.5) * (2 + self.revolverVolleyStage);
ball.velocityY = (Math.random() - 0.5) * (2 + self.revolverVolleyStage);
ball.active = true;
}
}
}
// Prevent normal volley logic during Revolver Volley
return;
}
// Handle Revolver Volley stun restoration
if (self.revolverVolleyStunEnd > 0 && Date.now() > self.revolverVolleyStunEnd) {
for (var i = 0; i < self.revolverVolleyStunnedOpponents.length; i++) {
var opponent = self.revolverVolleyStunnedOpponents[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.revolverVolleyStunnedOpponents = [];
self.revolverVolleyStunEnd = 0;
}
// --- FLOW: 5 Revolver Fake Volley logic ---
if (self.flowActive) {
// If close to ball, perform up to 5 chained fake volleys (fake+real) in a row
if (distance < 80 && self.flowVolleyCount < self.flowVolleyMax && self.fakeVolleyState === 0) {
self.fakeVolleyState = 1;
self.flowVolleyCount++;
// Stun nearby opponents
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.1,
scaleY: 1.1
}, {
duration: 200
});
}
self.fakeVolleyStunEnd = Date.now() + self.fakeVolleyStunDuration;
// Fake shot - ball barely moves
ball.velocityX = (Math.random() - 0.5) * 2;
ball.velocityY = (Math.random() - 0.5) * 2;
ball.active = true;
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFFFF00
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
} else if (self.flowActive && self.fakeVolleyState === 1) {
// After fake, do real volley
self.fakeVolleyState = 2;
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 * 16;
ball.velocityY = goalDy / goalDistance * 16;
ball.active = true;
}
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFF0000
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
// Reset fake volley state after a short delay, and chain next if not reached max
LK.setTimeout(function () {
self.fakeVolleyState = 0;
// If not reached 5, chain next volley automatically (simulate rapid-fire)
if (self.flowActive && self.flowVolleyCount < self.flowVolleyMax) {
// Move Nagi slightly forward for next volley
self.x += (opponentGoal.x - self.x) * 0.08;
self.y += (opponentGoal.y - self.y) * 0.08;
} else if (self.flowActive && self.flowVolleyCount >= self.flowVolleyMax) {
// End Flow after 5 volleys
self.flowActive = false;
self.flowVolleyCount = 0;
if (self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
}
}, 500);
}
// Prevent normal volley logic during Flow
return;
}
// After stealing, for 3s, Nagi cannot be stolen from and focuses on goal
if (self.hasBall && self.nagiStealImmunity > Date.now()) {
// Nagi focuses on goal: move toward opponent goal and shoot if close
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDist = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDist > 0) {
self.x += goalDx / goalDist * self.speed * 1.2;
self.y += goalDy / goalDist * self.speed * 1.2;
// If close to goal, shoot
if (goalDist < 200) {
ball.active = true;
ball.velocityX = goalDx / goalDist * 16;
ball.velocityY = goalDy / goalDist * 16;
self.hasBall = false;
// Remove immunity after shot
self.nagiStealImmunity = 0;
}
}
} else {
// If Nagi doesn't have ball, always move to a position close to the opponent's goal
// Find a spot within 200px of the goal center, but not inside the goal
var goalAreaRadius = 200;
var angle = Math.PI / 2 + Math.sin(Date.now() / 1200); // oscillate angle for realism
var targetX = opponentGoal.x + goalAreaRadius * Math.cos(angle);
var targetY = opponentGoal.y + 120 + goalAreaRadius * Math.abs(Math.sin(angle)); // always above the goal line
// Clamp to field bounds
targetX = Math.max(200, Math.min(1848, targetX));
targetY = Math.max(200, Math.min(600, targetY)); // keep Nagi near the top, but not inside the goal
var moveDx = targetX - self.x;
var moveDy = targetY - self.y;
var moveDist = Math.sqrt(moveDx * moveDx + moveDy * moveDy);
if (moveDist > 10) {
self.x += moveDx / moveDist * self.speed * 0.8;
self.y += moveDy / moveDist * self.speed * 0.8;
}
}
// Ball pull ability - show range when ball is within pull range
if (distance <= self.ballPullRange && !self.ballPullActive && self.ballPullCooldown <= 0) {
if (!self.ballPullVisual) {
// Create ball pull range visual
self.ballPullVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 3,
tint: 0x00FF00
}));
self.ballPullVisual.alpha = 0.2;
}
// Pull ball towards self
if (distance > 30) {
self.ballPullActive = true;
var pullForce = 8;
ball.velocityX = -dx / distance * pullForce;
ball.velocityY = -dy / distance * pullForce;
ball.active = true;
self.ballPullCooldown = self.ballPullCooldownTime;
// Visual effect for ball pull
tween(self, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00FF00
}, {
duration: 150,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 150
});
}
});
}
} else {
// Remove ball pull visual if ball is out of range
if (self.ballPullVisual) {
self.ballPullVisual.destroy();
self.ballPullVisual = null;
}
self.ballPullActive = false;
}
// Normal ball pursuit
if (distance < 400) {
var chaseSpeed = distance < 150 ? self.speed * 1.2 : self.speed * 1.0;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
// Volley ability when close to ball with Fake Attack chance
if (distance < 80) {
// Check if this should be a Fake Attack
var shouldFakeAttack = self.fakeAttackCooldown <= 0 && Math.random() < self.fakeAttackChance;
if (shouldFakeAttack) {
// FAKE ATTACK - stun opponents and barely move ball
// Find opponents within Fake Attack stun 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.fakeAttackStunRadius) {
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.fakeAttackStunnedOpponents.push(opponent);
// Visual effect for stunned opponent
tween(opponent, {
tint: 0xFF6600,
// Orange tint for Fake Attack
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250
});
}
// Randomize stun duration (5-10 seconds)
self.fakeAttackStunDuration = 5000 + Math.random() * 5000;
self.fakeAttackStunEnd = Date.now() + self.fakeAttackStunDuration;
// Randomize next cooldown (5-10 seconds)
self.fakeAttackCooldownTime = 5000 + Math.random() * 5000;
self.fakeAttackCooldown = self.fakeAttackCooldownTime;
// Fake attack - ball barely moves
ball.velocityX = (Math.random() - 0.5) * 3;
ball.velocityY = (Math.random() - 0.5) * 3;
ball.active = true;
// Visual effect for Fake Attack
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFF6600 // Orange for Fake Attack
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
} else if (self.fakeVolleyState === 0) {
// First shot - FAKE VOLLEY
self.fakeVolleyState = 1;
// Find opponents within stun 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);
}
}
// 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.fakeVolleyStunnedOpponents.push(opponent);
// Visual effect for stunned opponent
tween(opponent, {
tint: 0xFFFF00,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200
});
}
self.fakeVolleyStunEnd = Date.now() + self.fakeVolleyStunDuration;
// Fake shot - ball barely moves to show it's fake
ball.velocityX = (Math.random() - 0.5) * 2;
ball.velocityY = (Math.random() - 0.5) * 2;
ball.active = true;
// Visual effect for fake shot
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFFFF00
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
} else if (self.fakeVolleyState === 1) {
// Second shot - REAL VOLLEY
self.fakeVolleyState = 2;
// Real powerful shot toward opponent 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 * 16;
ball.velocityY = goalDy / goalDistance * 16;
ball.active = true;
}
// Visual effect for real shot
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFF0000
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
// Reset fake volley state after a delay
LK.setTimeout(function () {
self.fakeVolleyState = 0;
}, 3000);
}
}
} 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.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
// Add shootBall method to NagiPlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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;
// Add shootBall method to Player
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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 () {
// 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
if (self.isDefensiveMode) {
// Defensive mode: more active interception and ball pursuit
if (distance < 500) {
var defensiveSpeed = self.speed * 1.1;
self.x += dx / distance * defensiveSpeed;
self.y += dy / distance * defensiveSpeed;
// 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 (distance < 700) {
var attackSpeed = self.speed * 1.7;
self.x += dx / distance * attackSpeed;
self.y += dy / distance * attackSpeed;
// Attack shot
if (distance < 80) {
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;
}
}
} 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
});
}
});
}
};
// Add shootBall method to ReoPlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
return self;
});
var RinPlayer = 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 = 4.8;
self.role = 'rin';
self.homeX = 1024;
self.homeY = 1000;
self.hasBall = false;
// Destroyer mode abilities
self.destroyerModeActive = false;
self.destroyerModeCooldown = 0;
self.destroyerModeCooldownTime = 18000; // 18 seconds
self.destroyerModeDuration = 8000; // 8 seconds active
self.destroyerModeEndTime = 0;
self.destroyerModeTargets = [];
// Precision shot abilities
self.precisionShotCooldown = 0;
self.precisionShotCooldownTime = 10000; // 10 seconds
self.precisionShotActive = false;
// Spatial awareness abilities
self.spatialAwarenessActive = false;
self.spatialAwarenessCooldown = 0;
self.spatialAwarenessCooldownTime = 15000; // 15 seconds
self.spatialAwarenessDuration = 6000; // 6 seconds
self.spatialAwarenessEndTime = 0;
self.spatialAwarenessRadius = 250;
self.update = function () {
// Update cooldowns
if (self.destroyerModeCooldown > 0) {
self.destroyerModeCooldown -= 16;
if (self.destroyerModeCooldown < 0) self.destroyerModeCooldown = 0;
}
if (self.precisionShotCooldown > 0) {
self.precisionShotCooldown -= 16;
if (self.precisionShotCooldown < 0) self.precisionShotCooldown = 0;
}
if (self.spatialAwarenessCooldown > 0) {
self.spatialAwarenessCooldown -= 16;
if (self.spatialAwarenessCooldown < 0) self.spatialAwarenessCooldown = 0;
}
// Handle destroyer mode end
if (self.destroyerModeActive && Date.now() > self.destroyerModeEndTime) {
self.destroyerModeActive = false;
// Restore target speeds
for (var i = 0; i < self.destroyerModeTargets.length; i++) {
var target = self.destroyerModeTargets[i];
if (target.originalSpeed) target.speed = target.originalSpeed;
tween(target, {
tint: 0xFFFFFF
}, {
duration: 300
});
}
self.destroyerModeTargets = [];
}
// Handle spatial awareness end
if (self.spatialAwarenessActive && Date.now() > self.spatialAwarenessEndTime) {
self.spatialAwarenessActive = false;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Activate spatial awareness when enemies approach
var playerTeam = [player, chigiri, hiori, reo, nagi, bachira];
var nearbyEnemies = 0;
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var enemyDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (enemyDist < self.spatialAwarenessRadius) {
nearbyEnemies++;
}
}
if (nearbyEnemies >= 2 && !self.spatialAwarenessActive && self.spatialAwarenessCooldown <= 0) {
self.spatialAwarenessActive = true;
self.spatialAwarenessCooldown = self.spatialAwarenessCooldownTime;
self.spatialAwarenessEndTime = Date.now() + self.spatialAwarenessDuration;
// Visual effect
tween(self, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x800080
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Activate destroyer mode when frustrated (low ball control)
if (!self.destroyerModeActive && self.destroyerModeCooldown <= 0 && distance > 200) {
self.destroyerModeActive = true;
self.destroyerModeCooldown = self.destroyerModeCooldownTime;
self.destroyerModeEndTime = Date.now() + self.destroyerModeDuration;
// Target all nearby player team members
for (var i = 0; i < playerTeam.length; i++) {
if (!playerTeam[i]) continue;
var targetDist = Math.sqrt((playerTeam[i].x - self.x) * (playerTeam[i].x - self.x) + (playerTeam[i].y - self.y) * (playerTeam[i].y - self.y));
if (targetDist < 350) {
self.destroyerModeTargets.push(playerTeam[i]);
playerTeam[i].originalSpeed = playerTeam[i].speed;
playerTeam[i].speed = playerTeam[i].speed * 0.6; // 60% speed
tween(playerTeam[i], {
tint: 0x8B0000
}, {
duration: 300
});
}
}
// Visual effect for destroyer mode
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0x8B0000
}, {
duration: 500,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Ball pursuit with enhanced abilities
if (distance < 500) {
var pursuitSpeed = self.destroyerModeActive ? self.speed * 1.4 : self.speed * 1.2;
if (self.spatialAwarenessActive) pursuitSpeed *= 1.2; // Additional bonus
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
if (distance < 80) {
// Precision shot if available
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance < 400 && self.precisionShotCooldown <= 0) {
self.precisionShotActive = true;
self.precisionShotCooldown = self.precisionShotCooldownTime;
// Precision shot - extremely accurate and powerful
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 22; // High power
ball.velocityY = goalDy / goalDistance * 22;
ball.active = true;
}
// Visual effect for precision shot
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x00CED1
}, {
duration: 400,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
self.precisionShotActive = false;
} else {
// Normal shot toward goal
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 15;
ball.velocityY = goalDy / goalDistance * 15;
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.6;
self.y += homeDy / homeDistance * self.speed * 0.6;
}
}
};
// Add shootBall method
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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 = 4.5;
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 () {
// 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 but check for goal proximity
if (distance < 80) {
// Check if too close to player goal while dribbling
var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y));
if (goalDist < 250) {
// Too close to goal, use Shot skill instead of continuing dribble
self.Shot(playerGoal.x, playerGoal.y, 16);
self.isDribbling = false;
} else {
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 but check for goal proximity
if (distance < 100) {
// Check if too close to player goal while zigzag dribbling
var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y));
if (goalDist < 250) {
// Too close to goal, use Shot skill instead of continuing dribble
self.Shot(playerGoal.x, playerGoal.y, 16);
self.isZigzagDribbling = false;
} else {
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 but check for goal proximity
if (distance < 100) {
// Check if too close to player goal while chop dribbling
var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y));
if (goalDist < 250) {
// Too close to goal, use Shot skill instead of continuing dribble
self.Shot(playerGoal.x, playerGoal.y, 16);
self.isChopDribbling = false;
} else {
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 but check for goal proximity
if (distance < 120) {
// Check if too close to player goal while hilal dribbling
var goalDist = Math.sqrt((playerGoal.x - self.x) * (playerGoal.x - self.x) + (playerGoal.y - self.y) * (playerGoal.y - self.y));
if (goalDist < 250) {
// Too close to goal, use Shot skill instead of continuing dribble
self.Shot(playerGoal.x, playerGoal.y, 20);
self.flowHilalDribbling = false;
self.flowHilalShotDone = true;
} else {
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 aggressively but stay in attacking areas
if (distance < 500) {
// 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;
// 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.4 : self.speed * 1.2;
// 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 * 15;
ball.velocityY = passDy / passDist * 15;
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 = 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;
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 = opponentGoal.x - ball.x;
var goalDy = opponentGoal.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;
}
}
};
};
// Add shootBall method to SaePlayer
self.shootBall = function (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);
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
};
// Add Shot skill
self.Shot = function (targetX, targetY, power) {
self.shootBall(targetX, targetY, power);
};
// Add Pass skill
self.Pass = function (targetPlayer) {
if (!targetPlayer) return;
var dx = targetPlayer.x - ball.x;
var dy = targetPlayer.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
self.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
};
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.update = function () {
if (self.active) {
self.x += self.velocityX;
self.y += self.velocityY;
self.velocityX *= self.friction;
self.velocityY *= self.friction;
// 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);
// Forward player
var forwardPlayer = game.addChild(new ForwardPlayer());
forwardPlayer.x = 1024;
forwardPlayer.y = 1400;
forwardPlayer.homeX = 1024;
forwardPlayer.homeY = 1400;
opponents.push(forwardPlayer);
// 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);
// Aiku - opponent team defensive leader with Metavision
var aiku = game.addChild(new AikuPlayer(0x2E8B57));
aiku.x = 1024;
aiku.y = 500;
aiku.homeX = 1024;
aiku.homeY = 500;
opponents.push(aiku);
// Barou - opponent team predator striker
var barou = game.addChild(new BarouPlayer(0x8B0000));
barou.x = 1024;
barou.y = 1200;
barou.homeX = 1024;
barou.homeY = 1200;
opponents.push(barou);
// Rin - opponent team destroyer midfielder
var rin = game.addChild(new RinPlayer(0x800080));
rin.x = 1024;
rin.y = 1000;
rin.homeX = 1024;
rin.homeY = 1000;
opponents.push(rin);
// Kaiser - opponent team emperor striker with dual shot abilities
var kaiser = game.addChild(new KaiserPlayer(0x4169E1));
kaiser.x = 1024;
kaiser.y = 900;
kaiser.homeX = 1024;
kaiser.homeY = 900;
opponents.push(kaiser);
// 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, ball kicking, and pass request
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));
var teammateClicked = null;
// Check if a teammate was tapped (for pass request)
var teammates = [chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < teammates.length; i++) {
var mate = teammates[i];
if (!mate) continue;
var dist = Math.sqrt((x - mate.x) * (x - mate.x) + (y - mate.y) * (y - mate.y));
if (dist < 90) {
// Touch radius for teammate
teammateClicked = mate;
break;
}
}
if (teammateClicked && !player.hasBall) {
// Request pass from teammate
teammateClicked.passRequested = true;
// Show a quick visual indicator
if (!teammateClicked.passRequestText) {
teammateClicked.passRequestText = teammateClicked.addChild(new Text2('PAS!', {
size: 36,
fill: 0xFFFF00
}));
teammateClicked.passRequestText.anchor.set(0.5, 0.5);
teammateClicked.passRequestText.x = 0;
teammateClicked.passRequestText.y = -90;
teammateClicked.passRequestText.alpha = 1;
tween(teammateClicked.passRequestText, {
alpha: 0
}, {
duration: 900,
onFinish: function onFinish() {
if (teammateClicked.passRequestText) {
teammateClicked.passRequestText.destroy();
teammateClicked.passRequestText = null;
}
}
});
}
return; // Don't move player or start shot if pass requested
}
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;
}
};
// --- PASS LOGIC for player and teammates ---
// Each teammate will check if passRequested is set and if so, pass to player if possible
function teammatePassLogic(teammate) {
if (!teammate || typeof teammate.hasBall === "undefined") return;
if (teammate.hasBall && teammate.passRequested) {
// Find the teammate (including player) closest to the opponent goal
var allTeammates = [player, chigiri, hiori, reo, nagi, bachira];
var closestMate = null;
var closestDist = Infinity;
for (var i = 0; i < allTeammates.length; i++) {
var mate = allTeammates[i];
if (!mate || mate === teammate) continue;
// Use distance to opponentGoal as metric
var dGoal = Math.sqrt((mate.x - opponentGoal.x) * (mate.x - opponentGoal.x) + (mate.y - opponentGoal.y) * (mate.y - opponentGoal.y));
if (dGoal < closestDist) {
closestDist = dGoal;
closestMate = mate;
}
}
// If no other teammate found, fallback to player
if (!closestMate) closestMate = player;
// Pass to the closest teammate to the opponent goal
var dx = closestMate.x - ball.x;
var dy = closestMate.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
teammate.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
teammate.passRequested = false;
}
}
// 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;
}
// Game update loop
game.update = function () {
// 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)
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) {
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];
var ballThreatLevel = Math.sqrt((ball.x - 1024) * (ball.x - 1024) + (ball.y - 1366) * (ball.y - 1366));
// Adjust opponent aggression based on ball threat
if (ballThreatLevel < 300) {
// Ball is in center - all opponents become more aggressive
if (opponent.role === 'defense') {
opponent.speed = 4.5;
opponent.maxDistance = 500;
} else if (opponent.role === 'midfield') {
opponent.speed = 3.5;
} else if (opponent.role === 'forward') {
opponent.speed = 4.2;
}
} else {
// Ball is far - return to normal behavior
if (opponent.role === 'defense') {
opponent.speed = 4;
opponent.maxDistance = 400;
} else if (opponent.role === 'midfield') {
opponent.speed = 3;
} else if (opponent.role === 'forward') {
opponent.speed = 3.5;
}
}
}
// --- PASS LOGIC for teammates and player ---
// Teammates pass to player if requested
var teammates = [chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < teammates.length; i++) {
teammatePassLogic(teammates[i]);
}
// Player can pass to a tapped teammate if has ball and not charging shot
if (player.hasBall && !isChargingShot) {
// Find all teammates requesting a pass
var requestingTeammates = [];
for (var i = 0; i < teammates.length; i++) {
var mate = teammates[i];
if (mate && mate.passRequested) {
requestingTeammates.push(mate);
}
}
if (requestingTeammates.length > 0) {
// Pass to the requesting teammate closest to the opponent goal
var closestMate = null;
var closestDist = Infinity;
for (var i = 0; i < requestingTeammates.length; i++) {
var mate = requestingTeammates[i];
var dGoal = Math.sqrt((mate.x - opponentGoal.x) * (mate.x - opponentGoal.x) + (mate.y - opponentGoal.y) * (mate.y - opponentGoal.y));
if (dGoal < closestDist) {
closestDist = dGoal;
closestMate = mate;
}
}
if (closestMate) {
var dx = closestMate.x - ball.x;
var dy = closestMate.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
player.hasBall = false;
ball.active = true;
ballDetachCooldown = ballDetachCooldownTime;
ball.velocityX = dx / dist * 10;
ball.velocityY = dy / dist * 10;
LK.getSound('kick').play();
}
// Clear all pass requests
for (var i = 0; i < requestingTeammates.length; i++) {
requestingTeammates[i].passRequested = false;
}
}
}
}
// Role-based AI is now handled in each player's update method
// Defense, Midfield, and Forward players have their own specialized behaviors
};