using System; using ProjectI.Combat; using ProjectI.Runs; using Unity.Netcode; using UnityEngine; namespace ProjectI.Enemies { // Only accepted weapon discharges emit hearing in M2. No presentation/RPC-arrival producer. public readonly struct EnemyCombatNoise { public const float CellSize = 12; public readonly NetworkManager Network; public readonly StageSpatialContext Stage; public readonly StageSpatialDefinition StageDefinition; public readonly ulong RunId, StageId; public readonly EnemyObservation Observation; internal EnemyCombatNoise(PrototypePlayerCombat player, Vector3 acceptedPosition, double at) { Network = player.NetworkManager; Stage = StageSpatialContext.Active; StageDefinition = Stage.definition; var run = PrototypeRunDirector.Instance; RunId = run != null ? run.RunId : 0; StageId = run != null ? run.StageId : 0; Vector3 region = new Vector3(Cell(acceptedPosition.x), Cell(acceptedPosition.y), Cell(acceptedPosition.z)); Observation = new EnemyObservation(new EnemyTargetKey(player.NetworkObjectId, player.SpawnLifetime), region, at, default, 0, EnemyObservationKind.HeardRegion, Mathf.Sqrt(3) * CellSize * .5f); } private static float Cell(float value) => (Mathf.Floor(value / CellSize) + .5f) * CellSize; } public static class EnemyCombatNoiseEvents { public static event Action AcceptedWeaponShot; [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void Reset() => AcceptedWeaponShot = null; internal static void EmitAcceptedWeaponShotServer(PrototypePlayerCombat player, Vector3 acceptedPosition, double at) { if (AcceptedWeaponShot == null || player == null || !EnemyObservationRules.Finite(acceptedPosition) || !EnemyObservationRules.Finite(at) || at < 0 || player.NetworkManager == null || at > player.NetworkManager.ServerTime.Time) return; var run = PrototypeRunDirector.Instance; if (!EnemyTargetRules.Eligible(player, new EnemyTargetKey(player.NetworkObjectId, player.SpawnLifetime), player.NetworkManager, StageSpatialContext.Active, run != null ? run.RunId : 0, run != null ? run.StageId : 0)) return; var noise = new EnemyCombatNoise(player, acceptedPosition, at); foreach (Action receiver in AcceptedWeaponShot.GetInvocationList()) try { receiver(noise); } catch (Exception error) { Debug.LogException(error); } // An observer cannot interrupt accepted combat. } } }