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 = [ const awayTeamPositions = [
{ role: 'G', x: 1000 - goalXOffset, y: 300 }, { role: 'G', x: 1000 - goalXOffset, y: 300 },
{ role: 'LD', x: 800, y: 220 }, { role: 'LD', x: 800, y: 380 },
{ role: 'RD', x: 800, y: 380 }, { role: 'RD', x: 800, y: 220 },
{ role: 'LW', x: 650, y: 200 }, { role: 'LW', x: 650, y: 400 },
{ role: 'C', x: 600, y: 300 }, { role: 'C', x: 600, y: 300 },
{ role: 'RW', x: 650, y: 400 } { role: 'RW', x: 650, y: 200 }
]; ];
homeTeamPositions.forEach((pos, index) => { homeTeamPositions.forEach((pos, index) => {

View File

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