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; } }