using System; using System.Collections.Generic; using HG; using Unity.Collections; using Unity.Jobs; using UnityEngine; namespace RoR2; public class RayAttackIndicator : MonoBehaviour { private struct HitIndicatorInfo { public GameObject gameObject; public Transform transform; } private struct RaycastInfo { public Ray ray; public float maxDistance; public int hitsStart; public int maxHits; } private struct HitPointIndicatorData { public Vector3 position; public Quaternion rotation; public float scale; } private struct Updater { private bool running; public void ScheduleUpdate() { if (running) { return; } List instancesList = InstanceTracker.GetInstancesList(); if (instancesList.Count != 0) { requestsBuffer = new NativeArray(instancesList.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); raycastCommandBuffer = new NativeArray(requestsBuffer.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); raycastRequestersList = CollectionPool>.RentCollection(); int num = 1; resultsBuffer = new NativeArray(requestsBuffer.Length * num, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); for (int i = 0; i < instancesList.Count; i++) { RayAttackIndicator rayAttackIndicator = instancesList[i]; raycastRequestersList.Add(rayAttackIndicator); requestsBuffer[i] = new RaycastInfo { ray = rayAttackIndicator.attackRay, maxDistance = rayAttackIndicator.attackRange, hitsStart = i * num, maxHits = 1 }; raycastCommandBuffer[i] = new RaycastCommand(rayAttackIndicator.attackRay.origin, rayAttackIndicator.attackRay.direction, rayAttackIndicator.attackRange, rayAttackIndicator.layerMask); } raycastJobHandle = RaycastCommand.ScheduleBatch(raycastCommandBuffer, resultsBuffer, 4); running = true; } } public void CompleteUpdate() { if (!running) { return; } raycastJobHandle.Complete(); for (int i = 0; i < requestsBuffer.Length; i++) { RaycastInfo raycastInfo = requestsBuffer[i]; RayAttackIndicator rayAttackIndicator = raycastRequestersList[i]; if (!rayAttackIndicator) { continue; } List list = CollectionPool>.RentCollection(); try { Vector3 up = rayAttackIndicator.transform.up; Ray ray = raycastInfo.ray; Vector3 origin = ray.origin; Vector3 direction = ray.direction; if ((bool)rayAttackIndicator.originRecipient) { rayAttackIndicator.originRecipient.SetPositionAndRotation(origin, Quaternion.LookRotation(direction, rayAttackIndicator.originRecipient.up)); } float num = raycastInfo.maxDistance; float num2 = 0f; for (int j = 0; j < raycastInfo.maxHits; j++) { RaycastHit raycastHit = resultsBuffer[raycastInfo.hitsStart + j]; bool num3 = (object)raycastHit.collider != null; float num4 = (num3 ? raycastHit.distance : raycastInfo.maxDistance); num = ((num4 < num) ? num4 : num); num2 = ((num4 > num2) ? num4 : num2); Vector3 position = origin + direction * num4; if (num3) { list.Add(new HitPointIndicatorData { position = position, rotation = Quaternion.LookRotation(-direction, up), scale = rayAttackIndicator.attackRadius }); } } rayAttackIndicator.SetHits(list); if ((bool)rayAttackIndicator.nearestHitRecipient) { rayAttackIndicator.nearestHitRecipient.SetPositionAndRotation(origin + direction * num, Quaternion.LookRotation(-direction, rayAttackIndicator.nearestHitRecipient.up)); } if ((bool)rayAttackIndicator.furthestHitRecipient) { rayAttackIndicator.furthestHitRecipient.SetPositionAndRotation(origin + direction * num2, Quaternion.LookRotation(-direction, rayAttackIndicator.furthestHitRecipient.up)); } } catch (Exception exception) { Debug.LogException(exception); rayAttackIndicator.enabled = false; } finally { list = CollectionPool>.ReturnCollection(list); } } resultsBuffer.Dispose(); requestsBuffer.Dispose(); raycastCommandBuffer.Dispose(); raycastRequestersList = CollectionPool>.ReturnCollection(raycastRequestersList); running = false; } } [Tooltip("The prefab that will be instantiated and moved to wherever the ray hits.")] public GameObject hitIndicatorPrefab; [Tooltip("Transform which will be moved to the start of the ray.")] public Transform originRecipient; [Tooltip("Transform which will be moved to the nearest hit.")] public Transform nearestHitRecipient; [Tooltip("Transform which will be moved to the furthest hit.")] public Transform furthestHitRecipient; [Range(1f, 1f)] [Tooltip("How many hits this raycast is allowed to make. This is currently limited to 1.")] public int maxHits = 1; private static readonly Ray defaultRay = new Ray(Vector3.zero, Vector3.up); [NonSerialized] public Ray attackRay = defaultRay; [NonSerialized] public float attackRange = float.PositiveInfinity; [NonSerialized] public float attackRadius = 1f; [NonSerialized] public LayerMask layerMask; private List hitIndicators = new List(); private static bool raysDirty = false; private static List raycastRequestersList; private static NativeArray raycastCommandBuffer; private static NativeArray resultsBuffer; private static NativeArray requestsBuffer; private static JobHandle raycastJobHandle; private static Updater updater; private void Awake() { layerMask = LayerIndex.CommonMasks.bullet; } private void OnEnable() { InstanceTracker.Add(this); } private void OnDisable() { AllocateHitIndicators(0); InstanceTracker.Remove(this); } private void AllocateHitIndicators(int newHitIndicatorCount) { while (newHitIndicatorCount < hitIndicators.Count) { UnityEngine.Object.Destroy(ListUtils.TakeLast(hitIndicators).gameObject); } while (newHitIndicatorCount > hitIndicators.Count) { GameObject gameObject = UnityEngine.Object.Instantiate(hitIndicatorPrefab); gameObject.SetActive(value: true); hitIndicators.Add(new HitIndicatorInfo { gameObject = gameObject, transform = gameObject.transform }); } } private void SetHits(List hits) { if (base.enabled) { AllocateHitIndicators(hits.Count); for (int i = 0; i < hits.Count; i++) { HitIndicatorInfo hitIndicatorInfo = hitIndicators[i]; HitPointIndicatorData hitPointIndicatorData = hits[i]; hitIndicatorInfo.transform.SetPositionAndRotation(hitPointIndicatorData.position, hitPointIndicatorData.rotation); hitIndicatorInfo.transform.localScale = new Vector3(hitPointIndicatorData.scale, hitPointIndicatorData.scale, hitPointIndicatorData.scale); } } } [SystemInitializer(new Type[] { })] private static void Init() { RoR2Application.onUpdate += StaticUpdate; RoR2Application.onLateUpdate += StaticLateUpdate; RoR2Application.onFixedUpdate += StaticFixedUpdate; } private static void StaticUpdate() { if (raysDirty) { raysDirty = false; updater.ScheduleUpdate(); } } private static void StaticLateUpdate() { updater.CompleteUpdate(); } private static void StaticFixedUpdate() { raysDirty = true; } }