hockey-manager/src/systems/rules-system.js
2025-09-16 13:08:41 +02:00

151 lines
4.6 KiB
JavaScript

class RulesSystem {
constructor(gameState) {
this.gameState = gameState;
this.lastOffsideCheck = 0;
this.lastIcingCheck = 0;
}
update(players, puck, deltaTime) {
this.checkOffside(players, puck);
this.checkIcing(players, puck);
}
checkOffside(players, puck) {
const currentTime = Date.now();
if (currentTime - this.lastOffsideCheck < 500) return;
const puckOwner = players.find(p => p.state.hasPuck);
if (!puckOwner) return;
const centerLine = this.gameState.rink.centerX;
const offensiveZoneLine = puckOwner.team === 'home' ?
this.gameState.rink.width * 0.67 : this.gameState.rink.width * 0.33;
const puckInOffensiveZone = puckOwner.team === 'home' ?
puck.position.x > offensiveZoneLine :
puck.position.x < offensiveZoneLine;
if (puckInOffensiveZone) {
const teammates = players.filter(p =>
p.team === puckOwner.team &&
p.id !== puckOwner.id &&
p.role !== 'G'
);
const offsidePlayers = teammates.filter(teammate => {
const isAhead = puckOwner.team === 'home' ?
teammate.position.x > puck.position.x :
teammate.position.x < puck.position.x;
const inOffensiveZone = puckOwner.team === 'home' ?
teammate.position.x > offensiveZoneLine :
teammate.position.x < offensiveZoneLine;
return isAhead && inOffensiveZone;
});
if (offsidePlayers.length > 0) {
this.callOffside(puckOwner.team, offsidePlayers);
this.lastOffsideCheck = currentTime;
}
}
}
callOffside(team, players) {
this.gameState.addEvent(`OFFSIDE - ${team.toUpperCase()}`);
const faceoffPosition = this.gameState.rink.faceoffDots[
team === 'home' ? 1 : 3
];
this.gameState.emit('offside', {
team,
players: players.map(p => p.name),
faceoffPosition
});
this.scheduleFaceoff(faceoffPosition);
}
checkIcing(players, puck) {
const currentTime = Date.now();
if (currentTime - this.lastIcingCheck < 1000) return;
if (!puck.lastTeamTouch) return;
const shootingTeam = puck.lastTeamTouch;
const shootingZoneLine = shootingTeam === 'home' ?
this.gameState.rink.width * 0.33 : this.gameState.rink.width * 0.67;
const puckCrossedOwnZone = shootingTeam === 'home' ?
puck.position.x < shootingZoneLine :
puck.position.x > shootingZoneLine;
if (!puckCrossedOwnZone) return;
const goalLine = shootingTeam === 'home' ?
this.gameState.rink.width - 50 : 50;
const puckPastGoalLine = shootingTeam === 'home' ?
puck.position.x > goalLine :
puck.position.x < goalLine;
if (puckPastGoalLine) {
const shootingPlayers = players.filter(p => p.team === shootingTeam);
const opposingPlayers = players.filter(p => p.team !== shootingTeam);
const nearestShootingPlayer = this.findNearestPlayer(puck.position, shootingPlayers);
const nearestOpposingPlayer = this.findNearestPlayer(puck.position, opposingPlayers);
if (nearestOpposingPlayer &&
nearestOpposingPlayer.position.distance(puck.position) <
nearestShootingPlayer.position.distance(puck.position)) {
this.callIcing(shootingTeam);
this.lastIcingCheck = currentTime;
}
}
}
callIcing(team) {
this.gameState.addEvent(`ICING - ${team.toUpperCase()}`);
const faceoffPosition = this.gameState.rink.faceoffDots[
team === 'home' ? 0 : 4
];
this.gameState.emit('icing', {
team,
faceoffPosition
});
this.scheduleFaceoff(faceoffPosition);
}
scheduleFaceoff(position) {
setTimeout(() => {
this.gameState.emit('faceoff', { position });
}, 2000);
}
findNearestPlayer(position, players) {
let nearest = null;
let minDistance = Infinity;
players.forEach(player => {
const distance = player.position.distance(position);
if (distance < minDistance) {
minDistance = distance;
nearest = player;
}
});
return nearest;
}
reset() {
this.lastOffsideCheck = 0;
this.lastIcingCheck = 0;
}
}