Fix away team left/right player positioning

Away team Left Defender and Left Winger were incorrectly positioned on the right side due to formation logic not accounting for team orientation. Away team faces opposite direction, so their left/right perspective is flipped.

- Fix initial positioning in game-engine.js setupPlayers()
- Update formation logic in player.js for all positioning methods:
  - getOffensiveSupportPosition()
  - getContextualPosition()
  - getFaceoffPosition()
- Away team left positions now use positive Y (bottom), right positions use negative Y (top)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Pierre Wessman 2025-09-19 10:55:21 +02:00
parent 4bf182c2a9
commit 8674e65432
2 changed files with 41 additions and 20 deletions

View File

@ -39,11 +39,11 @@ class GameEngine {
const awayTeamPositions = [
{ role: 'G', x: 1000 - goalXOffset, y: 300 },
{ role: 'LD', x: 800, y: 220 },
{ role: 'RD', x: 800, y: 380 },
{ role: 'LW', x: 650, y: 200 },
{ role: 'LD', x: 800, y: 380 },
{ role: 'RD', x: 800, y: 220 },
{ role: 'LW', x: 650, y: 400 },
{ role: 'C', x: 600, y: 300 },
{ role: 'RW', x: 650, y: 400 }
{ role: 'RW', x: 650, y: 200 }
];
homeTeamPositions.forEach((pos, index) => {

View File

@ -450,6 +450,11 @@ class Player {
// Base position is an aggressive offensive formation
const side = this.team === 'home' ? 1 : -1;
const attackZone = this.team === 'home' ? rink.width * 0.75 : rink.width * 0.25;
// Away team players need their left/right flipped since they face the opposite direction
const leftSideY = this.team === 'home' ? -140 : 140;
const rightSideY = this.team === 'home' ? 140 : -140;
const leftDefenseY = this.team === 'home' ? -100 : 100;
const rightDefenseY = this.team === 'home' ? 100 : -100;
let baseX, baseY;
@ -462,22 +467,22 @@ class Player {
case 'LW':
// Left wing pushes forward on their side for passing options
baseX = attackZone + 40; // Push forward past attack zone
baseY = rink.centerY - 140;
baseY = rink.centerY + leftSideY;
break;
case 'RW':
// Right wing pushes forward on their side for passing options
baseX = attackZone + 40; // Push forward past attack zone
baseY = rink.centerY + 140;
baseY = rink.centerY + rightSideY;
break;
case 'LD':
// Left defense supports from the point
baseX = attackZone - 120;
baseY = rink.centerY - 100;
baseY = rink.centerY + leftDefenseY;
break;
case 'RD':
// Right defense supports from the point
baseX = attackZone - 120;
baseY = rink.centerY + 100;
baseY = rink.centerY + rightDefenseY;
break;
default:
return this.getFormationPosition(gameState, puck, [puckCarrier, ...opponents, this]);
@ -545,6 +550,16 @@ class Player {
const puckInfluenceX = (puck.position.x - rink.centerX) * 0.3; // Follow puck horizontally
const puckInfluenceY = (puck.position.y - centerY) * 0.2; // Follow puck vertically (less influence)
// Away team players need their left/right flipped since they face the opposite direction
const leftSideY = this.team === 'home' ? -120 : 120;
const rightSideY = this.team === 'home' ? 120 : -120;
const leftDefenseY = this.team === 'home' ? -80 : 80;
const rightDefenseY = this.team === 'home' ? 80 : -80;
const leftDefensiveY = this.team === 'home' ? -100 : 100;
const rightDefensiveY = this.team === 'home' ? 100 : -100;
const leftDefensiveDefenseY = this.team === 'home' ? -60 : 60;
const rightDefensiveDefenseY = this.team === 'home' ? 60 : -60;
let baseX, baseY;
if (isAttacking) {
@ -558,19 +573,19 @@ class Player {
break;
case 'LW':
baseX = attackZone - 50;
baseY = centerY - 120;
baseY = centerY + leftSideY;
break;
case 'RW':
baseX = attackZone - 50;
baseY = centerY + 120;
baseY = centerY + rightSideY;
break;
case 'LD':
baseX = attackZone - 150;
baseY = centerY - 80;
baseY = centerY + leftDefenseY;
break;
case 'RD':
baseX = attackZone - 150;
baseY = centerY + 80;
baseY = centerY + rightDefenseY;
break;
default:
return this.homePosition;
@ -586,19 +601,19 @@ class Player {
break;
case 'LW':
baseX = defenseZone + side * 50;
baseY = centerY - 100;
baseY = centerY + leftDefensiveY;
break;
case 'RW':
baseX = defenseZone + side * 50;
baseY = centerY + 100;
baseY = centerY + rightDefensiveY;
break;
case 'LD':
baseX = defenseZone + side * 100;
baseY = centerY - 60;
baseY = centerY + leftDefensiveDefenseY;
break;
case 'RD':
baseX = defenseZone + side * 100;
baseY = centerY + 60;
baseY = centerY + rightDefensiveDefenseY;
break;
default:
return this.homePosition;
@ -969,6 +984,12 @@ class Player {
const side = this.team === 'home' ? -1 : 1;
const faceoffRadius = RINK_CIRCLES.FACEOFF_CIRCLE_RADIUS; // Radius of faceoff circle
// Away team players need their left/right flipped since they face the opposite direction
const leftWingY = this.team === 'home' ? -(faceoffRadius + 20) : (faceoffRadius + 20);
const rightWingY = this.team === 'home' ? (faceoffRadius + 20) : -(faceoffRadius + 20);
const leftDefenseY = this.team === 'home' ? -(faceoffRadius + 40) : (faceoffRadius + 40);
const rightDefenseY = this.team === 'home' ? (faceoffRadius + 40) : -(faceoffRadius + 40);
switch (this.role) {
case 'C':
// Centers line up directly at the faceoff dot, facing each other
@ -982,7 +1003,7 @@ class Player {
// Position them further back and outside the circle
return new Vector2(
faceoffLocation.x + side * (faceoffRadius + 30), // Outside circle + buffer
faceoffLocation.y - (faceoffRadius + 20) // Outside circle on left side
faceoffLocation.y + leftWingY // Outside circle on left side
);
case 'RW':
@ -990,21 +1011,21 @@ class Player {
// Position them further back and outside the circle
return new Vector2(
faceoffLocation.x + side * (faceoffRadius + 30), // Outside circle + buffer
faceoffLocation.y + (faceoffRadius + 20) // Outside circle on right side
faceoffLocation.y + rightWingY // Outside circle on right side
);
case 'LD':
// Left defense well outside the faceoff area
return new Vector2(
faceoffLocation.x + side * (faceoffRadius + 80),
faceoffLocation.y - (faceoffRadius + 40)
faceoffLocation.y + leftDefenseY
);
case 'RD':
// Right defense well outside the faceoff area
return new Vector2(
faceoffLocation.x + side * (faceoffRadius + 80),
faceoffLocation.y + (faceoffRadius + 40)
faceoffLocation.y + rightDefenseY
);
case 'G':