User prompt
oyundaki tüm karakterleri stabilize et ve normal oynamaya geri dönsünler titriyorlar ve haraket etmiyorlar doğru dürüst
User prompt
Nagi yi forvet olarak ayarla
User prompt
Nagi Forvet gibi oynasın yani forvet olarka ayarla
User prompt
Make Nagi Rework !
User prompt
Please fix the bug: 'TypeError: self.performFakeVolley is not a function' in or related to this line: 'self.performFakeVolley();' Line Number: 2886
User prompt
Please fix the bug: 'TypeError: self.performFakeVolley is not a function' in or related to this line: 'self.performFakeVolley();' Line Number: 2886
User prompt
Please fix the bug: 'TypeError: self.performFakeVolley is not a function' in or related to this line: 'self.performFakeVolley();' Line Number: 2887
User prompt
Please fix the bug: 'TypeError: self.performFakeVolley is not a function' in or related to this line: 'self.performFakeVolley();' Line Number: 2886
User prompt
Please fix the bug: 'TypeError: self.performFakeVolley is not a function' in or related to this line: 'self.performFakeVolley();' Line Number: 2886
User prompt
Please fix the bug: 'TypeError: self.performFakeVolley is not a function' in or related to this line: 'self.performFakeVolley();' Line Number: 2886
User prompt
Please fix the bug: 'TypeError: self.activateEffortlessPlay is not a function' in or related to this line: 'self.activateEffortlessPlay();' Line Number: 2802
User prompt
Nagi Rework yap
User prompt
he bide kaleciler topu tuttukları zaman ki oyuncuların ayağından zorla alabilsinler topu attıklarında top durana kadar temas edilemez olsun
User prompt
bizim takımdakiler topu alıp Dribbling e başlarlarsa topu çaldıkları rakip 2 Saniye Stun yesin Dribblingle geçtikleri 1 saniye Stun yiyip haraket edemesin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
itip kakıp rakip oyun önündeki oyuncuyu kaleye sokamasın ya çalsın topu yada top rakip oyuncudaysa çalım atsın yani Dribble
User prompt
topu olanlar geçsin 5 Saniyede bir hepsi kendine özel bir Dribbling ile ya Stun yada yavaşlatma yada Devoured verip ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Rakipleri geçemiyorlar Rakiple 1 e 1 de direk çalımla geçme ekle ve oyuncular topun hizasına göre orta sahada veya işte dursunlar !
User prompt
oyuncuların yapay zekasını geliştir pas atma taktik kurma yetenekleri ekle
User prompt
Rakip oyuncuları daha güçsüzleştir
User prompt
Please fix the bug: 'ReferenceError: ballCarrier is not defined' in or related to this line: 'var shouldAttack = ballY < 1500 || ballCarrier && ballCarrier.role === 'striker';' Line Number: 4273
User prompt
Please fix the bug: 'ReferenceError: ballCarrier is not defined' in or related to this line: 'var shouldAttack = ballY < 1500 || ballCarrier && ballCarrier.role === 'striker';' Line Number: 4273
User prompt
Please fix the bug: 'ReferenceError: ballCarrier is not defined' in or related to this line: 'var shouldAttack = ballY < 1500 || ballCarrier && ballCarrier.role === 'striker';' Line Number: 4273
User prompt
Karakterler düzenli paslaşsınlar ve görev bölümü yapsınlar mesela kanattan ilerlerken bazı oyuncuları 1 2 kişi topla birlikte haraket etsin gibi gibi pas verme ve şut atma ekle haraketere karşı kaleye yakınsalar ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
ve asla takılmasınlar birbirlerine her karaktere kendilerine özgü bir Dribbling ve top çalma haraketi ekle mesela Hiori topu süreni durdurup topu alıp en yakındaki Forvet e pas atsın Nagi ise Aerial Strike! vursun topa kısaca bizim gibi şut çekebilsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
asla iç içe giremesinler ve agresif derken mesela 2 kişi Topa gideceği zaman diyerleri kanatlarda veya defans da dursun yani birbirlerinin hamlelerini okusunlar ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ChigiriPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && opponentGraphics) {
opponentGraphics.tint = colorOverride;
}
self.speed = 5;
self.role = 'chigiri';
self.homeX = 800;
self.homeY = 2000;
self.maxDistance = 600;
self.hasBall = false;
self.isSpeedBursting = false;
self.speedBurstTarget = null;
self.speedBurstCooldown = 0;
self.speedBurstCooldownTime = 8000; // 8 seconds
self.normalSpeed = 5;
self.burstSpeed = 12;
self.stamina = 100;
self.maxStamina = 100;
self.staminaDrainRate = 0.8;
self.staminaRegenRate = 0.3;
self.lowStaminaSpeed = 1.5; // Very slow when stamina is low
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, hiori, reo, nagi, bachira, chigiri];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Update speed burst cooldown
if (self.speedBurstCooldown > 0) {
self.speedBurstCooldown -= 16;
if (self.speedBurstCooldown < 0) self.speedBurstCooldown = 0;
}
// Steal ball if opponent is inside area
var nearestOpponent = null;
var nearestDistance = Infinity;
for (var i = 0; i < opponents.length; i++) {
var opponentDist = Math.sqrt((opponents[i].x - self.x) * (opponents[i].x - self.x) + (opponents[i].y - self.y) * (opponents[i].y - self.y));
if (opponentDist < nearestDistance) {
nearestDistance = opponentDist;
nearestOpponent = opponents[i];
}
}
if (nearestOpponent && nearestDistance < 80 && nearestOpponent.hasBall) {
// Steal the ball
nearestOpponent.hasBall = false;
ball.active = true;
// Ball moves toward Chigiri
var stealDx = self.x - nearestOpponent.x;
var stealDy = self.y - nearestOpponent.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 8;
ball.velocityY = stealDy / stealDist * 8;
}
ballDetachCooldown = ballDetachCooldownTime;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Coordinated ball pursuit - only chase if designated as active pursuer
if (self.coordinatedRole === 'pursuer' && distance < 800 && !self.isSpeedBursting) {
// Chase ball with hybrid intelligence - increased range and speed
var chaseSpeed = distance < 150 ? self.normalSpeed * 1.8 : self.normalSpeed * 1.5;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (self.coordinatedRole !== 'pursuer' && !self.isSpeedBursting) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.normalSpeed * 0.8;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
}
// Speed burst mechanic
if (!self.isSpeedBursting && self.speedBurstCooldown <= 0 && distance < 100) {
// Throw ball forward and start speed burst
var throwX = opponentGoal.x + (Math.random() - 0.5) * 200;
var throwY = opponentGoal.y + 200;
var throwDx = throwX - ball.x;
var throwDy = throwY - ball.y;
var throwDist = Math.sqrt(throwDx * throwDx + throwDy * throwDy);
if (throwDist > 0) {
ball.velocityX = throwDx / throwDist * 14;
ball.velocityY = throwDy / throwDist * 14;
}
// Set speed burst target and activate
self.speedBurstTarget = {
x: throwX,
y: throwY
};
self.isSpeedBursting = true;
self.speedBurstCooldown = self.speedBurstCooldownTime; // Start cooldown
// Visual effect for speed burst
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
}
if (self.isSpeedBursting && self.speedBurstTarget) {
// Move at burst speed toward target
var burstDx = self.speedBurstTarget.x - self.x;
var burstDy = self.speedBurstTarget.y - self.y;
var burstDist = Math.sqrt(burstDx * burstDx + burstDy * burstDy);
if (burstDist > 30) {
self.x += burstDx / burstDist * self.burstSpeed;
self.y += burstDy / burstDist * self.burstSpeed;
// Drain stamina during speed burst
self.stamina -= self.staminaDrainRate * 2; // Double drain during burst
if (self.stamina < 0) self.stamina = 0;
} else {
// Reached target, stop speed burst
self.isSpeedBursting = false;
self.speedBurstTarget = null;
}
} else {
// Stamina regeneration when not moving much
var currentSpeed = Math.sqrt((self.x - (self.lastMoveX || self.x)) * (self.x - (self.lastMoveX || self.x)) + (self.y - (self.lastMoveY || self.y)) * (self.y - (self.lastMoveY || self.y)));
if (currentSpeed < 2 && self.stamina < self.maxStamina) {
self.stamina += self.staminaRegenRate;
if (self.stamina > self.maxStamina) self.stamina = self.maxStamina;
}
self.lastMoveX = self.x;
self.lastMoveY = self.y;
// Determine current speed based on stamina
var currentMoveSpeed = self.stamina > 20 ? self.normalSpeed : self.lowStaminaSpeed;
// Normal hybrid behavior - enhanced ball pursuit
if (distance < 500) {
// Move toward ball with improved midfielder intelligence but adjust for stamina
var pursuitSpeed = distance < 200 ? currentMoveSpeed * 1.3 : currentMoveSpeed * 1.1;
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
// Drain stamina during pursuit
self.stamina -= self.staminaDrainRate * 0.5;
if (self.stamina < 0) self.stamina = 0;
// Coordinated strategic play when close to ball
if (distance < 80) {
// Try strategic passing first
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic pass executed
} else {
// Fallback to individual decision making
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [hiori, reo, nagi, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 30% chance to pass if teammate available, otherwise shoot
if (nearestTeammate && Math.random() < 0.3) {
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 10;
ball.velocityY = passDy / passDistance * 10;
}
} else {
// Shoot at goal
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 11;
ball.velocityY = goalDy / goalDistance * 11;
}
}
}
}
// Move to tactical position when not actively involved with ball
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 0.6;
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else {
// Return to defensive position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
// Use stamina-adjusted speed for returning home
var currentMoveSpeed = self.stamina > 20 ? self.normalSpeed : self.lowStaminaSpeed;
self.x += homeDx / homeDistance * currentMoveSpeed * 0.4;
self.y += homeDy / homeDistance * currentMoveSpeed * 0.4;
// Light stamina drain when returning to position
self.stamina -= self.staminaDrainRate * 0.3;
if (self.stamina < 0) self.stamina = 0;
}
}
}
};
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;
}
}
}
// Kick ball away from player goal if close enough
if (distance < 80) {
// Find nearest forward player
var nearestForward = null;
var nearestDistance = Infinity;
for (var i = 0; i < opponents.length; i++) {
if (opponents[i].role === 'forward') {
var forwardDx = opponents[i].x - self.x;
var forwardDy = opponents[i].y - self.y;
var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy);
if (forwardDistance < nearestDistance) {
nearestDistance = forwardDistance;
nearestForward = opponents[i];
}
}
}
// Pass to forward if found, otherwise clear as before
if (nearestForward) {
var passDx = nearestForward.x - ball.x;
var passDy = nearestForward.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 10;
ball.velocityY = passDy / passDistance * 10;
}
} else {
var kickAwayX = ball.x < 1024 ? ball.x - 200 : ball.x + 200;
var kickAwayY = ball.y - 300; // Kick upfield
var kickDx = kickAwayX - ball.x;
var kickDy = kickAwayY - ball.y;
var kickDistance = Math.sqrt(kickDx * kickDx + kickDy * kickDy);
if (kickDistance > 0) {
ball.velocityX = kickDx / kickDistance * 8;
ball.velocityY = kickDy / kickDistance * 8;
}
}
}
} else {
// Return to defensive position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.5;
self.y += homeDy / homeDistance * self.speed * 0.5;
}
}
};
return self;
});
var ForwardPlayer = Container.expand(function () {
var self = Container.call(this);
var opponentGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3.5;
self.role = 'forward';
self.homeX = 1024;
self.homeY = 1400;
self.hasBall = false;
self.directShotCooldown = self.directShotCooldown || 0;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Steal ball if player is inside opponent
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 80 && player.hasBall) {
// Steal the ball
player.hasBall = false;
ball.active = true;
// Ball moves away from player, toward forward
var stealDx = self.x - player.x;
var stealDy = self.y - player.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
// Prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
}
// Handle direct shot cooldown
if (self.directShotCooldown > 0) {
self.directShotCooldown -= 16;
if (self.directShotCooldown < 0) self.directShotCooldown = 0;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Forward: attack and try to score - more aggressive
if (distance < 700) {
// Move toward ball with aggressive pursuit
var pursuitSpeed = distance < 200 ? self.speed * 2.2 : self.speed * 1.7;
self.x += dx / distance * pursuitSpeed;
self.y += dy / distance * pursuitSpeed;
// If close to ball, shoot at goal
if (distance < 80) {
// Direct Shot every 30s
if (self.directShotCooldown <= 0) {
var goalDx = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
// Direct Shot: very fast and straight
ball.velocityX = goalDx / goalDistance * 18;
ball.velocityY = goalDy / goalDistance * 18;
}
self.directShotCooldown = 30000; // 30 seconds
} else {
// Normal shot
var goalDx = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 10;
ball.velocityY = goalDy / goalDistance * 10;
}
}
}
} else {
// Move toward attacking position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
return self;
});
var Goal = Container.expand(function () {
var self = Container.call(this);
// Goal area
var goalGraphics = self.attachAsset('goal', {
anchorX: 0.5,
anchorY: 0.5
});
goalGraphics.alpha = 0.3;
// Left goalpost
var leftPost = self.addChild(LK.getAsset('goalpost', {
anchorX: 0.5,
anchorY: 0.5
}));
leftPost.x = -210;
leftPost.y = 0;
// Right goalpost
var rightPost = self.addChild(LK.getAsset('goalpost', {
anchorX: 0.5,
anchorY: 0.5
}));
rightPost.x = 210;
rightPost.y = 0;
return self;
});
var Goalkeeper = Container.expand(function (isPlayer) {
var self = Container.call(this);
var keeperGraphics = self.attachAsset(isPlayer ? 'player' : 'opponent', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 2;
self.isPlayer = isPlayer;
self.rushCooldown = 0;
self.rushCooldownTime = 20000; // 20 seconds
self.isRushing = false;
self.originalX = 0;
self.originalY = 0;
self.rushSpeed = 8;
self.update = function () {
// Update rush cooldown
if (self.rushCooldown > 0) {
self.rushCooldown -= 16; // Approximately 60 FPS
if (self.rushCooldown < 0) self.rushCooldown = 0;
}
// Check if should rush for ball
var ballDistance = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y));
if (!self.isRushing && self.rushCooldown <= 0 && ballDistance < 400) {
// Start rushing
self.isRushing = true;
self.rushCooldown = self.rushCooldownTime;
}
if (self.isRushing) {
// Rush toward ball
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0 && distance > 50) {
self.x += dx / distance * self.rushSpeed;
self.y += dy / distance * self.rushSpeed;
} else if (distance <= 50) {
// Catch ball if close enough
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 () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, reo, nagi, bachira, hiori];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// FLOW TRIGGER: If 2 goals conceded, activate Flow for Hiori (only once)
if (!self.flowActive && !self.flowTriggerChecked && typeof opponentScore !== "undefined" && opponentScore >= 2) {
self.flowActive = true;
self.flowDribbleCount = 0;
self.flowTriggerChecked = true;
// Show Flow text above Hiori
if (!self.flowText) {
self.flowText = self.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
}
// Remove Flow text if not in Flow
if (!self.flowActive && self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
// Update cooldowns
if (self.winterZoneCooldown > 0) {
self.winterZoneCooldown -= 16;
if (self.winterZoneCooldown < 0) self.winterZoneCooldown = 0;
}
if (self.perfectPassCooldown > 0) {
self.perfectPassCooldown -= 16;
if (self.perfectPassCooldown < 0) self.perfectPassCooldown = 0;
}
// FLOW: If in Flow, boost Winter Zone and dribbling
var flowWinterZoneRadius = self.flowActive ? 420 : 200;
var flowWinterZoneSlow = self.flowActive ? 0.12 : 0.3; // 12% speed in Flow
var flowWinterZoneDuration = self.flowActive ? 18000 : self.winterZoneMaxDuration; // 18s in Flow
var flowWinterZoneCooldown = self.flowActive ? 6000 : self.winterZoneCooldownTime; // 6s in Flow
// Winter Zone duration and effect
if (self.winterZoneActive) {
self.winterZoneDuration -= 16;
if (self.winterZoneDuration <= 0) {
self.winterZoneActive = false;
// Remove Winter Zone visual
if (self.winterZoneVisual) {
self.winterZoneVisual.destroy();
self.winterZoneVisual = null;
}
// Restore opponent speeds
for (var i = 0; i < self.slowedOpponents.length; i++) {
var opponent = self.slowedOpponents[i];
opponent.speed = opponent.originalSpeed || opponent.speed * 2;
}
self.slowedOpponents = [];
} else {
// Apply Winter Zone effect to nearby opponents
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= (self.winterZoneActive ? flowWinterZoneRadius : self.winterZoneRadius)) {
if (self.slowedOpponents.indexOf(opponent) === -1) {
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * (self.winterZoneActive ? flowWinterZoneSlow : 0.3); // Much slower in Flow
self.slowedOpponents.push(opponent);
}
}
}
}
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// FLOW: Dribbling logic
if (self.isDribbling) {
var elapsed = Date.now() - self.dribbleStartTime;
var progress = Math.min(elapsed / self.dribbleDuration, 1);
// Create zig-zag pattern - slower frequency for better visibility
var zigzagAmplitude = 80;
var zigzagFrequency = 4;
var zigzagOffset = Math.sin(progress * Math.PI * zigzagFrequency) * zigzagAmplitude;
// Move toward target with zig-zag motion - slower speed
var targetDx = self.dribbleTargetX - self.dribbleStartX;
var targetDy = self.dribbleTargetY - self.dribbleStartY;
var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDist > 0) {
// Calculate perpendicular vector for zig-zag
var perpX = -targetDy / targetDist;
var perpY = targetDx / targetDist;
// Apply zig-zag movement with slower speed
var slowSpeed = 0.6; // Reduced from 1.0 to make it slower
self.x = self.dribbleStartX + targetDx * progress * slowSpeed + perpX * zigzagOffset;
self.y = self.dribbleStartY + targetDy * progress * slowSpeed + perpY * zigzagOffset;
// Ball follows during dribble
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
}
// End dribbling
if (progress >= 1) {
self.isDribbling = false;
self.zigzagCount = (self.zigzagCount || 0) + 1;
// FLOW: If in Flow, allow up to 4 extra dribbles in a row
if (self.flowActive && self.flowDribbleCount < self.flowDribbleMax) {
self.flowDribbleCount++;
// Start another dribble immediately
self.isDribbling = true;
self.dribbleStartTime = Date.now();
self.dribbleStartX = self.x;
self.dribbleStartY = self.y;
// Dribble toward opponent goal, randomize a bit
self.dribbleTargetX = opponentGoal.x + (Math.random() - 0.5) * 300;
self.dribbleTargetY = opponentGoal.y + 200 + Math.random() * 100;
} else {
// After 4 zigzags, pause for 10 seconds
if (self.zigzagCount >= 4) {
self.zigzagPaused = true;
self.zigzagPauseStart = Date.now();
self.zigzagCount = 0;
}
// Reset Flow dribble count after sequence
if (self.flowActive) self.flowDribbleCount = 0;
}
}
} else {
// Coordinated ball pursuit - only chase if designated as active pursuer
if (self.coordinatedRole === 'pursuer' && distance < 700) {
var chaseSpeed = distance < 150 ? self.speed * 1.7 : self.speed * 1.4;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 700) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 0.7;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Handle zigzag pause
if (self.zigzagPaused) {
var pauseElapsed = Date.now() - self.zigzagPauseStart;
if (pauseElapsed >= self.zigzagPauseDuration) {
self.zigzagPaused = false;
}
}
// Start zig-zag dribbling when close to ball (only if not paused)
if (distance < 80 && !self.isDribbling && !self.zigzagPaused) {
self.isDribbling = true;
self.dribbleStartTime = Date.now();
self.dribbleStartX = self.x;
self.dribbleStartY = self.y;
// Dribble toward opponent goal
self.dribbleTargetX = opponentGoal.x + (Math.random() - 0.5) * 300;
self.dribbleTargetY = opponentGoal.y + 200;
// FLOW: If in Flow, reset dribble count for new sequence
if (self.flowActive) self.flowDribbleCount = 0;
}
}
}
// FLOW: Winter Zone is much bigger, lasts longer, slows more, and triggers more often
if (!self.winterZoneActive && self.winterZoneCooldown <= 0) {
var nearbyOpponents = 0;
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= flowWinterZoneRadius + 50) {
nearbyOpponents++;
}
}
if (nearbyOpponents >= 2) {
// Activate Winter Zone
self.winterZoneActive = true;
self.winterZoneDuration = flowWinterZoneDuration;
self.winterZoneCooldown = flowWinterZoneCooldown;
// Create Winter Zone visual area
if (self.winterZoneVisual) {
self.winterZoneVisual.destroy();
self.winterZoneVisual = null;
}
self.winterZoneVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self.flowActive ? 8 : 4,
scaleY: self.flowActive ? 8 : 4,
tint: 0x87CEEB
}));
self.winterZoneVisual.alpha = 0.3;
// Visual effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0x87CEEB
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
}
// Strategic playmaking when close to ball
if (distance < 80) {
// Hiori as playmaker prioritizes strategic passing
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic pass executed
} else {
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, reo, nagi, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// FLOW: If in Flow, perfect pass always goes directly to player, no curve, and is instant
if (self.perfectPassCooldown <= 0) {
// Look for player to pass to
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 600 && playerDist > 100) {
// Execute perfect pass
var passDx = player.x - ball.x;
var passDy = player.y - ball.y;
var passDist = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDist > 0) {
if (self.flowActive) {
// FLOW: Pass is instant and direct
ball.x = player.x;
ball.y = player.y - 50;
ball.velocityX = 0;
ball.velocityY = 0;
ball.active = false;
player.hasBall = true;
} else {
// Normal perfect pass with slight curve to avoid interception
var curveAmount = 3;
ball.velocityX = passDx / passDist * 12 + curveAmount;
ball.velocityY = passDy / passDist * 12;
ball.active = true;
}
self.perfectPassCooldown = self.perfectPassCooldownTime;
// Visual effect for perfect pass
tween(ball, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1
}, {
duration: 150
});
}
});
}
}
} else if (nearestTeammate && Math.random() < 0.4) {
// 40% chance to pass to teammate (higher as playmaker)
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 9;
ball.velocityY = passDy / passDistance * 9;
}
} else {
// Shoot at goal
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 12;
ball.velocityY = goalDy / goalDistance * 12;
}
}
}
}
// Move to tactical position when supporting team
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 0.5;
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
// Return to home position when not actively playing
if (distance > 500) {
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
return self;
});
var MeguruBachiraPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 4.5;
self.role = 'bachira';
self.homeX = 1000;
self.homeY = 1900;
self.hasBall = false;
// FLOW SYSTEM for Bachira
self.flowActive = false;
self.flowTriggerChecked = false;
self.flowText = null;
// Monster Trans variables removed
// Roulette dribbling variables
self.rouletteActive = false;
self.rouletteStartTime = 0;
self.rouletteDuration = 1500; // 1.5 seconds
self.rouletteRotations = 3; // 3 full rotations
self.rouletteRadius = 80;
self.rouletteCooldown = 0;
self.rouletteCooldownTime = 4000; // 4 seconds
self.rouletteOriginX = 0;
self.rouletteOriginY = 0;
self.rouletteStunRadius = 120;
self.rouletteStunnedOpponents = [];
self.rouletteStunEnd = 0;
// Monster Dribbling variables
self.monsterDribblingActive = false;
self.monsterDribblingStartTime = 0;
self.monsterDribblingDuration = 2000; // 2 seconds
self.monsterDribblingSpeed = 8;
self.monsterDribblingCooldown = 0;
self.monsterDribblingCooldownTime = 6000; // 6 seconds
self.monsterDribblingStartX = 0;
self.monsterDribblingStartY = 0;
self.monsterDribblingTargetX = 0;
self.monsterDribblingTargetY = 0;
self.monsterDribblingStunRadius = 150;
self.monsterDribblingStunnedOpponents = [];
self.monsterDribblingStunEnd = 0;
// Rush to ball variables (after Roulette)
self.rushingToBall = false;
self.rushTargetX = 0;
self.rushTargetY = 0;
self.rushSpeed = 7;
self.readyToShoot = false;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// FLOW TRIGGER: Monster Trans for Bachira, random chance for Hiori, only one Flow at a time
if (!self.flowActive && !self.flowTriggerChecked) {
// Count Bachira and Hiori on field
var bachiraCount = typeof bachira !== "undefined" ? 1 : 0;
var hioriCount = typeof hiori !== "undefined" ? 1 : 0;
var totalFlowCandidates = bachiraCount + hioriCount;
var shouldFlow = false;
// If both Bachira and Hiori are present, randomly pick one for Flow
if (totalFlowCandidates > 1) {
// 50% chance for Bachira, 50% for Hiori
var pick = Math.random();
if (pick < 0.5) {
shouldFlow = true; // Bachira gets Flow
if (typeof hiori !== "undefined") {
hiori.flowActive = false;
hiori.flowTriggerChecked = true;
if (hiori.flowText) {
hiori.flowText.destroy();
hiori.flowText = null;
}
}
} else {
shouldFlow = false;
if (typeof hiori !== "undefined" && !hiori.flowActive && !hiori.flowTriggerChecked) {
hiori.flowActive = true;
hiori.flowTriggerChecked = true;
hiori.flowDribbleCount = 0;
// Show Flow text above Hiori
if (!hiori.flowText) {
hiori.flowText = hiori.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
hiori.flowText.anchor.set(0.5, 0.5);
hiori.flowText.x = 0;
hiori.flowText.y = -100;
hiori.flowText.alpha = 1;
if (hiori.flowText) {
// Defensive: only tween if flowText exists
tween(hiori.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
if (hiori.flowText) {
// Defensive: only tween if flowText still exists
tween(hiori.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
}
});
}
}
}
}
} else {
// Only Bachira or only Hiori present
shouldFlow = true;
}
if (shouldFlow) {
// Only set flowTriggerChecked, but do NOT activate Monster Trans/Flow yet
self.flowTriggerChecked = true;
// self.flowActive and self.monsterTransActive will be set when using Roulette or Monster Dribbling
}
}
// Remove Flow text and effects if not in Flow
if (!self.flowActive && self.flowText) {
self.flowText.destroy();
self.flowText = null;
}
// Monster Trans visual cleanup removed
// Update cooldowns
if (self.rouletteCooldown > 0) {
self.rouletteCooldown -= 16;
if (self.rouletteCooldown < 0) self.rouletteCooldown = 0;
}
if (self.monsterDribblingCooldown > 0) {
// In Flow mode, no cooldowns
if (!self.flowActive) {
self.monsterDribblingCooldown -= 16;
if (self.monsterDribblingCooldown < 0) self.monsterDribblingCooldown = 0;
} else {
self.monsterDribblingCooldown = 0; // No cooldown in Flow
}
}
// Handle Roulette stun end
if (self.rouletteStunEnd > 0 && Date.now() > self.rouletteStunEnd) {
// Restore stunned opponents from Roulette
for (var i = 0; i < self.rouletteStunnedOpponents.length; i++) {
var opponent = self.rouletteStunnedOpponents[i];
opponent.stunned = false;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.rouletteStunnedOpponents = [];
self.rouletteStunEnd = 0;
}
// Handle Monster Dribbling stun end
if (self.monsterDribblingStunEnd > 0 && Date.now() > self.monsterDribblingStunEnd) {
// Restore stunned opponents from Monster Dribbling
for (var i = 0; i < self.monsterDribblingStunnedOpponents.length; i++) {
var opponent = self.monsterDribblingStunnedOpponents[i];
opponent.stunned = false;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.monsterDribblingStunnedOpponents = [];
self.monsterDribblingStunEnd = 0;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Roulette dribbling logic
if (self.rouletteActive) {
var elapsed = Date.now() - self.rouletteStartTime;
var t = Math.min(elapsed / self.rouletteDuration, 1);
// Create spinning motion around origin point
var angle = t * self.rouletteRotations * 2 * Math.PI;
self.x = self.rouletteOriginX + Math.cos(angle) * self.rouletteRadius;
self.y = self.rouletteOriginY + Math.sin(angle) * self.rouletteRadius;
// Ball follows during roulette
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// End roulette and apply stun effect
if (t >= 1) {
self.rouletteActive = false;
self.applyRouletteStun();
}
}
// Monster Dribbling logic
if (self.monsterDribblingActive) {
var elapsed = Date.now() - self.monsterDribblingStartTime;
var t = Math.min(elapsed / self.monsterDribblingDuration, 1);
// Fast, erratic movement toward target
var chaosAmplitude = 40;
var chaosFrequency = 8;
var chaosOffsetX = Math.sin(t * Math.PI * chaosFrequency) * chaosAmplitude;
var chaosOffsetY = Math.cos(t * Math.PI * chaosFrequency * 1.3) * chaosAmplitude;
// Move toward target with chaos
var targetDx = self.monsterDribblingTargetX - self.monsterDribblingStartX;
var targetDy = self.monsterDribblingTargetY - self.monsterDribblingStartY;
self.x = self.monsterDribblingStartX + targetDx * t + chaosOffsetX;
self.y = self.monsterDribblingStartY + targetDy * t + chaosOffsetY;
// Ball follows during monster dribbling
if (distance < 120) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// Stun all opponents passed by during Monster Dribbling (only in Flow/Monster Trans)
if (self.flowActive) {
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (oppDist <= self.monsterDribblingStunRadius && !opp.stunned) {
opp.stunned = true;
opp.originalSpeed = opp.speed;
opp.speed = 0;
self.monsterDribblingStunnedOpponents.push(opp);
tween(opp, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250
});
}
}
if (self.monsterDribblingStunnedOpponents.length > 0) {
self.monsterDribblingStunEnd = Date.now() + 4000;
}
}
// End monster dribbling and apply stun effect
if (t >= 1) {
self.monsterDribblingActive = false;
self.applyMonsterDribblingStun();
}
}
// Rush to ball after Roulette throw
if (self.rushingToBall) {
var rushDx = self.rushTargetX - self.x;
var rushDy = self.rushTargetY - self.y;
var rushDist = Math.sqrt(rushDx * rushDx + rushDy * rushDy);
if (rushDist > 30) {
// Rush toward the thrown ball location
self.x += rushDx / rushDist * self.rushSpeed;
self.y += rushDy / rushDist * self.rushSpeed;
// Stun all opponents passed by during Monster Trans rush (only in Flow)
if (self.flowActive) {
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (oppDist <= self.monsterDribblingStunRadius && !opp.stunned) {
opp.stunned = true;
opp.originalSpeed = opp.speed;
opp.speed = 0;
self.monsterDribblingStunnedOpponents.push(opp);
tween(opp, {
tint: 0xFF0000,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 250
});
}
}
if (self.monsterDribblingStunnedOpponents.length > 0) {
self.monsterDribblingStunEnd = Date.now() + 4000;
}
}
} else {
// Reached the target area, check if ball is nearby
var ballDist = Math.sqrt((ball.x - self.x) * (ball.x - self.x) + (ball.y - self.y) * (ball.y - self.y));
if (ballDist < 100) {
// Ball is close, prepare to shoot
self.readyToShoot = true;
self.rushingToBall = false;
// Control the ball briefly
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
// Immediately shoot toward opponent goal
var goalDx = opponentGoal.x - self.x;
var goalDy = opponentGoal.y - self.y;
var goalDist = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDist > 0) {
ball.velocityX = goalDx / goalDist * 18; // Powerful shot
ball.velocityY = goalDy / goalDist * 18;
ball.active = true;
// Visual effect for powerful shot
tween(self, {
scaleX: 1.4,
scaleY: 1.4,
tint: 0xFF4500
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: colorOverride || 0xFFFFFF
}, {
duration: 300
});
}
});
}
// Reset after shot
self.readyToShoot = false;
} else {
// Ball not found at target, stop rushing
self.rushingToBall = false;
}
}
} else if (!self.rouletteActive && !self.monsterDribblingActive && self.coordinatedRole === 'pursuer' && distance < 700) {
// Normal ball pursuit - more aggressive
var chaseSpeed = distance < 150 ? self.speed * 1.7 : self.speed * 1.4;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (!self.rouletteActive && !self.monsterDribblingActive && self.coordinatedRole !== 'pursuer' && distance < 700) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 0.8;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Creative wing play with tactical awareness
if (distance < 80) {
// Strategic passing consideration for wing play
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic pass executed - Bachira creates from wide
} else {
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, hiori, reo, nagi, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 20% chance to pass if teammate available, 30% for abilities, 50% for goal shot
var actionChoice = Math.random();
if (nearestTeammate && actionChoice < 0.2) {
// Pass to teammate
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 11;
ball.velocityY = passDy / passDistance * 11;
}
} else if (actionChoice < 0.5) {
// Use special abilities (existing code)
// Start abilities when close to ball
if (distance < 80) {
// Roulette (normal cooldown applies)
if (!self.rouletteActive && self.rouletteCooldown <= 0) {
var shouldUseRoulette = Math.random() < 0.4; // 40% chance
if (shouldUseRoulette) {
self.rouletteActive = true;
self.rouletteStartTime = Date.now();
self.rouletteOriginX = self.x;
self.rouletteOriginY = self.y;
// If Flow was triggered for Bachira, activate Flow now
if (self.flowTriggerChecked && !self.flowActive) {
self.flowActive = true;
// Show Flow text above Bachira
if (!self.flowText) {
self.flowText = self.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
}
if (!self.flowActive) {
self.rouletteCooldown = self.rouletteCooldownTime;
}
// Visual effect for roulette start
tween(self, {
tint: 0x00FFFF
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
tint: colorOverride || 0xFFFFFF
}, {
duration: 100
});
}
});
}
}
// Monster Dribbling (no cooldown in Flow)
if (!self.monsterDribblingActive && (self.flowActive || self.monsterDribblingCooldown <= 0)) {
var shouldUseMonsterDribbling = Math.random() < 0.6; // 60% chance
if (shouldUseMonsterDribbling) {
self.monsterDribblingActive = true;
self.monsterDribblingStartTime = Date.now();
self.monsterDribblingStartX = self.x;
self.monsterDribblingStartY = self.y;
// Target toward opponent goal
self.monsterDribblingTargetX = opponentGoal.x + (Math.random() - 0.5) * 200;
self.monsterDribblingTargetY = opponentGoal.y + 300;
// If Flow was triggered for Bachira, activate Flow now
if (self.flowTriggerChecked && !self.flowActive) {
self.flowActive = true;
// Show Flow text above Bachira
if (!self.flowText) {
self.flowText = self.addChild(new Text2('FLOW', {
size: 48,
fill: 0x00FFFF
}));
self.flowText.anchor.set(0.5, 0.5);
self.flowText.x = 0;
self.flowText.y = -100;
self.flowText.alpha = 1;
tween(self.flowText, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 300,
onFinish: function onFinish() {
tween(self.flowText, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 300
});
}
});
}
}
if (!self.flowActive) {
self.monsterDribblingCooldown = self.monsterDribblingCooldownTime;
}
// Visual effect for monster dribbling start
tween(self, {
tint: 0xFF0000,
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: colorOverride || 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
}
});
}
}
}
} else {
// Shoot at goal if no abilities used
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 13;
ball.velocityY = goalDy / goalDistance * 13;
}
}
}
}
// Move to tactical wing position when not directly involved
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 0.7;
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else if (!self.rouletteActive && !self.monsterDribblingActive && distance > 500) {
// Return to home position when not actively playing
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
self.applyRouletteStun = function () {
var opponentsInRange = [];
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var opponentDist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (opponentDist <= self.rouletteStunRadius) {
opponentsInRange.push(opponent);
}
}
// Stun opponents in range
for (var i = 0; i < opponentsInRange.length; i++) {
var opponent = opponentsInRange[i];
opponent.stunned = true;
opponent.originalSpeed = opponent.speed;
opponent.speed = 0; // Completely stop them
self.rouletteStunnedOpponents.push(opponent);
// Visual effect for stunned opponent
tween(opponent, {
tint: 0x00FFFF,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200
});
}
if (opponentsInRange.length > 0) {
self.rouletteStunEnd = Date.now() + 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
});
}
});
};
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 () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Steal ball if player is inside opponent
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 80 && player.hasBall) {
// Steal the ball
player.hasBall = false;
ball.active = true;
// Ball moves away from player, toward midfield
var stealDx = self.x - player.x;
var stealDy = self.y - player.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
// Prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Midfield: get ball and pass to forward - more aggressive
if (distance < 600) {
// Move toward ball with increased speed when chasing
var chaseSpeed = distance < 150 ? self.speed * 1.8 : self.speed * 1.5;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
// If close to ball, run away from player and pass to forward
if (distance < 80) {
// Move away from player for a moment
var awayDx = self.x - player.x;
var awayDy = self.y - player.y;
var awayDist = Math.sqrt(awayDx * awayDx + awayDy * awayDy);
if (awayDist > 0) {
tween(self, {
x: self.x + awayDx / awayDist * 120,
y: self.y + awayDy / awayDist * 120
}, {
duration: 200,
easing: tween.cubicOut
});
}
// Find nearest forward player
var nearestForward = null;
var nearestDistance = Infinity;
for (var i = 0; i < opponents.length; i++) {
if (opponents[i].role === 'forward') {
var forwardDx = opponents[i].x - self.x;
var forwardDy = opponents[i].y - self.y;
var forwardDistance = Math.sqrt(forwardDx * forwardDx + forwardDy * forwardDy);
if (forwardDistance < nearestDistance) {
nearestDistance = forwardDistance;
nearestForward = opponents[i];
}
}
}
// Pass to forward if found
if (nearestForward) {
// Perfect pass: ball goes directly to forward with good speed
var passDx = nearestForward.x - ball.x;
var passDy = nearestForward.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 10;
ball.velocityY = passDy / passDistance * 10;
}
} else {
// No forward to pass to: perform dribbling and curved shot
if (!self.isDribbling) {
// Start dribbling: move in a half-circle around the player
self.isDribbling = true;
self.dribbleStartTime = Date.now();
self.dribbleDuration = 1000; // 1 second
self.dribbleRadius = 120;
// Calculate angle from midfield to player
var angleToPlayer = Math.atan2(player.y - self.y, player.x - self.x);
self.dribbleStartAngle = angleToPlayer - Math.PI / 2;
self.dribbleEndAngle = angleToPlayer + Math.PI / 2;
self.dribbleOriginX = self.x;
self.dribbleOriginY = self.y;
self.hasDribbled = false;
}
if (self.isDribbling) {
var elapsed = Date.now() - self.dribbleStartTime;
var t = Math.min(elapsed / self.dribbleDuration, 1);
// Interpolate angle for half-circle
var angle = self.dribbleStartAngle + (self.dribbleEndAngle - self.dribbleStartAngle) * t;
self.x = self.dribbleOriginX + Math.cos(angle) * self.dribbleRadius;
self.y = self.dribbleOriginY + Math.sin(angle) * self.dribbleRadius;
// Ball follows midfield during dribble
if (distance < 80) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// When dribble completes, shoot with curve
if (t >= 1 && !self.hasDribbled) {
self.hasDribbled = true;
self.isDribbling = false;
// Falsolu şut: apply curve by adding to X velocity
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
// Add curve: right-footed, so curve to the right (positive X)
var curveAmount = 7;
ball.velocityX = goalDx / goalDistance * 12 + curveAmount;
ball.velocityY = goalDy / goalDistance * 12;
ball.active = true;
}
}
}
}
}
} else {
// Return to midfield position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.3;
self.y += homeDy / homeDistance * self.speed * 0.3;
}
}
};
return self;
});
var NagiPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 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;
// 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 () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// --- FLOW TRIGGER: 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;
}
// 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);
}
});
}
}
// --- 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 good attacking position near the opponent goal
var attackTargetX = opponentGoal.x + 200 * Math.sin(Date.now() / 1200); // oscillate a bit for realism
var attackTargetY = opponentGoal.y + 250;
var attackDx = attackTargetX - self.x;
var attackDy = attackTargetY - self.y;
var attackDist = Math.sqrt(attackDx * attackDx + attackDy * attackDy);
if (attackDist > 10) {
self.x += attackDx / attackDist * self.speed * 0.8;
self.y += attackDy / attackDist * self.speed * 0.8;
}
}
// Enhanced Trap ability - long-range ball steal/pass or aerial goal scoring
var trapRange = 300; // Much longer range than before
if (distance <= trapRange && !self.ballPullActive && self.ballPullCooldown <= 0) {
if (!self.ballPullVisual) {
// Create larger trap range visual
self.ballPullVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6,
// Larger visual for longer range
scaleY: 6,
tint: 0x9966ff
}));
self.ballPullVisual.alpha = 0.15;
}
// Check if opponent has ball within range for stealing
var ballStolen = false;
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
if (opp.hasBall) {
var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
if (oppDist <= trapRange) {
// Steal ball from opponent at distance
opp.hasBall = false;
ball.active = true;
ballStolen = true;
break;
}
}
}
// Pull ball towards self or redirect for aerial goal
if (distance > 30 || ballStolen) {
self.ballPullActive = true;
// Check if close to opponent goal for aerial shot
var goalDist = Math.sqrt((opponentGoal.x - self.x) * (opponentGoal.x - self.x) + (opponentGoal.y - self.y) * (opponentGoal.y - self.y));
if (goalDist < 400) {
// Aerial goal attempt - ball goes high then drops into goal
var aerialX = opponentGoal.x + (Math.random() - 0.5) * 100;
var aerialY = opponentGoal.y;
var aerialDx = aerialX - ball.x;
var aerialDy = aerialY - ball.y;
var aerialDist = Math.sqrt(aerialDx * aerialDx + aerialDy * aerialDy);
if (aerialDist > 0) {
ball.velocityX = aerialDx / aerialDist * 15;
ball.velocityY = aerialDy / aerialDist * 15;
}
// Show "Aerial Goal!" text
var aerialText = self.addChild(new Text2('Aerial Goal!', {
size: 48,
fill: 0x9966ff
}));
aerialText.anchor.set(0.5, 0.5);
aerialText.x = 0;
aerialText.y = -120;
tween(aerialText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
LK.setTimeout(function () {
aerialText.destroy();
}, 2000);
}
});
} else {
// Check for teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, hiori, reo, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 600) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
if (nearestTeammate && Math.random() < 0.6) {
// Pass to teammate
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 12;
ball.velocityY = passDy / passDistance * 12;
}
} else {
// Pull ball towards self
var pullForce = 10;
ball.velocityX = -dx / distance * pullForce;
ball.velocityY = -dy / distance * pullForce;
}
}
ball.active = true;
self.ballPullCooldown = self.ballPullCooldownTime;
// Visual effect for enhanced trap
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0x9966ff
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
} else {
// Remove trap visual if ball is out of range
if (self.ballPullVisual) {
self.ballPullVisual.destroy();
self.ballPullVisual = null;
}
self.ballPullActive = false;
}
// Coordinated ball pursuit - only chase if designated as active pursuer
if (self.coordinatedRole === 'pursuer' && distance < 700) {
var chaseSpeed = distance < 150 ? self.speed * 1.6 : self.speed * 1.3;
self.x += dx / distance * chaseSpeed;
self.y += dy / distance * chaseSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 700) {
// Move to support position instead of chasing ball
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 0.9;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Strategic striker play when close to ball
if (distance < 80) {
// As striker, prioritize goal scoring but coordinate with team
if (self.y < 1200 && formationCoordinator.executeStrategicPass(self)) {
// In attacking zone - consider strategic pass for better position
} else {
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, hiori, reo, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 20% chance to pass, 80% for abilities/shooting (striker focus)
if (nearestTeammate && Math.random() < 0.2) {
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 13;
ball.velocityY = passDy / passDistance * 13;
}
} else {
// 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 {
// Regular shot at goal
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 14;
ball.velocityY = goalDy / goalDistance * 14;
}
}
}
}
}
}
// Striker positioning - always look for goal scoring opportunities
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * 0.9;
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else {
// Return to home position when not actively playing
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.4;
self.y += homeDy / homeDistance * self.speed * 0.4;
}
}
};
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;
return self;
});
var ReoPlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 4;
self.role = 'reo';
self.homeX = 600;
self.homeY = 1800;
self.hasBall = false;
// Mode switching variables
self.isDefensiveMode = true;
self.defensiveColor = 0x000000; // Black
self.attackingColor = 0xffa500; // Orange
self.modeSwitch = 0;
self.modeSwitchInterval = 5000; // 5 seconds
// Chameleon ability variables
self.chameleonCooldown = 0;
self.chameleonInterval = 30000; // 30 seconds
self.copiedStats = null;
self.originalSpeed = 4;
self.originalDribbling = 1;
self.originalShot = 1;
// Copied ability variables
self.copiedWinterZone = false;
self.copiedWinterZoneRadius = 200;
self.copiedWinterZoneCooldown = 0;
self.copiedWinterZoneActive = false;
self.copiedWinterZoneDuration = 0;
self.copiedWinterZoneMaxDuration = 8000;
self.copiedSlowedOpponents = [];
self.copiedWinterZoneVisual = null;
self.copiedSpeedBurst = false;
self.copiedSpeedBurstCooldown = 0;
self.copiedSpeedBurstActive = false;
self.copiedBurstSpeed = 10;
self.copiedNormalSpeed = 4;
self.update = function () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Update mode switching
self.modeSwitch += 16;
if (self.modeSwitch >= self.modeSwitchInterval) {
self.modeSwitch = 0;
self.isDefensiveMode = !self.isDefensiveMode;
// Change color based on mode
if (self.isDefensiveMode) {
tween(playerGraphics, {
tint: self.defensiveColor
}, {
duration: 300
});
} else {
tween(playerGraphics, {
tint: self.attackingColor
}, {
duration: 300
});
}
}
// Update Chameleon cooldown
self.chameleonCooldown += 16;
if (self.chameleonCooldown >= self.chameleonInterval) {
self.chameleonCooldown = 0;
self.activateChameleon();
}
// Update copied abilities
if (self.copiedWinterZone) {
// Update Winter Zone cooldown
if (self.copiedWinterZoneCooldown > 0) {
self.copiedWinterZoneCooldown -= 16;
if (self.copiedWinterZoneCooldown < 0) self.copiedWinterZoneCooldown = 0;
}
// Winter Zone duration and effect
if (self.copiedWinterZoneActive) {
self.copiedWinterZoneDuration -= 16;
if (self.copiedWinterZoneDuration <= 0) {
self.copiedWinterZoneActive = false;
// Remove Winter Zone visual
if (self.copiedWinterZoneVisual) {
self.copiedWinterZoneVisual.destroy();
self.copiedWinterZoneVisual = null;
}
// Restore opponent speeds
for (var i = 0; i < self.copiedSlowedOpponents.length; i++) {
var opponent = self.copiedSlowedOpponents[i];
opponent.speed = opponent.originalSpeed || opponent.speed * 2;
}
self.copiedSlowedOpponents = [];
} else {
// Apply Winter Zone effect to nearby opponents
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= self.copiedWinterZoneRadius) {
if (self.copiedSlowedOpponents.indexOf(opponent) === -1) {
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * 0.3; // Slow to 30% speed
self.copiedSlowedOpponents.push(opponent);
}
}
}
}
}
// Activate Winter Zone when opponents get close
if (!self.copiedWinterZoneActive && self.copiedWinterZoneCooldown <= 0) {
var nearbyOpponents = 0;
for (var i = 0; i < opponents.length; i++) {
var opponent = opponents[i];
var dist = Math.sqrt((opponent.x - self.x) * (opponent.x - self.x) + (opponent.y - self.y) * (opponent.y - self.y));
if (dist <= self.copiedWinterZoneRadius + 50) {
nearbyOpponents++;
}
}
if (nearbyOpponents >= 2) {
// Activate Winter Zone
self.copiedWinterZoneActive = true;
self.copiedWinterZoneDuration = self.copiedWinterZoneMaxDuration;
self.copiedWinterZoneCooldown = 12000; // 12 seconds cooldown
// Create Winter Zone visual area
self.copiedWinterZoneVisual = self.addChild(LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 4,
scaleY: 4,
tint: 0x87CEEB
}));
self.copiedWinterZoneVisual.alpha = 0.3;
// Visual effect
tween(self, {
scaleX: 1.5,
scaleY: 1.5,
tint: 0x87CEEB
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor
}, {
duration: 200
});
}
});
}
}
}
if (self.copiedSpeedBurst) {
// Update Speed Burst cooldown
if (self.copiedSpeedBurstCooldown > 0) {
self.copiedSpeedBurstCooldown -= 16;
if (self.copiedSpeedBurstCooldown < 0) self.copiedSpeedBurstCooldown = 0;
}
// Speed burst when close to ball
if (!self.copiedSpeedBurstActive && self.copiedSpeedBurstCooldown <= 0 && distance < 100) {
self.copiedSpeedBurstActive = true;
self.copiedSpeedBurstCooldown = 8000; // 8 seconds cooldown
self.speed = self.copiedBurstSpeed;
// Visual effect for speed burst
tween(self, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 100
});
}
});
// Speed burst lasts for 2 seconds
LK.setTimeout(function () {
self.copiedSpeedBurstActive = false;
self.speed = self.copiedNormalSpeed;
}, 2000);
}
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Mode-based behavior with coordination
if (self.isDefensiveMode) {
// Defensive mode: more active interception and ball pursuit
if (self.coordinatedRole === 'pursuer' && distance < 750) {
var defensiveSpeed = self.speed * 1.4;
self.x += dx / distance * defensiveSpeed;
self.y += dy / distance * defensiveSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 750) {
// Move to defensive support position
var supportPos = ballPursuitCoordinator.getSupportPosition('defense');
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 0.9;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Defensive tackle
if (distance < 80) {
var clearX = ball.x < 1024 ? ball.x - 300 : ball.x + 300;
var clearY = ball.y - 200;
var clearDx = clearX - ball.x;
var clearDy = clearY - ball.y;
var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy);
if (clearDist > 0) {
ball.velocityX = clearDx / clearDist * 8;
ball.velocityY = clearDy / clearDist * 8;
}
}
} else {
// Return to defensive position, but move more actively
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
var homeDistance = Math.sqrt(homeDx * homeDx + homeDy * homeDy);
if (homeDistance > 50) {
self.x += homeDx / homeDistance * self.speed * 0.7;
self.y += homeDy / homeDistance * self.speed * 0.7;
}
}
} else {
// Attacking mode: chase ball very aggressively and support attack
if (self.coordinatedRole === 'pursuer' && distance < 900) {
var attackSpeed = self.speed * 2.0;
self.x += dx / distance * attackSpeed;
self.y += dy / distance * attackSpeed;
} else if (self.coordinatedRole !== 'pursuer' && distance < 900) {
// Move to attacking support position
var supportPos = ballPursuitCoordinator.getSupportPosition(self.coordinatedRole);
var supportDx = supportPos.x - self.x;
var supportDy = supportPos.y - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 50) {
var supportSpeed = self.speed * 1.2;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
// Adaptive tactical play based on mode when close to ball
if (distance < 80) {
// Mode-based strategic decision making
if (self.isDefensiveMode) {
// Defensive mode - prioritize safe clearing and distribution
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic defensive pass executed
} else {
// Fallback defensive action
var clearX = ball.x < 1024 ? ball.x - 300 : ball.x + 300;
var clearY = ball.y - 200;
var clearDx = clearX - ball.x;
var clearDy = clearY - ball.y;
var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy);
if (clearDist > 0) {
ball.velocityX = clearDx / clearDist * 10;
ball.velocityY = clearDy / clearDist * 10;
}
}
} else {
// Attacking mode - focus on progressive play
if (formationCoordinator.executeStrategicPass(self)) {
// Strategic attacking pass executed
} else {
// Check for nearby teammates to pass to
var nearestTeammate = null;
var nearestTeammateDistance = Infinity;
var teammates = [chigiri, hiori, nagi, bachira, player];
for (var t = 0; t < teammates.length; t++) {
if (teammates[t] && teammates[t] !== self) {
var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 450) {
nearestTeammateDistance = teammateDist;
nearestTeammate = teammates[t];
}
}
}
// 35% chance to pass if teammate available, otherwise shoot
if (nearestTeammate && Math.random() < 0.35) {
var passDx = nearestTeammate.x - ball.x;
var passDy = nearestTeammate.y - ball.y;
var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
if (passDistance > 0) {
ball.velocityX = passDx / passDistance * 12;
ball.velocityY = passDy / passDistance * 12;
}
} else {
// Attack shot
var goalDx = opponentGoal.x - ball.x;
var goalDy = opponentGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 14;
ball.velocityY = goalDy / goalDistance * 14;
}
}
}
}
}
// Position based on tactical role and mode
if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
var tacticalDx = self.tacticalX - self.x;
var tacticalDy = self.tacticalY - self.y;
var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
if (tacticalDist > 80) {
var tacticalSpeed = self.speed * (self.isDefensiveMode ? 0.6 : 0.8);
self.x += tacticalDx / tacticalDist * tacticalSpeed;
self.y += tacticalDy / tacticalDist * tacticalSpeed;
}
}
} else {
// If not near ball, move up to a supporting attacking position
var supportX = opponentGoal.x - 200;
var supportY = opponentGoal.y + 400;
var supportDx = supportX - self.x;
var supportDy = supportY - self.y;
var supportDist = Math.sqrt(supportDx * supportDx + supportDy * supportDy);
if (supportDist > 30) {
self.x += supportDx / supportDist * self.speed * 0.7;
self.y += supportDy / supportDist * self.speed * 0.7;
}
}
}
};
self.activateChameleon = function () {
// Find random NPC to copy stats from (including player team members)
var availableNPCs = [];
for (var i = 0; i < opponents.length; i++) {
availableNPCs.push(opponents[i]);
}
// Also add player team members with special abilities
if (typeof hiori !== 'undefined') availableNPCs.push(hiori);
if (typeof chigiri !== 'undefined') availableNPCs.push(chigiri);
if (typeof nagi !== 'undefined') availableNPCs.push(nagi);
if (availableNPCs.length > 0) {
var randomNPC = availableNPCs[Math.floor(Math.random() * availableNPCs.length)];
// Copy stats from random NPC
self.copiedStats = {
speed: randomNPC.speed || self.originalSpeed,
dribbling: randomNPC.dribbleSpeed || self.originalDribbling,
shot: randomNPC.burstSpeed || self.originalShot,
role: randomNPC.role || 'reo'
};
// Apply copied stats
self.speed = self.copiedStats.speed;
// Copy special abilities based on role
if (self.copiedStats.role === 'hiori') {
// Copy Winter Zone ability
self.copiedWinterZone = true;
self.copiedWinterZoneRadius = 200;
self.copiedWinterZoneCooldown = 0;
self.copiedWinterZoneActive = false;
self.copiedWinterZoneDuration = 0;
self.copiedWinterZoneMaxDuration = 8000; // Slightly shorter than original
self.copiedSlowedOpponents = [];
self.copiedWinterZoneVisual = null;
} else if (self.copiedStats.role === 'chigiri') {
// Copy Speed Burst ability
self.copiedSpeedBurst = true;
self.copiedSpeedBurstCooldown = 0;
self.copiedSpeedBurstActive = false;
self.copiedBurstSpeed = 10;
self.copiedNormalSpeed = self.speed;
} else {
// Reset copied abilities
self.copiedWinterZone = false;
self.copiedSpeedBurst = false;
}
// Visual effect for Chameleon activation
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xff00ff
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor
}, {
duration: 200
});
}
});
}
};
return self;
});
var SaePlayer = Container.expand(function (colorOverride) {
var self = Container.call(this);
var playerGraphics = self.attachAsset('opponent', {
anchorX: 0.5,
anchorY: 0.5
});
if (typeof colorOverride === "number" && playerGraphics) {
playerGraphics.tint = colorOverride;
}
self.speed = 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 () {
// Hitbox collision detection with other characters
var hitboxRadius = 50;
var allCharacters = [player, chigiri, hiori, reo, nagi, bachira];
for (var c = 0; c < allCharacters.length; c++) {
if (allCharacters[c] && allCharacters[c] !== self) {
var charDist = Math.sqrt((allCharacters[c].x - self.x) * (allCharacters[c].x - self.x) + (allCharacters[c].y - self.y) * (allCharacters[c].y - self.y));
if (charDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - allCharacters[c].x) / charDist * 2;
var pushY = (self.y - allCharacters[c].y) / charDist * 2;
self.x += pushX;
self.y += pushY;
allCharacters[c].x -= pushX;
allCharacters[c].y -= pushY;
}
}
}
// Check collision with opponents too
for (var o = 0; o < opponents.length; o++) {
if (opponents[o] && opponents[o] !== self) {
var oppDist = Math.sqrt((opponents[o].x - self.x) * (opponents[o].x - self.x) + (opponents[o].y - self.y) * (opponents[o].y - self.y));
if (oppDist < hitboxRadius * 2) {
// Push characters apart
var pushX = (self.x - opponents[o].x) / oppDist * 2;
var pushY = (self.y - opponents[o].y) / oppDist * 2;
self.x += pushX;
self.y += pushY;
opponents[o].x -= pushX;
opponents[o].y -= pushY;
}
}
}
// Update cooldowns
if (self.perfectPassCooldown > 0) {
self.perfectPassCooldown -= 16;
if (self.perfectPassCooldown < 0) self.perfectPassCooldown = 0;
}
if (self.dashCooldown > 0) {
self.dashCooldown -= 16;
if (self.dashCooldown < 0) self.dashCooldown = 0;
}
if (self.falseShotCooldown > 0) {
self.falseShotCooldown -= 16;
if (self.falseShotCooldown < 0) self.falseShotCooldown = 0;
}
// Handle dribble stun end
if (self.dribbleStunEnd > 0 && Date.now() > self.dribbleStunEnd) {
// Restore stunned opponents
for (var i = 0; i < self.dribbleStunnedOpponents.length; i++) {
var opponent = self.dribbleStunnedOpponents[i];
opponent.stunned = false;
opponent.originalSpeed = opponent.originalSpeed || opponent.speed;
opponent.speed = opponent.originalSpeed;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.dribbleStunnedOpponents = [];
self.dribbleStunEnd = 0;
}
// Handle dash duration
if (self.dashActive && Date.now() > self.dashEndTime) {
self.dashActive = false;
self.speed = 4.5; // Return to normal speed
}
// Steal ball if player is inside opponent
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist < 80 && player.hasBall) {
// Steal the ball
player.hasBall = false;
ball.active = true;
// Ball moves away from player, toward Sae
var stealDx = self.x - player.x;
var stealDy = self.y - player.y;
var stealDist = Math.sqrt(stealDx * stealDx + stealDy * stealDy);
if (stealDist > 0) {
ball.velocityX = stealDx / stealDist * 10;
ball.velocityY = stealDy / stealDist * 10;
}
// Prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
}
var dx = ball.x - self.x;
var dy = ball.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Handle devoured opponents restoration
if (self.devouredEndTime > 0 && Date.now() > self.devouredEndTime) {
// Restore devoured opponents
for (var i = 0; i < self.devouredOpponents.length; i++) {
var opponent = self.devouredOpponents[i];
opponent.speed = opponent.originalSpeed || opponent.speed * 2;
opponent.devoured = false;
tween(opponent, {
tint: 0xFFFFFF,
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
self.devouredOpponents = [];
self.devouredEndTime = 0;
}
// Half-circle dribbling logic
if (self.isDribbling) {
var elapsed = Date.now() - self.dribbleStartTime;
var t = Math.min(elapsed / self.dribbleDuration, 1);
// Interpolate angle for half-circle
var angle = self.dribbleStartAngle + (self.dribbleEndAngle - self.dribbleStartAngle) * t;
self.x = self.dribbleOriginX + Math.cos(angle) * self.dribbleRadius;
self.y = self.dribbleOriginY + Math.sin(angle) * self.dribbleRadius;
// Ball follows during dribble
if (distance < 80) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// When dribble completes, apply Devoured effect
if (t >= 1) {
self.isDribbling = false;
self.applyDevouredEffect(self.dribbleStunRadius);
}
}
// Zigzag dribbling logic
if (self.isZigzagDribbling) {
var elapsed = Date.now() - self.zigzagStartTime;
var progress = Math.min(elapsed / self.zigzagDuration, 1);
// Create zigzag pattern
var zigzagAmplitude = 60;
var zigzagFrequency = 6;
var zigzagOffset = Math.sin(progress * Math.PI * zigzagFrequency) * zigzagAmplitude;
// Move toward target with zigzag motion
var targetDx = self.zigzagTargetX - self.zigzagStartX;
var targetDy = self.zigzagTargetY - self.zigzagStartY;
var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDist > 0) {
// Calculate perpendicular vector for zigzag
var perpX = -targetDy / targetDist;
var perpY = targetDx / targetDist;
// Apply zigzag movement
self.x = self.zigzagStartX + targetDx * progress + perpX * zigzagOffset;
self.y = self.zigzagStartY + targetDy * progress + perpY * zigzagOffset;
// Ball follows during dribble
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
}
// End zigzag dribbling and apply Devoured effect
if (progress >= 1) {
self.isZigzagDribbling = false;
self.applyDevouredEffect(self.zigzagDevourRadius);
// Chain up to 6 zigzags, then pause
self.zigzagCount = (self.zigzagCount || 0) + 1;
if (self.zigzagCount < 6) {
// Start another zigzag immediately
self.isZigzagDribbling = true;
self.zigzagStartTime = Date.now();
self.zigzagStartX = self.x;
self.zigzagStartY = self.y;
// Dribble toward player goal with reduced distance
self.zigzagTargetX = self.x + (playerGoal.x - self.x) * 0.3 + (Math.random() - 0.5) * 80;
self.zigzagTargetY = self.y + (playerGoal.y - self.y) * 0.3 + 50;
} else {
// Pause after 6 zigzags
self.zigzagPaused = true;
self.zigzagPauseStart = Date.now();
self.zigzagCount = 0;
}
}
}
// Zigzag pause logic
if (self.zigzagPaused) {
var pauseElapsed = Date.now() - self.zigzagPauseStart;
if (pauseElapsed >= self.zigzagPauseDuration) {
self.zigzagPaused = false;
}
}
// Chop dribbling logic
if (self.isChopDribbling) {
var elapsed = Date.now() - self.chopStartTime;
var progress = Math.min(elapsed / self.chopDuration, 1);
// Quick chop movement - sharp direction change
var chopPhase = progress < 0.5 ? progress * 2 : 1 - (progress - 0.5) * 2;
var chopIntensity = Math.sin(chopPhase * Math.PI) * 80;
// Move with chop motion
var targetDx = self.chopTargetX - self.chopStartX;
var targetDy = self.chopTargetY - self.chopStartY;
var targetDist = Math.sqrt(targetDx * targetDx + targetDy * targetDy);
if (targetDist > 0) {
// Calculate perpendicular vector for chop
var perpX = -targetDy / targetDist;
var perpY = targetDx / targetDist;
// Apply chop movement
self.x = self.chopStartX + targetDx * progress + perpX * chopIntensity;
self.y = self.chopStartY + targetDy * progress + perpY * chopIntensity;
// Ball follows during dribble
if (distance < 100) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
}
// End chop dribbling and apply Devoured effect
if (progress >= 1) {
self.isChopDribbling = false;
self.applyDevouredEffect(self.chopDevourRadius);
}
}
// FLOW: If Sae is in Flow, perform continuous crescent (hilal) dribble and finish with a striker shot
if (self.flowActive) {
// Start Hilal dribble if not already started and not shot yet
if (!self.flowHilalDribbling && !self.flowHilalShotDone && distance < 120) {
self.flowHilalDribbling = true;
self.flowHilalStartTime = Date.now();
self.flowHilalOriginX = self.x;
self.flowHilalOriginY = self.y;
// Each crescent alternates direction for visual effect
var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x);
var dir = self.flowHilalCount % 2 === 0 ? 1 : -1;
self.flowHilalStartAngle = angleToGoal - dir * Math.PI / 2;
self.flowHilalEndAngle = angleToGoal + dir * Math.PI / 2;
self.flowHilalPhase = 0;
}
// Hilal dribble logic
if (self.flowHilalDribbling && !self.flowHilalShotDone) {
var elapsed = Date.now() - self.flowHilalStartTime;
var t = Math.min(elapsed / self.flowHilalDuration, 1);
var angle = self.flowHilalStartAngle + (self.flowHilalEndAngle - self.flowHilalStartAngle) * t;
self.x = self.flowHilalOriginX + Math.cos(angle) * self.flowHilalRadius;
self.y = self.flowHilalOriginY + Math.sin(angle) * self.flowHilalRadius;
// Ball follows during dribble
if (distance < 120) {
ball.x = self.x;
ball.y = self.y;
ball.velocityX = 0;
ball.velocityY = 0;
}
// When crescent completes, start next or shoot
if (t >= 1) {
self.flowHilalDribbling = false;
self.flowHilalCount++;
// Visual effect for each crescent
tween(self, {
scaleX: 1.15,
scaleY: 1.15,
tint: 0xFF6600
}, {
duration: 120,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 120
});
}
});
// After enough crescents, shoot at goal
if (self.flowHilalCount >= self.flowHilalMax) {
// Striker shot: powerful, straight, with curve
var goalDx = playerGoal.x - self.x;
var goalDy = playerGoal.y - self.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
var curveAmount = 18 + Math.random() * 8;
ball.velocityX = goalDx / goalDistance * 20 + curveAmount;
ball.velocityY = goalDy / goalDistance * 20;
ball.active = true;
// Visual effect for shot
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xFF6600
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 200
});
}
});
}
self.flowHilalShotDone = true;
// Reset after a short delay for next Flow
LK.setTimeout(function () {
self.flowActive = false;
self.flowHilalCount = 0;
self.flowHilalShotDone = false;
}, 1200);
} else {
// Start next crescent immediately
self.flowHilalDribbling = true;
self.flowHilalStartTime = Date.now();
self.flowHilalOriginX = self.x;
self.flowHilalOriginY = self.y;
var angleToGoal = Math.atan2(playerGoal.y - self.y, playerGoal.x - self.x);
var dir = self.flowHilalCount % 2 === 0 ? 1 : -1;
self.flowHilalStartAngle = angleToGoal - dir * Math.PI / 2;
self.flowHilalEndAngle = angleToGoal + dir * Math.PI / 2;
self.flowHilalPhase = 0;
}
}
// Prevent other dribbling/shot logic during Flow
return;
}
}
// (Original code follows)
// Sae behavior: pursue ball aggressively but stay in attacking areas - more aggressive
if (distance < 800) {
// Use dash if available and close to ball
if (!self.dashActive && self.dashCooldown <= 0 && distance < 200) {
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 = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
ball.velocityX = goalDx / goalDistance * 14;
ball.velocityY = goalDy / goalDistance * 14;
ball.active = true;
}
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
self.falseShotActive = false;
}
});
}
}
} else {
// Sae is always active: maintain attacking midfield position
var attackingMidfieldX = 1024 + (Math.random() - 0.5) * 300;
var attackingMidfieldY = 1000 + Math.random() * 200; // Stay in upper midfield area
var shotDx = attackingMidfieldX - self.x;
var shotDy = attackingMidfieldY - self.y;
var shotDist = Math.sqrt(shotDx * shotDx + shotDy * shotDy);
if (shotDist > 50) {
self.x += shotDx / shotDist * self.speed * 0.5;
self.y += shotDy / shotDist * self.speed * 0.5;
}
// If far from ball but in shooting range, attempt long-range curved shot
if (distance > 200 && distance < 700 && !self.isDribbling && !self.isZigzagDribbling && !self.isChopDribbling && !self.falseShotActive) {
// Falsolu şut: apply curve by adding to X velocity
var goalDx = playerGoal.x - ball.x;
var goalDy = playerGoal.y - ball.y;
var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
if (goalDistance > 0) {
var curveAmount = 12 + Math.random() * 8; // Stronger curve for long shot
ball.velocityX = goalDx / goalDistance * 16 + curveAmount;
ball.velocityY = goalDy / goalDistance * 16;
ball.active = true;
// Visual effect for long-range shot
tween(self, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF1493
}, {
duration: 300,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
}
}
// Apply Devoured effect to nearby opponents
self.applyDevouredEffect = function (radius) {
var opponentsInRange = [];
// Check all player team members
if (typeof player !== 'undefined') {
var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
if (playerDist <= radius) {
opponentsInRange.push(player);
}
}
if (typeof chigiri !== 'undefined') {
var chigiriDist = Math.sqrt((chigiri.x - self.x) * (chigiri.x - self.x) + (chigiri.y - self.y) * (chigiri.y - self.y));
if (chigiriDist <= radius) {
opponentsInRange.push(chigiri);
}
}
if (typeof hiori !== 'undefined') {
var hioriDist = Math.sqrt((hiori.x - self.x) * (hiori.x - self.x) + (hiori.y - self.y) * (hiori.y - self.y));
if (hioriDist <= radius) {
opponentsInRange.push(hiori);
}
}
if (typeof reo !== 'undefined') {
var reoDist = Math.sqrt((reo.x - self.x) * (reo.x - self.x) + (reo.y - self.y) * (reo.y - self.y));
if (reoDist <= radius) {
opponentsInRange.push(reo);
}
}
if (typeof nagi !== 'undefined') {
var nagiDist = Math.sqrt((nagi.x - self.x) * (nagi.x - self.x) + (nagi.y - self.y) * (nagi.y - self.y));
if (nagiDist <= radius) {
opponentsInRange.push(nagi);
}
}
// Apply Devoured effect to found opponents
for (var i = 0; i < opponentsInRange.length; i++) {
var opponent = opponentsInRange[i];
// 30% chance to apply full effect, otherwise just stun
var isFullDevoured = Math.random() < 0.3;
if (isFullDevoured) {
// Full Devoured effect: severely reduce speed
opponent.originalSpeed = opponent.speed;
opponent.speed = opponent.speed * 0.2; // 20% speed
opponent.devoured = true;
self.devouredOpponents.push(opponent);
// Visual effect for devoured opponent
tween(opponent, {
tint: 0x800080,
// Purple tint for devoured
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300
});
} else {
// Just stun effect
opponent.originalSpeed = opponent.speed;
opponent.speed = 0;
opponent.stunned = true;
self.dribbleStunnedOpponents.push(opponent);
// Visual effect for stunned opponent
tween(opponent, {
tint: 0xFFFF00,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 200
});
}
}
if (opponentsInRange.length > 0) {
self.devouredEndTime = Date.now() + self.devouredDuration;
if (self.dribbleStunnedOpponents.length > 0) {
self.dribbleStunEnd = Date.now() + self.dribbleStunDuration;
}
}
};
};
return self;
});
var SoccerBall = Container.expand(function () {
var self = Container.call(this);
var ballGraphics = self.attachAsset('ball', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = 0;
self.velocityY = 0;
self.friction = 0.98;
self.active = true;
self.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);
// Create goalkeepers
var playerGoalkeeper = game.addChild(new Goalkeeper(true)); // Blue goalkeeper for player team
playerGoalkeeper.x = 1024;
playerGoalkeeper.y = 2500; // In front of player's goal
playerGoalkeeper.originalX = 1024;
playerGoalkeeper.originalY = 2500;
var opponentGoalkeeper = game.addChild(new Goalkeeper(false)); // Red goalkeeper for opponent team
opponentGoalkeeper.x = 1024;
opponentGoalkeeper.y = 400; // In front of opponent's goal
opponentGoalkeeper.originalX = 1024;
opponentGoalkeeper.originalY = 400;
// Ball kicking mechanics
function kickBall(targetX, targetY, power) {
var dx = targetX - ball.x;
var dy = targetY - ball.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
var finalPower = power || Math.min(distance / 100, 15);
// Properly detach ball from player first
player.hasBall = false;
ball.active = true;
// Set cooldown to prevent immediate re-attachment
ballDetachCooldown = ballDetachCooldownTime;
// Apply velocity after detachment
ball.velocityX = dx / distance * finalPower;
ball.velocityY = dy / distance * finalPower;
LK.getSound('kick').play();
}
}
// Touch controls for player movement and ball kicking
game.down = function (x, y, obj) {
// Always update last touch position for shot direction
lastTouchX = x;
lastTouchY = y;
var playerDistance = Math.sqrt((x - player.x) * (x - player.x) + (y - player.y) * (y - player.y));
var ballDistance = Math.sqrt((x - ball.x) * (x - ball.x) + (y - ball.y) * (y - ball.y));
if (playerDistance < ballDistance) {
// Move player
dragNode = player;
} else {
// Start charging shot if player has ball
if (player.hasBall) {
shotChargeStart = Date.now();
isChargingShot = true;
shotPower = 0;
}
}
};
game.move = function (x, y, obj) {
// Always update last touch position for shot direction
lastTouchX = x;
lastTouchY = y;
if (dragNode && currentStamina > 0) {
var dx = x - dragNode.x;
var dy = y - dragNode.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Slow down movement and consume stamina - even slower when having ball
var moveSpeed = player.hasBall ? 0.15 : 0.2;
dragNode.x += dx * moveSpeed;
dragNode.y += dy * moveSpeed;
// Consume stamina based on movement
if (distance > 5) {
currentStamina -= staminaDrainRate;
if (currentStamina < 0) currentStamina = 0;
}
}
};
game.up = function (x, y, obj) {
dragNode = null;
// Handle shot release
if (isChargingShot && player.hasBall) {
var chargeTime = Date.now() - shotChargeStart;
if (chargeTime >= minChargeTime) {
// Release charged shot - use last touch position for direction
kickBall(lastTouchX, lastTouchY, shotPower);
} else {
// Not charged enough, no shot
}
isChargingShot = false;
shotPower = 0;
}
};
// Goal scoring function
function scoreGoal(isPlayer) {
if (isPlayer) {
playerScore++;
} else {
opponentScore++;
}
scoreTxt.setText('Player: ' + playerScore + ' - Opponent: ' + opponentScore);
LK.getSound('goal').play();
// Reset ball position
ball.x = 1024;
ball.y = 1366;
ball.velocityX = 0;
ball.velocityY = 0;
}
// Formation and passing coordinator for strategic team play
var formationCoordinator = {
currentFormation: 'attacking',
ballCarrier: null,
passingOptions: [],
wingPlayers: [],
supportPlayers: [],
formation: {
attacking: {
chigiri: {
x: 800,
y: 1200,
role: 'wing'
},
hiori: {
x: 1024,
y: 1600,
role: 'playmaker'
},
reo: {
x: 1400,
y: 1400,
role: 'support'
},
nagi: {
x: 1200,
y: 800,
role: 'striker'
},
bachira: {
x: 800,
y: 1000,
role: 'wing'
}
},
defensive: {
chigiri: {
x: 600,
y: 1800,
role: 'defense'
},
hiori: {
x: 1024,
y: 1700,
role: 'playmaker'
},
reo: {
x: 1400,
y: 1800,
role: 'defense'
},
nagi: {
x: 1024,
y: 1200,
role: 'support'
},
bachira: {
x: 1000,
y: 1500,
role: 'support'
}
}
},
updateFormation: function updateFormation() {
// Switch formation based on ball position and game state
var ballY = ball.y;
var shouldAttack = ballY < 1500 || ballCarrier && ballCarrier.role === 'striker';
this.currentFormation = shouldAttack ? 'attacking' : 'defensive';
// Update each player's tactical position
var teammates = [chigiri, hiori, reo, nagi, bachira];
for (var i = 0; i < teammates.length; i++) {
if (teammates[i]) {
var formationData = this.formation[this.currentFormation][teammates[i].role];
if (formationData) {
teammates[i].tacticalX = formationData.x;
teammates[i].tacticalY = formationData.y;
teammates[i].tacticalRole = formationData.role;
}
}
}
},
findPassingOptions: function findPassingOptions(ballHolder) {
this.passingOptions = [];
this.wingPlayers = [];
this.supportPlayers = [];
var teammates = [chigiri, hiori, reo, nagi, bachira, player];
for (var i = 0; i < teammates.length; i++) {
if (teammates[i] && teammates[i] !== ballHolder) {
var dist = Math.sqrt((teammates[i].x - ballHolder.x) * (teammates[i].x - ballHolder.x) + (teammates[i].y - ballHolder.y) * (teammates[i].y - ballHolder.y));
if (dist > 150 && dist < 600) {
var passQuality = this.calculatePassQuality(ballHolder, teammates[i]);
this.passingOptions.push({
player: teammates[i],
distance: dist,
quality: passQuality,
angle: Math.atan2(teammates[i].y - ballHolder.y, teammates[i].x - ballHolder.x)
});
// Categorize players for wing play
if (teammates[i].tacticalRole === 'wing' || teammates[i].role === 'chigiri' || teammates[i].role === 'bachira') {
this.wingPlayers.push(teammates[i]);
} else {
this.supportPlayers.push(teammates[i]);
}
}
}
}
// Sort by pass quality
this.passingOptions.sort(function (a, b) {
return b.quality - a.quality;
});
},
calculatePassQuality: function calculatePassQuality(from, to) {
var baseQuality = 100;
// Distance factor (closer = better, but not too close)
var dist = Math.sqrt((to.x - from.x) * (to.x - from.x) + (to.y - from.y) * (to.y - from.y));
if (dist < 200) baseQuality -= 30;
if (dist > 400) baseQuality -= 20;
// Forward progress factor
var forwardProgress = from.y - to.y; // Moving toward opponent goal
if (forwardProgress > 0) baseQuality += forwardProgress * 0.1;
// Wing position bonus
if (to.x < 600 || to.x > 1400) baseQuality += 25;
// Striker position bonus
if (to.role === 'nagi' && to.y < 1000) baseQuality += 40;
// Check for opponent interference
for (var i = 0; i < opponents.length; i++) {
var opp = opponents[i];
var oppDist = Math.sqrt((opp.x - to.x) * (opp.x - to.x) + (opp.y - to.y) * (opp.y - to.y));
if (oppDist < 120) baseQuality -= 35;
}
return Math.max(baseQuality, 10);
},
executeStrategicPass: function executeStrategicPass(ballHolder) {
this.findPassingOptions(ballHolder);
if (this.passingOptions.length === 0) return false;
var chosenPass = null;
// Strategy selection based on field position and formation
if (ballHolder.y > 1800) {
// Defensive third - prioritize safe passes or wing play
chosenPass = this.selectSafePass() || this.selectWingPass();
} else if (ballHolder.y > 1200) {
// Middle third - mix of progressive and wing passes
var strategy = Math.random();
if (strategy < 0.4) {
chosenPass = this.selectProgressivePass();
} else if (strategy < 0.7) {
chosenPass = this.selectWingPass();
} else {
chosenPass = this.selectSafePass();
}
} else {
// Attacking third - prioritize progressive and through passes
chosenPass = this.selectProgressivePass() || this.selectThroughPass();
}
if (chosenPass) {
this.performPass(ballHolder, chosenPass);
return true;
}
return false;
},
selectSafePass: function selectSafePass() {
var safeOptions = this.passingOptions.filter(function (option) {
return option.quality > 70 && option.distance < 350;
});
return safeOptions.length > 0 ? safeOptions[0] : null;
},
selectWingPass: function selectWingPass() {
var wingOptions = this.passingOptions.filter(function (option) {
return (option.player.x < 600 || option.player.x > 1400) && option.quality > 50;
});
return wingOptions.length > 0 ? wingOptions[0] : null;
},
selectProgressivePass: function selectProgressivePass() {
var progressiveOptions = this.passingOptions.filter(function (option) {
return option.player.y < ball.y - 100 && option.quality > 60;
});
return progressiveOptions.length > 0 ? progressiveOptions[0] : null;
},
selectThroughPass: function selectThroughPass() {
var throughOptions = this.passingOptions.filter(function (option) {
return option.player.y < 1000 && option.quality > 40;
});
return throughOptions.length > 0 ? throughOptions[0] : null;
},
performPass: function performPass(from, passOption) {
var target = passOption.player;
var passPower = Math.min(passOption.distance / 40, 15);
// Add some curve for style
var curve = (Math.random() - 0.5) * 2;
var dx = target.x - ball.x + curve;
var dy = target.y - ball.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 0) {
ball.velocityX = dx / dist * passPower;
ball.velocityY = dy / dist * passPower;
ball.active = true;
// Visual effect for strategic pass
tween(ball, {
scaleX: 1.2,
scaleY: 1.2,
tint: 0x00FF88
}, {
duration: 300,
onFinish: function onFinish() {
tween(ball, {
scaleX: 1,
scaleY: 1,
tint: 0xFFFFFF
}, {
duration: 300
});
}
});
}
}
};
// Ball pursuit coordination system
var ballPursuitCoordinator = {
activePursuer: null,
pursuitRange: 150,
supportPositions: {
wing: {
x: 0,
y: 0
},
defense: {
x: 0,
y: 0
},
support: {
x: 0,
y: 0
}
},
updatePursuit: function updatePursuit() {
var teammates = [chigiri, hiori, reo, nagi, bachira];
var ballToTeammateDistances = [];
// Update formation first
formationCoordinator.updateFormation();
// Calculate distances and find closest available teammate
for (var i = 0; i < teammates.length; i++) {
if (teammates[i]) {
var dist = Math.sqrt((teammates[i].x - ball.x) * (teammates[i].x - ball.x) + (teammates[i].y - ball.y) * (teammates[i].y - ball.y));
ballToTeammateDistances.push({
player: teammates[i],
distance: dist,
isCurrentPursuer: teammates[i] === this.activePursuer
});
}
}
// Sort by distance
ballToTeammateDistances.sort(function (a, b) {
return a.distance - b.distance;
});
// Assign active pursuer (closest player within pursuit range)
var newPursuer = null;
for (var i = 0; i < ballToTeammateDistances.length; i++) {
var candidate = ballToTeammateDistances[i];
if (candidate.distance < 800) {
// Only pursue if reasonably close
newPursuer = candidate.player;
break;
}
}
// Update active pursuer
this.activePursuer = newPursuer;
// Calculate support positions
this.calculateSupportPositions();
// Assign roles to non-pursuing players
for (var i = 0; i < teammates.length; i++) {
if (teammates[i] && teammates[i] !== this.activePursuer) {
teammates[i].coordinatedRole = this.assignSupportRole(teammates[i]);
} else if (teammates[i] === this.activePursuer) {
teammates[i].coordinatedRole = 'pursuer';
}
}
},
calculateSupportPositions: function calculateSupportPositions() {
// Wing position - stay wide for passes
this.supportPositions.wing.x = ball.x < 1024 ? ball.x + 400 : ball.x - 400;
this.supportPositions.wing.y = ball.y + 100;
// Defense position - stay back for protection
this.supportPositions.defense.x = ball.x + (1024 - ball.x) * 0.3;
this.supportPositions.defense.y = Math.max(ball.y + 300, 1800);
// Support position - intermediate position for backup
this.supportPositions.support.x = ball.x + (Math.random() - 0.5) * 200;
this.supportPositions.support.y = ball.y + 200;
},
assignSupportRole: function assignSupportRole(player) {
// Assign roles based on player characteristics and position
if (player.role === 'chigiri') {
return Math.random() < 0.7 ? 'wing' : 'defense';
} else if (player.role === 'reo') {
return player.isDefensiveMode ? 'defense' : 'support';
} else if (player.role === 'hiori') {
return 'support';
} else if (player.role === 'nagi') {
return Math.random() < 0.6 ? 'wing' : 'support';
} else if (player.role === 'bachira') {
return Math.random() < 0.8 ? 'wing' : 'support';
}
return 'support';
},
getSupportPosition: function getSupportPosition(role) {
return this.supportPositions[role] || this.supportPositions.support;
}
};
// Game update loop
game.update = function () {
// Update ball pursuit coordination
ballPursuitCoordinator.updatePursuit();
// Update ball detach cooldown
if (ballDetachCooldown > 0) {
ballDetachCooldown -= 16; // Approximately 60 FPS
if (ballDetachCooldown < 0) ballDetachCooldown = 0;
}
// Check if player is close to ball for interaction (only if cooldown expired)
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;
}
}
}
// Role-based AI is now handled in each player's update method
// Defense, Midfield, and Forward players have their own specialized behaviors
}; ===================================================================
--- original.js
+++ change.js
@@ -114,9 +114,9 @@
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
}
- // Chigiri's unique ability: Speed Burst with stunning
+ // 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;
@@ -133,57 +133,18 @@
y: throwY
};
self.isSpeedBursting = true;
self.speedBurstCooldown = self.speedBurstCooldownTime; // Start cooldown
- // Stun nearby opponents during speed burst
- for (var o = 0; o < opponents.length; o++) {
- var opp = opponents[o];
- if (opp) {
- var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
- if (oppDist < 120) {
- // Stun opponent
- opp.originalSpeed = opp.speed;
- opp.speed = 0;
- opp.stunned = true;
- // Visual effect for stunned opponent
- tween(opp, {
- tint: 0x00FFFF,
- scaleX: 0.8,
- scaleY: 0.8
- }, {
- duration: 200
- });
- // Restore after 2 seconds
- LK.setTimeout(function (opponent) {
- return function () {
- if (opponent.stunned) {
- opponent.speed = opponent.originalSpeed || 3;
- opponent.stunned = false;
- tween(opponent, {
- tint: 0xFFFFFF,
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 200
- });
- }
- };
- }(opp), 2000);
- }
- }
- }
// Visual effect for speed burst
tween(self, {
scaleX: 1.2,
- scaleY: 1.2,
- tint: 0x00FFFF
+ scaleY: 1.2
}, {
duration: 100,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
- scaleY: 1,
- tint: 0xFFFFFF
+ scaleY: 1
}, {
duration: 100
});
}
@@ -224,43 +185,60 @@
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
+ // Coordinated strategic play when close to ball
if (distance < 80) {
- // Check for nearby teammates to pass to
- var nearestTeammate = null;
- var nearestTeammateDistance = Infinity;
- var teammates = [hiori, reo, nagi, bachira, player];
- for (var t = 0; t < teammates.length; t++) {
- if (teammates[t] && teammates[t] !== self) {
- var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
- if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
- nearestTeammateDistance = teammateDist;
- nearestTeammate = teammates[t];
+ // Try strategic passing first
+ if (formationCoordinator.executeStrategicPass(self)) {
+ // Strategic pass executed
+ } else {
+ // Fallback to individual decision making
+ // Check for nearby teammates to pass to
+ var nearestTeammate = null;
+ var nearestTeammateDistance = Infinity;
+ var teammates = [hiori, reo, nagi, bachira, player];
+ for (var t = 0; t < teammates.length; t++) {
+ if (teammates[t] && teammates[t] !== self) {
+ var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
+ if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
+ nearestTeammateDistance = teammateDist;
+ nearestTeammate = teammates[t];
+ }
}
}
- }
- // 40% chance to pass if teammate available, otherwise shoot
- if (nearestTeammate && Math.random() < 0.4) {
- var passDx = nearestTeammate.x - ball.x;
- var passDy = nearestTeammate.y - ball.y;
- var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
- if (passDistance > 0) {
- ball.velocityX = passDx / passDistance * 10;
- ball.velocityY = passDy / passDistance * 10;
+ // 30% chance to pass if teammate available, otherwise shoot
+ if (nearestTeammate && Math.random() < 0.3) {
+ var passDx = nearestTeammate.x - ball.x;
+ var passDy = nearestTeammate.y - ball.y;
+ var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
+ if (passDistance > 0) {
+ ball.velocityX = passDx / passDistance * 10;
+ ball.velocityY = passDy / passDistance * 10;
+ }
+ } else {
+ // Shoot at goal
+ var goalDx = opponentGoal.x - ball.x;
+ var goalDy = opponentGoal.y - ball.y;
+ var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
+ if (goalDistance > 0) {
+ ball.velocityX = goalDx / goalDistance * 11;
+ ball.velocityY = goalDy / goalDistance * 11;
+ }
}
- } else {
- // Shoot at goal
- var goalDx = opponentGoal.x - ball.x;
- var goalDy = opponentGoal.y - ball.y;
- var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
- if (goalDistance > 0) {
- ball.velocityX = goalDx / goalDistance * 11;
- ball.velocityY = goalDy / goalDistance * 11;
- }
}
}
+ // Move to tactical position when not actively involved with ball
+ if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
+ var tacticalDx = self.tacticalX - self.x;
+ var tacticalDy = self.tacticalY - self.y;
+ var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
+ if (tacticalDist > 80) {
+ var tacticalSpeed = self.speed * 0.6;
+ self.x += tacticalDx / tacticalDist * tacticalSpeed;
+ self.y += tacticalDy / tacticalDist * tacticalSpeed;
+ }
+ }
} else {
// Return to defensive position
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
@@ -919,157 +897,102 @@
}
});
}
}
- // Hiori's unique ability: Steal from dribbling opponents and pass to forwards
- var nearestDribblingOpponent = null;
- var nearestOppDistance = Infinity;
- for (var o = 0; o < opponents.length; o++) {
- var opp = opponents[o];
- if (opp && (opp.isDribbling || opp.isZigzagDribbling || opp.isChopDribbling || opp.rouletteActive || opp.monsterDribblingActive)) {
- var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
- if (oppDist < nearestOppDistance && oppDist < 150) {
- nearestOppDistance = oppDist;
- nearestDribblingOpponent = opp;
- }
- }
- }
- // If found dribbling opponent, steal ball and stop their dribbling
- if (nearestDribblingOpponent) {
- // Stop opponent's dribbling
- nearestDribblingOpponent.isDribbling = false;
- nearestDribblingOpponent.isZigzagDribbling = false;
- nearestDribblingOpponent.isChopDribbling = false;
- nearestDribblingOpponent.rouletteActive = false;
- nearestDribblingOpponent.monsterDribblingActive = false;
- nearestDribblingOpponent.hasBall = false;
- // Steal ball to Hiori
- ball.active = true;
- ball.velocityX = 0;
- ball.velocityY = 0;
- ball.x = self.x;
- ball.y = self.y;
- // Find nearest forward to pass to
- var nearestForward = null;
- var nearestForwardDistance = Infinity;
- var forwards = [nagi, bachira, player];
- for (var f = 0; f < forwards.length; f++) {
- if (forwards[f] && forwards[f] !== self) {
- var forwardDist = Math.sqrt((forwards[f].x - self.x) * (forwards[f].x - self.x) + (forwards[f].y - self.y) * (forwards[f].y - self.y));
- if (forwardDist < nearestForwardDistance && forwardDist > 80 && forwardDist < 600) {
- nearestForwardDistance = forwardDist;
- nearestForward = forwards[f];
- }
- }
- }
- // Pass to forward if found
- 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 * 14;
- ball.velocityY = passDy / passDistance * 14;
- ball.active = true;
- }
- }
- // Visual effect for steal
- tween(self, {
- scaleX: 1.3,
- scaleY: 1.3,
- tint: 0xFF0000
- }, {
- duration: 200,
- onFinish: function onFinish() {
- tween(self, {
- scaleX: 1,
- scaleY: 1,
- tint: 0xFFFFFF
- }, {
- duration: 200
- });
- }
- });
- return; // Skip normal ball handling
- }
- // Universal passing and shooting when close to ball
+ // Strategic playmaking when close to ball
if (distance < 80) {
- // Check for nearby teammates to pass to
- var nearestTeammate = null;
- var nearestTeammateDistance = Infinity;
- var teammates = [chigiri, reo, nagi, bachira, player];
- for (var t = 0; t < teammates.length; t++) {
- if (teammates[t] && teammates[t] !== self) {
- var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
- if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
- nearestTeammateDistance = teammateDist;
- nearestTeammate = teammates[t];
+ // Hiori as playmaker prioritizes strategic passing
+ if (formationCoordinator.executeStrategicPass(self)) {
+ // Strategic pass executed
+ } else {
+ // Check for nearby teammates to pass to
+ var nearestTeammate = null;
+ var nearestTeammateDistance = Infinity;
+ var teammates = [chigiri, reo, nagi, bachira, player];
+ for (var t = 0; t < teammates.length; t++) {
+ if (teammates[t] && teammates[t] !== self) {
+ var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
+ if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
+ nearestTeammateDistance = teammateDist;
+ nearestTeammate = teammates[t];
+ }
}
}
- }
- // FLOW: If in Flow, perfect pass always goes directly to player, no curve, and is instant
- if (self.perfectPassCooldown <= 0) {
- // Look for player to pass to
- var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
- if (playerDist < 600 && playerDist > 100) {
- // Execute perfect pass
- var passDx = player.x - ball.x;
- var passDy = player.y - ball.y;
- var passDist = Math.sqrt(passDx * passDx + passDy * passDy);
- if (passDist > 0) {
- if (self.flowActive) {
- // FLOW: Pass is instant and direct
- ball.x = player.x;
- ball.y = player.y - 50;
- ball.velocityX = 0;
- ball.velocityY = 0;
- ball.active = false;
- player.hasBall = true;
- } else {
- // Normal perfect pass with slight curve to avoid interception
- var curveAmount = 3;
- ball.velocityX = passDx / passDist * 12 + curveAmount;
- ball.velocityY = passDy / passDist * 12;
- ball.active = true;
- }
- self.perfectPassCooldown = self.perfectPassCooldownTime;
- // Visual effect for perfect pass
- tween(ball, {
- scaleX: 1.3,
- scaleY: 1.3
- }, {
- duration: 150,
- onFinish: function onFinish() {
- tween(ball, {
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 150
- });
+ // FLOW: If in Flow, perfect pass always goes directly to player, no curve, and is instant
+ if (self.perfectPassCooldown <= 0) {
+ // Look for player to pass to
+ var playerDist = Math.sqrt((player.x - self.x) * (player.x - self.x) + (player.y - self.y) * (player.y - self.y));
+ if (playerDist < 600 && playerDist > 100) {
+ // Execute perfect pass
+ var passDx = player.x - ball.x;
+ var passDy = player.y - ball.y;
+ var passDist = Math.sqrt(passDx * passDx + passDy * passDy);
+ if (passDist > 0) {
+ if (self.flowActive) {
+ // FLOW: Pass is instant and direct
+ ball.x = player.x;
+ ball.y = player.y - 50;
+ ball.velocityX = 0;
+ ball.velocityY = 0;
+ ball.active = false;
+ player.hasBall = true;
+ } else {
+ // Normal perfect pass with slight curve to avoid interception
+ var curveAmount = 3;
+ ball.velocityX = passDx / passDist * 12 + curveAmount;
+ ball.velocityY = passDy / passDist * 12;
+ ball.active = true;
}
- });
+ self.perfectPassCooldown = self.perfectPassCooldownTime;
+ // Visual effect for perfect pass
+ tween(ball, {
+ scaleX: 1.3,
+ scaleY: 1.3
+ }, {
+ duration: 150,
+ onFinish: function onFinish() {
+ tween(ball, {
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 150
+ });
+ }
+ });
+ }
}
+ } else if (nearestTeammate && Math.random() < 0.4) {
+ // 40% chance to pass to teammate (higher as playmaker)
+ var passDx = nearestTeammate.x - ball.x;
+ var passDy = nearestTeammate.y - ball.y;
+ var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
+ if (passDistance > 0) {
+ ball.velocityX = passDx / passDistance * 9;
+ ball.velocityY = passDy / passDistance * 9;
+ }
+ } else {
+ // Shoot at goal
+ var goalDx = opponentGoal.x - ball.x;
+ var goalDy = opponentGoal.y - ball.y;
+ var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
+ if (goalDistance > 0) {
+ ball.velocityX = goalDx / goalDistance * 12;
+ ball.velocityY = goalDy / goalDistance * 12;
+ }
}
- } else if (nearestTeammate && Math.random() < 0.3) {
- // 30% chance to pass to teammate
- var passDx = nearestTeammate.x - ball.x;
- var passDy = nearestTeammate.y - ball.y;
- var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
- if (passDistance > 0) {
- ball.velocityX = passDx / passDistance * 9;
- ball.velocityY = passDy / passDistance * 9;
- }
- } else {
- // Shoot at goal
- var goalDx = opponentGoal.x - ball.x;
- var goalDy = opponentGoal.y - ball.y;
- var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
- if (goalDistance > 0) {
- ball.velocityX = goalDx / goalDistance * 12;
- ball.velocityY = goalDy / goalDistance * 12;
- }
}
}
+ // Move to tactical position when supporting team
+ if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
+ var tacticalDx = self.tacticalX - self.x;
+ var tacticalDy = self.tacticalY - self.y;
+ var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
+ if (tacticalDist > 80) {
+ var tacticalSpeed = self.speed * 0.5;
+ self.x += tacticalDx / tacticalDist * tacticalSpeed;
+ self.y += tacticalDy / tacticalDist * tacticalSpeed;
+ }
+ }
// Return to home position when not actively playing
if (distance > 500) {
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
@@ -1463,171 +1386,187 @@
var supportSpeed = self.speed * 0.8;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
- // Universal passing and shooting when close to ball
+ // Creative wing play with tactical awareness
if (distance < 80) {
- // Check for nearby teammates to pass to
- var nearestTeammate = null;
- var nearestTeammateDistance = Infinity;
- var teammates = [chigiri, hiori, reo, nagi, player];
- for (var t = 0; t < teammates.length; t++) {
- if (teammates[t] && teammates[t] !== self) {
- var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
- if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
- nearestTeammateDistance = teammateDist;
- nearestTeammate = teammates[t];
+ // Strategic passing consideration for wing play
+ if (formationCoordinator.executeStrategicPass(self)) {
+ // Strategic pass executed - Bachira creates from wide
+ } else {
+ // Check for nearby teammates to pass to
+ var nearestTeammate = null;
+ var nearestTeammateDistance = Infinity;
+ var teammates = [chigiri, hiori, reo, nagi, player];
+ for (var t = 0; t < teammates.length; t++) {
+ if (teammates[t] && teammates[t] !== self) {
+ var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
+ if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 400) {
+ nearestTeammateDistance = teammateDist;
+ nearestTeammate = teammates[t];
+ }
}
}
- }
- // 25% chance to pass if teammate available, 25% for abilities, 50% for goal shot
- var actionChoice = Math.random();
- if (nearestTeammate && actionChoice < 0.25) {
- // Pass to teammate
- var passDx = nearestTeammate.x - ball.x;
- var passDy = nearestTeammate.y - ball.y;
- var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
- if (passDistance > 0) {
- ball.velocityX = passDx / passDistance * 11;
- ball.velocityY = passDy / passDistance * 11;
- }
- } else if (actionChoice < 0.5) {
- // Use special abilities (existing code)
- // Start abilities when close to ball
- if (distance < 80) {
- // Roulette (normal cooldown applies)
- if (!self.rouletteActive && self.rouletteCooldown <= 0) {
- var shouldUseRoulette = Math.random() < 0.4; // 40% chance
- if (shouldUseRoulette) {
- self.rouletteActive = true;
- self.rouletteStartTime = Date.now();
- self.rouletteOriginX = self.x;
- self.rouletteOriginY = self.y;
- // If Flow was triggered for Bachira, activate Flow now
- if (self.flowTriggerChecked && !self.flowActive) {
- self.flowActive = true;
- // Show Flow text above Bachira
- if (!self.flowText) {
- self.flowText = self.addChild(new Text2('FLOW', {
- size: 48,
- fill: 0x00FFFF
- }));
- self.flowText.anchor.set(0.5, 0.5);
- self.flowText.x = 0;
- self.flowText.y = -100;
- self.flowText.alpha = 1;
- tween(self.flowText, {
- scaleX: 1.5,
- scaleY: 1.5,
- alpha: 1
- }, {
- duration: 300,
- onFinish: function onFinish() {
- tween(self.flowText, {
- scaleX: 1,
- scaleY: 1,
- alpha: 1
- }, {
- duration: 300
- });
- }
- });
+ // 20% chance to pass if teammate available, 30% for abilities, 50% for goal shot
+ var actionChoice = Math.random();
+ if (nearestTeammate && actionChoice < 0.2) {
+ // Pass to teammate
+ var passDx = nearestTeammate.x - ball.x;
+ var passDy = nearestTeammate.y - ball.y;
+ var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
+ if (passDistance > 0) {
+ ball.velocityX = passDx / passDistance * 11;
+ ball.velocityY = passDy / passDistance * 11;
+ }
+ } else if (actionChoice < 0.5) {
+ // Use special abilities (existing code)
+ // Start abilities when close to ball
+ if (distance < 80) {
+ // Roulette (normal cooldown applies)
+ if (!self.rouletteActive && self.rouletteCooldown <= 0) {
+ var shouldUseRoulette = Math.random() < 0.4; // 40% chance
+ if (shouldUseRoulette) {
+ self.rouletteActive = true;
+ self.rouletteStartTime = Date.now();
+ self.rouletteOriginX = self.x;
+ self.rouletteOriginY = self.y;
+ // If Flow was triggered for Bachira, activate Flow now
+ if (self.flowTriggerChecked && !self.flowActive) {
+ self.flowActive = true;
+ // Show Flow text above Bachira
+ if (!self.flowText) {
+ self.flowText = self.addChild(new Text2('FLOW', {
+ size: 48,
+ fill: 0x00FFFF
+ }));
+ self.flowText.anchor.set(0.5, 0.5);
+ self.flowText.x = 0;
+ self.flowText.y = -100;
+ self.flowText.alpha = 1;
+ tween(self.flowText, {
+ scaleX: 1.5,
+ scaleY: 1.5,
+ alpha: 1
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ tween(self.flowText, {
+ scaleX: 1,
+ scaleY: 1,
+ alpha: 1
+ }, {
+ duration: 300
+ });
+ }
+ });
+ }
}
- }
- if (!self.flowActive) {
- self.rouletteCooldown = self.rouletteCooldownTime;
- }
- // Visual effect for roulette start
- tween(self, {
- tint: 0x00FFFF
- }, {
- duration: 100,
- onFinish: function onFinish() {
- tween(self, {
- tint: colorOverride || 0xFFFFFF
- }, {
- duration: 100
- });
+ 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
- });
- }
- });
+ // 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
- });
+ if (!self.flowActive) {
+ self.monsterDribblingCooldown = self.monsterDribblingCooldownTime;
}
- });
+ // Visual effect for monster dribbling start
+ tween(self, {
+ tint: 0xFF0000,
+ scaleX: 1.3,
+ scaleY: 1.3
+ }, {
+ duration: 200,
+ onFinish: function onFinish() {
+ tween(self, {
+ tint: colorOverride || 0xFFFFFF,
+ scaleX: 1,
+ scaleY: 1
+ }, {
+ duration: 200
+ });
+ }
+ });
+ }
}
}
+ } else {
+ // Shoot at goal if no abilities used
+ var goalDx = opponentGoal.x - ball.x;
+ var goalDy = opponentGoal.y - ball.y;
+ var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
+ if (goalDistance > 0) {
+ ball.velocityX = goalDx / goalDistance * 13;
+ ball.velocityY = goalDy / goalDistance * 13;
+ }
}
- } else {
- // Shoot at goal if no abilities used
- var goalDx = opponentGoal.x - ball.x;
- var goalDy = opponentGoal.y - ball.y;
- var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
- if (goalDistance > 0) {
- ball.velocityX = goalDx / goalDistance * 13;
- ball.velocityY = goalDy / goalDistance * 13;
- }
}
}
+ // Move to tactical wing position when not directly involved
+ if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
+ var tacticalDx = self.tacticalX - self.x;
+ var tacticalDy = self.tacticalY - self.y;
+ var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
+ if (tacticalDist > 80) {
+ var tacticalSpeed = self.speed * 0.7;
+ self.x += tacticalDx / tacticalDist * tacticalSpeed;
+ self.y += tacticalDy / tacticalDist * tacticalSpeed;
+ }
+ }
} else if (!self.rouletteActive && !self.monsterDribblingActive && distance > 500) {
// Return to home position when not actively playing
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
@@ -2524,237 +2463,204 @@
var supportSpeed = self.speed * 0.9;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
- // Universal passing and shooting when close to ball
+ // Strategic striker play when close to ball
if (distance < 80) {
- // Check for nearby teammates to pass to
- var nearestTeammate = null;
- var nearestTeammateDistance = Infinity;
- var teammates = [chigiri, hiori, reo, bachira, player];
- for (var t = 0; t < teammates.length; t++) {
- if (teammates[t] && teammates[t] !== self) {
- var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
- if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
- nearestTeammateDistance = teammateDist;
- nearestTeammate = teammates[t];
+ // As striker, prioritize goal scoring but coordinate with team
+ if (self.y < 1200 && formationCoordinator.executeStrategicPass(self)) {
+ // In attacking zone - consider strategic pass for better position
+ } else {
+ // Check for nearby teammates to pass to
+ var nearestTeammate = null;
+ var nearestTeammateDistance = Infinity;
+ var teammates = [chigiri, hiori, reo, bachira, player];
+ for (var t = 0; t < teammates.length; t++) {
+ if (teammates[t] && teammates[t] !== self) {
+ var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
+ if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 500) {
+ nearestTeammateDistance = teammateDist;
+ nearestTeammate = teammates[t];
+ }
}
}
- }
- // Nagi's unique ability: Aerial Strike - powerful aerial shots
- var shouldUseAerialStrike = Math.random() < 0.4; // 40% chance
- if (shouldUseAerialStrike) {
- // Aerial Strike - high trajectory shot toward goal
- var goalDx = opponentGoal.x - ball.x;
- var goalDy = opponentGoal.y - ball.y;
- var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
- if (goalDistance > 0) {
- // Add upward trajectory for aerial effect
- ball.velocityX = goalDx / goalDistance * 16;
- ball.velocityY = goalDy / goalDistance * 16 - 8; // Negative Y for upward trajectory
- ball.active = true;
- }
- // Visual effect for Aerial Strike
- tween(self, {
- scaleX: 1.4,
- scaleY: 1.4,
- tint: 0x9966ff
- }, {
- duration: 300,
- onFinish: function onFinish() {
- tween(self, {
- scaleX: 1,
- scaleY: 1,
- tint: 0xFFFFFF
- }, {
- duration: 300
- });
+ // 20% chance to pass, 80% for abilities/shooting (striker focus)
+ if (nearestTeammate && Math.random() < 0.2) {
+ var passDx = nearestTeammate.x - ball.x;
+ var passDy = nearestTeammate.y - ball.y;
+ var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
+ if (passDistance > 0) {
+ ball.velocityX = passDx / passDistance * 13;
+ ball.velocityY = passDy / passDistance * 13;
}
- });
- // Show "Aerial Strike!" text
- var aerialText = self.addChild(new Text2('Aerial Strike!', {
- size: 42,
- fill: 0x9966ff
- }));
- aerialText.anchor.set(0.5, 0.5);
- aerialText.x = 0;
- aerialText.y = -80;
- tween(aerialText, {
- alpha: 1,
- scaleX: 1.2,
- scaleY: 1.2
- }, {
- duration: 300,
- onFinish: function onFinish() {
- LK.setTimeout(function () {
- aerialText.destroy();
- }, 1500);
- }
- });
- } else if (nearestTeammate && Math.random() < 0.3) {
- var passDx = nearestTeammate.x - ball.x;
- var passDy = nearestTeammate.y - ball.y;
- var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
- if (passDistance > 0) {
- ball.velocityX = passDx / passDistance * 13;
- ball.velocityY = passDy / passDistance * 13;
- }
- } else {
- // 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);
+ } else {
+ // 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
+ // 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: 300
+ duration: 250
});
}
- });
- } 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
+ // 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: 200
+ duration: 300,
+ onFinish: function onFinish() {
+ tween(self, {
+ scaleX: 1,
+ scaleY: 1,
+ tint: 0xFFFFFF
+ }, {
+ duration: 300
+ });
+ }
});
- }
- 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
+ } 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
});
}
- });
- } 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;
+ 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 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
- });
+ // 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;
}
- });
- // Reset fake volley state after a delay
- LK.setTimeout(function () {
- self.fakeVolleyState = 0;
- }, 3000);
- } else {
- // Regular shot at goal
- var goalDx = opponentGoal.x - ball.x;
- var goalDy = opponentGoal.y - ball.y;
- var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
- if (goalDistance > 0) {
- ball.velocityX = goalDx / goalDistance * 14;
- ball.velocityY = goalDy / goalDistance * 14;
+ // 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 {
+ // Regular shot at goal
+ var goalDx = opponentGoal.x - ball.x;
+ var goalDy = opponentGoal.y - ball.y;
+ var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
+ if (goalDistance > 0) {
+ ball.velocityX = goalDx / goalDistance * 14;
+ ball.velocityY = goalDy / goalDistance * 14;
+ }
}
}
}
}
}
+ // Striker positioning - always look for goal scoring opportunities
+ if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
+ var tacticalDx = self.tacticalX - self.x;
+ var tacticalDy = self.tacticalY - self.y;
+ var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
+ if (tacticalDist > 80) {
+ var tacticalSpeed = self.speed * 0.9;
+ self.x += tacticalDx / tacticalDist * tacticalSpeed;
+ self.y += tacticalDy / tacticalDist * tacticalSpeed;
+ }
+ }
} else {
// Return to home position when not actively playing
var homeDx = self.homeX - self.x;
var homeDy = self.homeY - self.y;
@@ -3051,168 +2957,78 @@
var supportSpeed = self.speed * 1.2;
self.x += supportDx / supportDist * supportSpeed;
self.y += supportDy / supportDist * supportSpeed;
}
- // Reo's unique ability: Chameleon Steal - steal ball from nearby opponents and copy their abilities
- var nearestOpponentWithBall = null;
- var nearestOppDistance = Infinity;
- for (var o = 0; o < opponents.length; o++) {
- var opp = opponents[o];
- if (opp && opp.hasBall) {
- var oppDist = Math.sqrt((opp.x - self.x) * (opp.x - self.x) + (opp.y - self.y) * (opp.y - self.y));
- if (oppDist < nearestOppDistance && oppDist < 120) {
- nearestOppDistance = oppDist;
- nearestOpponentWithBall = opp;
- }
- }
- }
- // If found opponent with ball, steal and copy abilities
- if (nearestOpponentWithBall) {
- // Steal ball
- nearestOpponentWithBall.hasBall = false;
- ball.active = true;
- ball.velocityX = 0;
- ball.velocityY = 0;
- ball.x = self.x;
- ball.y = self.y;
- // Copy opponent's abilities based on their role
- if (nearestOpponentWithBall.role === 'sae') {
- // Copy Sae's dribbling abilities temporarily
- self.copiedSaeDribbling = true;
- self.copiedSaeDribblingEnd = Date.now() + 5000; // 5 seconds
- // Visual effect for copying Sae
- tween(self, {
- tint: 0xff6600,
- scaleX: 1.3,
- scaleY: 1.3
- }, {
- duration: 200,
- onFinish: function onFinish() {
- tween(self, {
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 200
- });
- }
- });
- } else if (nearestOpponentWithBall.role === 'forward') {
- // Copy shooting power temporarily
- self.copiedShootingPower = true;
- self.copiedShootingPowerEnd = Date.now() + 5000; // 5 seconds
- // Visual effect for copying shooting
- tween(self, {
- tint: 0xff0000,
- scaleX: 1.3,
- scaleY: 1.3
- }, {
- duration: 200,
- onFinish: function onFinish() {
- tween(self, {
- scaleX: 1,
- scaleY: 1
- }, {
- duration: 200
- });
- }
- });
- }
- // Show "Chameleon Steal!" text
- var stealText = self.addChild(new Text2('Chameleon Steal!', {
- size: 38,
- fill: 0xff00ff
- }));
- stealText.anchor.set(0.5, 0.5);
- stealText.x = 0;
- stealText.y = -70;
- tween(stealText, {
- alpha: 1,
- scaleX: 1.2,
- scaleY: 1.2
- }, {
- duration: 300,
- onFinish: function onFinish() {
- LK.setTimeout(function () {
- stealText.destroy();
- }, 2000);
- }
- });
- return; // Skip normal ball handling
- }
- // Handle copied abilities timeout
- if (self.copiedSaeDribblingEnd && Date.now() > self.copiedSaeDribblingEnd) {
- self.copiedSaeDribbling = false;
- self.copiedSaeDribblingEnd = 0;
- tween(self, {
- tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor
- }, {
- duration: 300
- });
- }
- if (self.copiedShootingPowerEnd && Date.now() > self.copiedShootingPowerEnd) {
- self.copiedShootingPower = false;
- self.copiedShootingPowerEnd = 0;
- tween(self, {
- tint: self.isDefensiveMode ? self.defensiveColor : self.attackingColor
- }, {
- duration: 300
- });
- }
- // Universal passing and attacking when close to ball
+ // Adaptive tactical play based on mode when close to ball
if (distance < 80) {
- // Check for nearby teammates to pass to
- var nearestTeammate = null;
- var nearestTeammateDistance = Infinity;
- var teammates = [chigiri, hiori, nagi, bachira, player];
- for (var t = 0; t < teammates.length; t++) {
- if (teammates[t] && teammates[t] !== self) {
- var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
- if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 450) {
- nearestTeammateDistance = teammateDist;
- nearestTeammate = teammates[t];
+ // Mode-based strategic decision making
+ if (self.isDefensiveMode) {
+ // Defensive mode - prioritize safe clearing and distribution
+ if (formationCoordinator.executeStrategicPass(self)) {
+ // Strategic defensive pass executed
+ } else {
+ // Fallback defensive action
+ var clearX = ball.x < 1024 ? ball.x - 300 : ball.x + 300;
+ var clearY = ball.y - 200;
+ var clearDx = clearX - ball.x;
+ var clearDy = clearY - ball.y;
+ var clearDist = Math.sqrt(clearDx * clearDx + clearDy * clearDy);
+ if (clearDist > 0) {
+ ball.velocityX = clearDx / clearDist * 10;
+ ball.velocityY = clearDy / clearDist * 10;
}
}
- }
- // 35% chance to pass if teammate available, otherwise shoot
- if (nearestTeammate && Math.random() < 0.35) {
- var passDx = nearestTeammate.x - ball.x;
- var passDy = nearestTeammate.y - ball.y;
- var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
- if (passDistance > 0) {
- ball.velocityX = passDx / passDistance * 12;
- ball.velocityY = passDy / passDistance * 12;
- }
} else {
- // Attack shot - enhanced if copied shooting power
- var goalDx = opponentGoal.x - ball.x;
- var goalDy = opponentGoal.y - ball.y;
- var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
- if (goalDistance > 0) {
- var shotPower = self.copiedShootingPower ? 18 : 14; // Enhanced power if copied
- ball.velocityX = goalDx / goalDistance * shotPower;
- ball.velocityY = goalDy / goalDistance * shotPower;
- // Visual effect for enhanced shot
- if (self.copiedShootingPower) {
- tween(ball, {
- scaleX: 1.4,
- scaleY: 1.4,
- tint: 0xff0000
- }, {
- duration: 200,
- onFinish: function onFinish() {
- tween(ball, {
- scaleX: 1,
- scaleY: 1,
- tint: 0xFFFFFF
- }, {
- duration: 200
- });
+ // Attacking mode - focus on progressive play
+ if (formationCoordinator.executeStrategicPass(self)) {
+ // Strategic attacking pass executed
+ } else {
+ // Check for nearby teammates to pass to
+ var nearestTeammate = null;
+ var nearestTeammateDistance = Infinity;
+ var teammates = [chigiri, hiori, nagi, bachira, player];
+ for (var t = 0; t < teammates.length; t++) {
+ if (teammates[t] && teammates[t] !== self) {
+ var teammateDist = Math.sqrt((teammates[t].x - self.x) * (teammates[t].x - self.x) + (teammates[t].y - self.y) * (teammates[t].y - self.y));
+ if (teammateDist < nearestTeammateDistance && teammateDist > 100 && teammateDist < 450) {
+ nearestTeammateDistance = teammateDist;
+ nearestTeammate = teammates[t];
}
- });
+ }
}
+ // 35% chance to pass if teammate available, otherwise shoot
+ if (nearestTeammate && Math.random() < 0.35) {
+ var passDx = nearestTeammate.x - ball.x;
+ var passDy = nearestTeammate.y - ball.y;
+ var passDistance = Math.sqrt(passDx * passDx + passDy * passDy);
+ if (passDistance > 0) {
+ ball.velocityX = passDx / passDistance * 12;
+ ball.velocityY = passDy / passDistance * 12;
+ }
+ } else {
+ // Attack shot
+ var goalDx = opponentGoal.x - ball.x;
+ var goalDy = opponentGoal.y - ball.y;
+ var goalDistance = Math.sqrt(goalDx * goalDx + goalDy * goalDy);
+ if (goalDistance > 0) {
+ ball.velocityX = goalDx / goalDistance * 14;
+ ball.velocityY = goalDy / goalDistance * 14;
+ }
+ }
}
}
}
+ // Position based on tactical role and mode
+ if (self.coordinatedRole !== 'pursuer' && distance > 300 && self.tacticalX && self.tacticalY) {
+ var tacticalDx = self.tacticalX - self.x;
+ var tacticalDy = self.tacticalY - self.y;
+ var tacticalDist = Math.sqrt(tacticalDx * tacticalDx + tacticalDy * tacticalDy);
+ if (tacticalDist > 80) {
+ var tacticalSpeed = self.speed * (self.isDefensiveMode ? 0.6 : 0.8);
+ self.x += tacticalDx / tacticalDist * tacticalSpeed;
+ self.y += tacticalDy / tacticalDist * tacticalSpeed;
+ }
+ }
} else {
// If not near ball, move up to a supporting attacking position
var supportX = opponentGoal.x - 200;
var supportY = opponentGoal.y + 400;
@@ -4311,15 +4127,228 @@
ball.y = 1366;
ball.velocityX = 0;
ball.velocityY = 0;
}
+// Formation and passing coordinator for strategic team play
+var formationCoordinator = {
+ currentFormation: 'attacking',
+ ballCarrier: null,
+ passingOptions: [],
+ wingPlayers: [],
+ supportPlayers: [],
+ formation: {
+ attacking: {
+ chigiri: {
+ x: 800,
+ y: 1200,
+ role: 'wing'
+ },
+ hiori: {
+ x: 1024,
+ y: 1600,
+ role: 'playmaker'
+ },
+ reo: {
+ x: 1400,
+ y: 1400,
+ role: 'support'
+ },
+ nagi: {
+ x: 1200,
+ y: 800,
+ role: 'striker'
+ },
+ bachira: {
+ x: 800,
+ y: 1000,
+ role: 'wing'
+ }
+ },
+ defensive: {
+ chigiri: {
+ x: 600,
+ y: 1800,
+ role: 'defense'
+ },
+ hiori: {
+ x: 1024,
+ y: 1700,
+ role: 'playmaker'
+ },
+ reo: {
+ x: 1400,
+ y: 1800,
+ role: 'defense'
+ },
+ nagi: {
+ x: 1024,
+ y: 1200,
+ role: 'support'
+ },
+ bachira: {
+ x: 1000,
+ y: 1500,
+ role: 'support'
+ }
+ }
+ },
+ updateFormation: function updateFormation() {
+ // Switch formation based on ball position and game state
+ var ballY = ball.y;
+ var shouldAttack = ballY < 1500 || ballCarrier && ballCarrier.role === 'striker';
+ this.currentFormation = shouldAttack ? 'attacking' : 'defensive';
+ // Update each player's tactical position
+ var teammates = [chigiri, hiori, reo, nagi, bachira];
+ for (var i = 0; i < teammates.length; i++) {
+ if (teammates[i]) {
+ var formationData = this.formation[this.currentFormation][teammates[i].role];
+ if (formationData) {
+ teammates[i].tacticalX = formationData.x;
+ teammates[i].tacticalY = formationData.y;
+ teammates[i].tacticalRole = formationData.role;
+ }
+ }
+ }
+ },
+ findPassingOptions: function findPassingOptions(ballHolder) {
+ this.passingOptions = [];
+ this.wingPlayers = [];
+ this.supportPlayers = [];
+ var teammates = [chigiri, hiori, reo, nagi, bachira, player];
+ for (var i = 0; i < teammates.length; i++) {
+ if (teammates[i] && teammates[i] !== ballHolder) {
+ var dist = Math.sqrt((teammates[i].x - ballHolder.x) * (teammates[i].x - ballHolder.x) + (teammates[i].y - ballHolder.y) * (teammates[i].y - ballHolder.y));
+ if (dist > 150 && dist < 600) {
+ var passQuality = this.calculatePassQuality(ballHolder, teammates[i]);
+ this.passingOptions.push({
+ player: teammates[i],
+ distance: dist,
+ quality: passQuality,
+ angle: Math.atan2(teammates[i].y - ballHolder.y, teammates[i].x - ballHolder.x)
+ });
+ // Categorize players for wing play
+ if (teammates[i].tacticalRole === 'wing' || teammates[i].role === 'chigiri' || teammates[i].role === 'bachira') {
+ this.wingPlayers.push(teammates[i]);
+ } else {
+ this.supportPlayers.push(teammates[i]);
+ }
+ }
+ }
+ }
+ // Sort by pass quality
+ this.passingOptions.sort(function (a, b) {
+ return b.quality - a.quality;
+ });
+ },
+ calculatePassQuality: function calculatePassQuality(from, to) {
+ var baseQuality = 100;
+ // Distance factor (closer = better, but not too close)
+ var dist = Math.sqrt((to.x - from.x) * (to.x - from.x) + (to.y - from.y) * (to.y - from.y));
+ if (dist < 200) baseQuality -= 30;
+ if (dist > 400) baseQuality -= 20;
+ // Forward progress factor
+ var forwardProgress = from.y - to.y; // Moving toward opponent goal
+ if (forwardProgress > 0) baseQuality += forwardProgress * 0.1;
+ // Wing position bonus
+ if (to.x < 600 || to.x > 1400) baseQuality += 25;
+ // Striker position bonus
+ if (to.role === 'nagi' && to.y < 1000) baseQuality += 40;
+ // Check for opponent interference
+ for (var i = 0; i < opponents.length; i++) {
+ var opp = opponents[i];
+ var oppDist = Math.sqrt((opp.x - to.x) * (opp.x - to.x) + (opp.y - to.y) * (opp.y - to.y));
+ if (oppDist < 120) baseQuality -= 35;
+ }
+ return Math.max(baseQuality, 10);
+ },
+ executeStrategicPass: function executeStrategicPass(ballHolder) {
+ this.findPassingOptions(ballHolder);
+ if (this.passingOptions.length === 0) return false;
+ var chosenPass = null;
+ // Strategy selection based on field position and formation
+ if (ballHolder.y > 1800) {
+ // Defensive third - prioritize safe passes or wing play
+ chosenPass = this.selectSafePass() || this.selectWingPass();
+ } else if (ballHolder.y > 1200) {
+ // Middle third - mix of progressive and wing passes
+ var strategy = Math.random();
+ if (strategy < 0.4) {
+ chosenPass = this.selectProgressivePass();
+ } else if (strategy < 0.7) {
+ chosenPass = this.selectWingPass();
+ } else {
+ chosenPass = this.selectSafePass();
+ }
+ } else {
+ // Attacking third - prioritize progressive and through passes
+ chosenPass = this.selectProgressivePass() || this.selectThroughPass();
+ }
+ if (chosenPass) {
+ this.performPass(ballHolder, chosenPass);
+ return true;
+ }
+ return false;
+ },
+ selectSafePass: function selectSafePass() {
+ var safeOptions = this.passingOptions.filter(function (option) {
+ return option.quality > 70 && option.distance < 350;
+ });
+ return safeOptions.length > 0 ? safeOptions[0] : null;
+ },
+ selectWingPass: function selectWingPass() {
+ var wingOptions = this.passingOptions.filter(function (option) {
+ return (option.player.x < 600 || option.player.x > 1400) && option.quality > 50;
+ });
+ return wingOptions.length > 0 ? wingOptions[0] : null;
+ },
+ selectProgressivePass: function selectProgressivePass() {
+ var progressiveOptions = this.passingOptions.filter(function (option) {
+ return option.player.y < ball.y - 100 && option.quality > 60;
+ });
+ return progressiveOptions.length > 0 ? progressiveOptions[0] : null;
+ },
+ selectThroughPass: function selectThroughPass() {
+ var throughOptions = this.passingOptions.filter(function (option) {
+ return option.player.y < 1000 && option.quality > 40;
+ });
+ return throughOptions.length > 0 ? throughOptions[0] : null;
+ },
+ performPass: function performPass(from, passOption) {
+ var target = passOption.player;
+ var passPower = Math.min(passOption.distance / 40, 15);
+ // Add some curve for style
+ var curve = (Math.random() - 0.5) * 2;
+ var dx = target.x - ball.x + curve;
+ var dy = target.y - ball.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist > 0) {
+ ball.velocityX = dx / dist * passPower;
+ ball.velocityY = dy / dist * passPower;
+ ball.active = true;
+ // Visual effect for strategic pass
+ tween(ball, {
+ scaleX: 1.2,
+ scaleY: 1.2,
+ tint: 0x00FF88
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ tween(ball, {
+ scaleX: 1,
+ scaleY: 1,
+ tint: 0xFFFFFF
+ }, {
+ duration: 300
+ });
+ }
+ });
+ }
+ }
+};
// Ball pursuit coordination system
var ballPursuitCoordinator = {
activePursuer: null,
pursuitRange: 150,
- lastPursuerChange: 0,
- pursuerChangeDelay: 1000,
- // 1 second minimum between pursuer changes
supportPositions: {
wing: {
x: 0,
y: 0
@@ -4335,11 +4364,13 @@
},
updatePursuit: function updatePursuit() {
var teammates = [chigiri, hiori, reo, nagi, bachira];
var ballToTeammateDistances = [];
+ // Update formation first
+ formationCoordinator.updateFormation();
// Calculate distances and find closest available teammate
for (var i = 0; i < teammates.length; i++) {
- if (teammates[i] && !teammates[i].isDribbling && !teammates[i].isZigzagDribbling && !teammates[i].rouletteActive && !teammates[i].monsterDribblingActive) {
+ if (teammates[i]) {
var dist = Math.sqrt((teammates[i].x - ball.x) * (teammates[i].x - ball.x) + (teammates[i].y - ball.y) * (teammates[i].y - ball.y));
ballToTeammateDistances.push({
player: teammates[i],
distance: dist,
@@ -4350,30 +4381,21 @@
// Sort by distance
ballToTeammateDistances.sort(function (a, b) {
return a.distance - b.distance;
});
- // Assign active pursuer (closest player within pursuit range) but limit changes
+ // Assign active pursuer (closest player within pursuit range)
var newPursuer = null;
- var currentTime = Date.now();
- if (!this.activePursuer || currentTime - this.lastPursuerChange > this.pursuerChangeDelay) {
- for (var i = 0; i < ballToTeammateDistances.length; i++) {
- var candidate = ballToTeammateDistances[i];
- if (candidate.distance < 600) {
- // Reduced range to prevent overcrowding
- // Only pursue if reasonably close and not currently in special ability
- newPursuer = candidate.player;
- break;
- }
+ for (var i = 0; i < ballToTeammateDistances.length; i++) {
+ var candidate = ballToTeammateDistances[i];
+ if (candidate.distance < 800) {
+ // Only pursue if reasonably close
+ newPursuer = candidate.player;
+ break;
}
- if (newPursuer !== this.activePursuer) {
- this.lastPursuerChange = currentTime;
- }
- } else {
- newPursuer = this.activePursuer; // Keep current pursuer
}
// Update active pursuer
this.activePursuer = newPursuer;
- // Calculate support positions with more spacing
+ // Calculate support positions
this.calculateSupportPositions();
// Assign roles to non-pursuing players
for (var i = 0; i < teammates.length; i++) {
if (teammates[i] && teammates[i] !== this.activePursuer) {
@@ -4383,40 +4405,35 @@
}
}
},
calculateSupportPositions: function calculateSupportPositions() {
- // Wing position - stay wide for passes with more spacing
- this.supportPositions.wing.x = ball.x < 1024 ? ball.x + 500 : ball.x - 500;
- this.supportPositions.wing.y = ball.y + 150;
- // Defense position - stay back for protection with more spacing
- this.supportPositions.defense.x = ball.x + (1024 - ball.x) * 0.4;
- this.supportPositions.defense.y = Math.max(ball.y + 400, 1800);
- // Support position - intermediate position for backup with more spacing
- this.supportPositions.support.x = ball.x + (Math.random() - 0.5) * 300;
- this.supportPositions.support.y = ball.y + 250;
+ // Wing position - stay wide for passes
+ this.supportPositions.wing.x = ball.x < 1024 ? ball.x + 400 : ball.x - 400;
+ this.supportPositions.wing.y = ball.y + 100;
+ // Defense position - stay back for protection
+ this.supportPositions.defense.x = ball.x + (1024 - ball.x) * 0.3;
+ this.supportPositions.defense.y = Math.max(ball.y + 300, 1800);
+ // Support position - intermediate position for backup
+ this.supportPositions.support.x = ball.x + (Math.random() - 0.5) * 200;
+ this.supportPositions.support.y = ball.y + 200;
},
assignSupportRole: function assignSupportRole(player) {
- // Assign roles based on player characteristics and position with more variety
+ // Assign roles based on player characteristics and position
if (player.role === 'chigiri') {
- return Math.random() < 0.6 ? 'wing' : 'defense';
+ return Math.random() < 0.7 ? 'wing' : 'defense';
} else if (player.role === 'reo') {
return player.isDefensiveMode ? 'defense' : 'support';
} else if (player.role === 'hiori') {
- return Math.random() < 0.7 ? 'support' : 'wing';
+ return 'support';
} else if (player.role === 'nagi') {
- return Math.random() < 0.5 ? 'wing' : 'support';
+ return Math.random() < 0.6 ? 'wing' : 'support';
} else if (player.role === 'bachira') {
- return Math.random() < 0.7 ? 'wing' : 'support';
+ return Math.random() < 0.8 ? 'wing' : 'support';
}
return 'support';
},
getSupportPosition: function getSupportPosition(role) {
- var basePos = this.supportPositions[role] || this.supportPositions.support;
- // Add some randomization to prevent clustering
- return {
- x: basePos.x + (Math.random() - 0.5) * 100,
- y: basePos.y + (Math.random() - 0.5) * 100
- };
+ return this.supportPositions[role] || this.supportPositions.support;
}
};
// Game update loop
game.update = function () {